[GRASS-SVN] r50882 - in grass/branches/releasebranch_6_4/gui/wxpython: . core dbmgr docs gcp gmodeler gui_core icons lmgr location_wizard mapdisp modules nviz psmap tools vdigit wxplot xml

svn_grass at osgeo.org svn_grass at osgeo.org
Sun Feb 19 15:31:20 EST 2012


Author: martinl
Date: 2012-02-19 12:31:20 -0800 (Sun, 19 Feb 2012)
New Revision: 50882

Added:
   grass/branches/releasebranch_6_4/gui/wxpython/core/
   grass/branches/releasebranch_6_4/gui/wxpython/core/debug.py
   grass/branches/releasebranch_6_4/gui/wxpython/core/gcmd.py
   grass/branches/releasebranch_6_4/gui/wxpython/core/globalvar.py
   grass/branches/releasebranch_6_4/gui/wxpython/core/menudata.py
   grass/branches/releasebranch_6_4/gui/wxpython/core/render.py
   grass/branches/releasebranch_6_4/gui/wxpython/core/settings.py
   grass/branches/releasebranch_6_4/gui/wxpython/core/units.py
   grass/branches/releasebranch_6_4/gui/wxpython/core/utils.py
   grass/branches/releasebranch_6_4/gui/wxpython/core/workspace.py
   grass/branches/releasebranch_6_4/gui/wxpython/create__init__.py
   grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/
   grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/dialogs.py
   grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/manager.py
   grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/sqlbuilder.py
   grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/vinfo.py
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Components.html
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_nviz_tools_light.jpg
   grass/branches/releasebranch_6_4/gui/wxpython/gcp/
   grass/branches/releasebranch_6_4/gui/wxpython/gcp/manager.py
   grass/branches/releasebranch_6_4/gui/wxpython/gcp/mapdisplay.py
   grass/branches/releasebranch_6_4/gui/wxpython/gcp/toolbars.py
   grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/
   grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/dialogs.py
   grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/frame.py
   grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/menudata.py
   grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/model.py
   grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/preferences.py
   grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/toolbars.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_core/
   grass/branches/releasebranch_6_4/gui/wxpython/gui_core/dialogs.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_core/forms.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_core/ghelp.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_core/goutput.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_core/gselect.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_core/mapdisp.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_core/mapwindow.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_core/menu.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_core/preferences.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_core/prompt.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_core/toolbars.py
   grass/branches/releasebranch_6_4/gui/wxpython/gui_core/widgets.py
   grass/branches/releasebranch_6_4/gui/wxpython/lmgr/
   grass/branches/releasebranch_6_4/gui/wxpython/lmgr/frame.py
   grass/branches/releasebranch_6_4/gui/wxpython/lmgr/layertree.py
   grass/branches/releasebranch_6_4/gui/wxpython/lmgr/menudata.py
   grass/branches/releasebranch_6_4/gui/wxpython/lmgr/pyshell.py
   grass/branches/releasebranch_6_4/gui/wxpython/lmgr/toolbars.py
   grass/branches/releasebranch_6_4/gui/wxpython/location_wizard/
   grass/branches/releasebranch_6_4/gui/wxpython/location_wizard/base.py
   grass/branches/releasebranch_6_4/gui/wxpython/location_wizard/dialogs.py
   grass/branches/releasebranch_6_4/gui/wxpython/location_wizard/wizard.py
   grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/
   grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/frame.py
   grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/gprint.py
   grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/main.py
   grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/mapwindow.py
   grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/statusbar.py
   grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/toolbars.py
   grass/branches/releasebranch_6_4/gui/wxpython/modules/
   grass/branches/releasebranch_6_4/gui/wxpython/modules/colorrules.py
   grass/branches/releasebranch_6_4/gui/wxpython/modules/extensions.py
   grass/branches/releasebranch_6_4/gui/wxpython/modules/histogram.py
   grass/branches/releasebranch_6_4/gui/wxpython/modules/mcalc_builder.py
   grass/branches/releasebranch_6_4/gui/wxpython/modules/ogc_services.py
   grass/branches/releasebranch_6_4/gui/wxpython/modules/vclean.py
   grass/branches/releasebranch_6_4/gui/wxpython/nviz/
   grass/branches/releasebranch_6_4/gui/wxpython/nviz/animation.py
   grass/branches/releasebranch_6_4/gui/wxpython/nviz/main.py
   grass/branches/releasebranch_6_4/gui/wxpython/nviz/mapwindow.py
   grass/branches/releasebranch_6_4/gui/wxpython/nviz/preferences.py
   grass/branches/releasebranch_6_4/gui/wxpython/nviz/tools.py
   grass/branches/releasebranch_6_4/gui/wxpython/nviz/workspace.py
   grass/branches/releasebranch_6_4/gui/wxpython/nviz/wxnviz.py
   grass/branches/releasebranch_6_4/gui/wxpython/psmap/
   grass/branches/releasebranch_6_4/gui/wxpython/psmap/dialogs.py
   grass/branches/releasebranch_6_4/gui/wxpython/psmap/frame.py
   grass/branches/releasebranch_6_4/gui/wxpython/psmap/instructions.py
   grass/branches/releasebranch_6_4/gui/wxpython/psmap/menudata.py
   grass/branches/releasebranch_6_4/gui/wxpython/psmap/toolbars.py
   grass/branches/releasebranch_6_4/gui/wxpython/psmap/utils.py
   grass/branches/releasebranch_6_4/gui/wxpython/states.txt
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/dialogs.py
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/main.py
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/mapwindow.py
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/preferences.py
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/toolbars.py
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/wxdigit.py
   grass/branches/releasebranch_6_4/gui/wxpython/vdigit/wxdisplay.py
   grass/branches/releasebranch_6_4/gui/wxpython/wxplot/
   grass/branches/releasebranch_6_4/gui/wxpython/wxplot/base.py
   grass/branches/releasebranch_6_4/gui/wxpython/wxplot/dialogs.py
   grass/branches/releasebranch_6_4/gui/wxpython/wxplot/profile.py
Removed:
   grass/branches/releasebranch_6_4/gui/wxpython/gui_modules/
Modified:
   grass/branches/releasebranch_6_4/gui/wxpython/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.Modeler.html
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Nviz.html
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.PsMap.html
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Vector_Digitizer.html
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.html
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_layer_manager.jpg
   grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_nviz_toolbar.jpg
   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/docs/wxGUI_vector_digitizer_toolbar.jpg
   grass/branches/releasebranch_6_4/gui/wxpython/gis_set.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/tools/update_menudata.py
   grass/branches/releasebranch_6_4/gui/wxpython/wxgui.py
   grass/branches/releasebranch_6_4/gui/wxpython/wxpythonlib.dox
   grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata.xml
   grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata_modeler.xml
   grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata_psmap.xml
Log:
wxGUI: major update from devbr6


Modified: grass/branches/releasebranch_6_4/gui/wxpython/Makefile
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/Makefile	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/Makefile	2012-02-19 20:31:20 UTC (rev 50882)
@@ -1,8 +1,7 @@
 MODULE_TOPDIR = ../..
 
-SUBDIRS = docs scripts
+SUBDIRS = docs
 EXTRA_CLEAN_FILES = menustrings.py build_ext.pyc
-CLEAN_SUBDIRS = scripts
 
 include $(MODULE_TOPDIR)/include/Make/Dir.make
 include $(MODULE_TOPDIR)/include/Make/Doxygen.make
@@ -10,29 +9,35 @@
 
 ETCDIR = $(ETC)/wxpython
 
-SRCFILES := $(wildcard compat/* gui_modules/* icons/*.* icons/silk/* xml/*) gis_set.py wxgui.py README
+SRCFILES := $(wildcard compat/* icons/*.* scripts/* xml/*) \
+	$(wildcard core/* dbmgr/* gcp/* gmodeler/* gui_core/* lmgr/* location_wizard/* \
+	mapdisp/* modules/* nviz/* psmap/* vdigit/* wxplot/* ) \
+	gis_set.py wxgui.py README
 DSTFILES := $(patsubst %,$(ETCDIR)/%,$(SRCFILES)) $(patsubst %.py,$(ETCDIR)/%.pyc,$(filter %.py,$(SRCFILES)))
 
-default: install_scripts
+PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,core dbmgr gcp gmodeler gui_core lmgr location_wizard \
+	mapdisp modules nviz psmap vdigit wxplot)
+DSTDIRS := $(patsubst %,$(ETCDIR)/%,compat icons scripts xml)
+
+default: $(DSTFILES) menustrings.py
 	$(MAKE) parsubdirs
 
-clean: cleansubdirs
+$(ETCDIR)/%: % | $(PYDSTDIRS) $(DSTDIRS)
+	$(INSTALL_DATA) $< $@
 
-install_scripts:
-	-for dir in '' compat gui_modules icons icons/silk xml; do \
-	if [ ! -d $(ETCDIR)/$$dir ] ; then $(MKDIR) $(ETCDIR)/$$dir ; fi ; \
-	done
-	$(MAKE) $(DSTFILES)
-	$(MAKE) menustrings.py
+menustrings.py: core/menudata.py $(ETCDIR)/xml/menudata.xml $(ETCDIR)/xml/menudata_modeler.xml 
+	$(call run_grass,$(PYTHON) $< > $@)
+	$(call run_grass,$(PYTHON) $< "modeler" >> $@)
 
-$(ETCDIR)/%: %
-	$(INSTALL_DATA) $< $@
+$(PYDSTDIRS): %: | $(ETCDIR)
+	$(MKDIR) $@
+	$(call run_grass,$(PYTHON) create__init__.py $@)
 
-menustrings.py: gui_modules/menudata.py $(ETCDIR)/xml/menudata.xml $(ETCDIR)/xml/menudata_modeler.xml 
-	GISBASE="$(GISBASE)" \
-	$(PYTHON) $< > $@
-	GISBASE="$(GISBASE)" \
-	$(PYTHON) $< "modeler" >> $@
+$(DSTDIRS): %: | $(ETCDIR)
+	$(MKDIR) $@
 
+$(ETCDIR):
+	$(MKDIR) $@
+
 #doxygen:
 DOXNAME=wxpython

Added: grass/branches/releasebranch_6_4/gui/wxpython/core/debug.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/core/debug.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/core/debug.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,67 @@
+"""!
+ at package core.debug
+
+ at brief wxGUI debugging
+
+Classes:
+ - debug::DebugMsg
+
+ at code
+from core.debug import Debug
+Debug.msg (3, 'debug message')
+ at endcode
+         
+(C) 2007-2009, 2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+
+import grass.script as grass
+
+class DebugMsg:
+    """!wxGUI debugging
+
+    @code
+    g.gisenv set=WX_DEBUG=[0-5]
+    @endcode
+    """
+    def __init__(self):
+        # default level
+        self.debuglevel = 0
+        
+        self.SetLevel()
+
+    def SetLevel(self):
+        """!Initialize gui debug level
+        """
+        self.debuglevel = int(grass.gisenv().get('WX_DEBUG', 0))
+        
+    def msg(self, level, message, *args):
+        """!Print debug message
+
+        @param level debug level (0-5)
+        @param message message to be printed
+        @param *args formatting params
+        """
+        # self.SetLevel()
+        if self.debuglevel > 0 and level > 0 and level <= self.debuglevel:
+            if args:
+                sys.stderr.write("GUI D%d/%d: " % (level, self.debuglevel) + \
+                    message % args + os.linesep)
+            else:
+                sys.stderr.write("GUI D%d/%d: " % (level, self.debuglevel) + \
+                                     message + os.linesep)
+            sys.stderr.flush() # force flush (required for MS Windows)
+        
+    def GetLevel(self):
+        """!Return current GUI debug level"""
+        return self.debuglevel
+
+# Debug instance
+Debug = DebugMsg()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/core/gcmd.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/core/gcmd.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/core/gcmd.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,683 @@
+"""!
+ at package core.gcmd
+
+ at brief wxGUI command interface
+
+Classes:
+ - gcmd::GError
+ - gcmd::GWarning
+ - gcmd::GMessage
+ - gcmd::GException
+ - gcmd::Popen (from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554)
+ - gcmd::Command
+ - gcmd::CommandThread
+
+Functions:
+ - RunCommand
+
+(C) 2007-2008, 2010-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+import time
+import errno
+import signal
+import locale
+import traceback
+
+import wx
+
+try:
+    import subprocess
+except:
+    compatPath = os.path.join(globalvar.ETCWXDIR, "compat")
+    sys.path.append(compatPath)
+    import subprocess
+if subprocess.mswindows:
+    from win32file import ReadFile, WriteFile
+    from win32pipe import PeekNamedPipe
+    import msvcrt
+else:
+    import select
+    import fcntl
+from threading import Thread
+
+from grass.script import core as grass
+
+from core       import globalvar
+from core.debug import Debug
+
+def GetRealCmd(cmd):
+    """!Return real command name - only for MS Windows
+    """
+    if sys.platform == 'win32':
+        for ext in globalvar.grassScripts.keys():
+            if cmd in globalvar.grassScripts[ext]:
+                return cmd + ext
+    
+    return cmd
+
+def DecodeString(string):
+    """!Decode string using system encoding
+    
+    @param string string to be decoded
+    
+    @return decoded string
+    """
+    if not string:
+        return string
+    
+    enc = locale.getdefaultlocale()[1]
+    if enc:
+        Debug.msg(5, "DecodeString(): enc=%s" % enc)
+        return string.decode(enc)
+    
+    return string
+
+def EncodeString(string):
+    """!Return encoded string using system locales
+    
+    @param string string to be encoded
+    
+    @return encoded string
+    """
+    if not string:
+        return string
+    enc = locale.getdefaultlocale()[1]
+    if enc:
+        Debug.msg(5, "EncodeString(): enc=%s" % enc)
+        return string.encode(enc)
+    
+    return string
+
+class GError:
+    def __init__(self, message, parent = None, caption = None, showTraceback = True):
+        if not caption:
+            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()
+        
+        if Debug.GetLevel() > 0 and exc_traceback:
+            sys.stderr.write(exception)
+        
+        if showTraceback and exc_traceback:
+            wx.MessageBox(parent = parent,
+                          message = message + '\n\n%s: %s\n\n%s' % \
+                              (_('Reason'),
+                               reason, exception),
+                          caption = caption,
+                          style = style)
+        else:
+            wx.MessageBox(parent = parent,
+                          message = message,
+                          caption = caption,
+                          style = style)
+
+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)
+        
+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)
+
+class GException(Exception):
+    def __init__(self, value = ''):
+        self.value = value
+
+    def __str__(self):
+        return self.value
+
+class Popen(subprocess.Popen):
+    """!Subclass subprocess.Popen"""
+    def __init__(self, args, **kwargs):
+        if subprocess.mswindows:
+            args = map(EncodeString, args)
+        
+        subprocess.Popen.__init__(self, args, **kwargs)
+        
+    def recv(self, maxsize = None):
+        return self._recv('stdout', maxsize)
+    
+    def recv_err(self, maxsize = None):
+        return self._recv('stderr', maxsize)
+
+    def send_recv(self, input = '', maxsize = None):
+        return self.send(input), self.recv(maxsize), self.recv_err(maxsize)
+
+    def get_conn_maxsize(self, which, maxsize):
+        if maxsize is None:
+            maxsize = 1024
+        elif maxsize < 1:
+            maxsize = 1
+        return getattr(self, which), maxsize
+    
+    def _close(self, which):
+        getattr(self, which).close()
+        setattr(self, which, None)
+
+    def kill(self):
+        """!Try to kill running process"""
+        if subprocess.mswindows:
+            import win32api
+            handle = win32api.OpenProcess(1, 0, self.pid)
+            return (0 != win32api.TerminateProcess(handle, 0))
+	else:
+            try:
+                os.kill(-self.pid, signal.SIGTERM) # kill whole group
+            except OSError:
+                pass
+
+    if subprocess.mswindows:
+        def send(self, input):
+            if not self.stdin:
+                return None
+
+            try:
+                x = msvcrt.get_osfhandle(self.stdin.fileno())
+                (errCode, written) = WriteFile(x, input)
+            except ValueError:
+                return self._close('stdin')
+            except (subprocess.pywintypes.error, Exception), why:
+                if why[0] in (109, errno.ESHUTDOWN):
+                    return self._close('stdin')
+                raise
+
+            return written
+
+        def _recv(self, which, maxsize):
+            conn, maxsize = self.get_conn_maxsize(which, maxsize)
+            if conn is None:
+                return None
+            
+            try:
+                x = msvcrt.get_osfhandle(conn.fileno())
+                (read, nAvail, nMessage) = PeekNamedPipe(x, 0)
+                if maxsize < nAvail:
+                    nAvail = maxsize
+                if nAvail > 0:
+                    (errCode, read) = ReadFile(x, nAvail, None)
+            except ValueError:
+                return self._close(which)
+            except (subprocess.pywintypes.error, Exception), why:
+                if why[0] in (109, errno.ESHUTDOWN):
+                    return self._close(which)
+                raise
+            
+            if self.universal_newlines:
+                read = self._translate_newlines(read)
+            return read
+
+    else:
+        def send(self, input):
+            if not self.stdin:
+                return None
+
+            if not select.select([], [self.stdin], [], 0)[1]:
+                return 0
+
+            try:
+                written = os.write(self.stdin.fileno(), input)
+            except OSError, why:
+                if why[0] == errno.EPIPE: #broken pipe
+                    return self._close('stdin')
+                raise
+
+            return written
+
+        def _recv(self, which, maxsize):
+            conn, maxsize = self.get_conn_maxsize(which, maxsize)
+            if conn is None:
+                return None
+            
+            flags = fcntl.fcntl(conn, fcntl.F_GETFL)
+            if not conn.closed:
+                fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK)
+            
+            try:
+                if not select.select([conn], [], [], 0)[0]:
+                    return ''
+                
+                r = conn.read(maxsize)
+                
+                if not r:
+                    return self._close(which)
+    
+                if self.universal_newlines:
+                    r = self._translate_newlines(r)
+                return r
+            finally:
+                if not conn.closed:
+                    fcntl.fcntl(conn, fcntl.F_SETFL, flags)
+
+message = "Other end disconnected!"
+
+def recv_some(p, t = .1, e = 1, tr = 5, stderr = 0):
+    if tr < 1:
+        tr = 1
+    x = time.time()+t
+    y = []
+    r = ''
+    pr = p.recv
+    if stderr:
+        pr = p.recv_err
+    while time.time() < x or r:
+        r = pr()
+        if r is None:
+            if e:
+                raise Exception(message)
+            else:
+                break
+        elif r:
+            y.append(r)
+        else:
+            time.sleep(max((x-time.time())/tr, 0))
+    return ''.join(y)
+    
+def send_all(p, data):
+    while len(data):
+        sent = p.send(data)
+        if sent is None:
+            raise Exception(message)
+        data = buffer(data, sent)
+
+class Command:
+    """!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.
+
+    @code
+        cmd = Command(cmd=['d.rast', 'elevation.dem'], verbose=3, wait=True)
+
+        if cmd.returncode == None:
+            print 'RUNNING?'
+        elif cmd.returncode == 0:
+            print 'SUCCESS'
+        else:
+            print 'FAILURE (%d)' % cmd.returncode
+    @endcode
+
+    @param cmd     command given as list
+    @param stdin   standard input stream
+    @param verbose verbose level [0, 3] (--q, --v)
+    @param wait    wait for child execution terminated
+    @param rerr    error handling (when CmdError raised).
+    True for redirection to stderr, False for GUI dialog,
+    None for no operation (quiet mode)
+    @param stdout  redirect standard output or None
+    @param stderr  redirect standard error output or None
+    """
+    def __init__ (self, cmd, stdin = None,
+                  verbose = None, wait = True, rerr = False,
+                  stdout = None, stderr = None):
+        Debug.msg(1, "gcmd.Command(): %s" % ' '.join(cmd))
+        self.cmd = cmd
+        self.stderr = stderr
+	
+        #
+        # set verbosity level
+        #
+        verbose_orig = None
+        if ('--q' not in self.cmd and '--quiet' not in self.cmd) and \
+                ('--v' not in self.cmd and '--verbose' not in self.cmd):
+            if verbose is not None:
+                if verbose == 0:
+                    self.cmd.append('--quiet')
+                elif verbose == 3:
+                    self.cmd.append('--verbose')
+                else:
+                    verbose_orig = os.getenv("GRASS_VERBOSE")
+                    os.environ["GRASS_VERBOSE"] = str(verbose)
+
+        #
+        # create command thread
+        #
+        self.cmdThread = CommandThread(cmd, stdin,
+                                       stdout, stderr)
+        self.cmdThread.start()
+        
+        if wait:
+            self.cmdThread.join()
+            if self.cmdThread.module:
+                self.cmdThread.module.wait()
+                self.returncode = self.cmdThread.module.returncode
+            else:
+                self.returncode = 1
+        else:
+            self.cmdThread.join(0.5)
+            self.returncode = None
+
+        if self.returncode is not None:
+            Debug.msg (3, "Command(): cmd='%s', wait=%s, returncode=%d, alive=%s" % \
+                           (' '.join(cmd), wait, self.returncode, self.cmdThread.isAlive()))
+            if rerr is not None and self.returncode != 0:
+                if rerr is False: # GUI dialog
+                    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(),
+                                                     os.linesep))
+            else:
+                pass # nop
+        else:
+            Debug.msg (3, "Command(): cmd='%s', wait=%s, returncode=?, alive=%s" % \
+                           (' '.join(cmd), wait, self.cmdThread.isAlive()))
+
+        if verbose_orig:
+            os.environ["GRASS_VERBOSE"] = verbose_orig
+        elif "GRASS_VERBOSE" in os.environ:
+            del os.environ["GRASS_VERBOSE"]
+            
+    def __ReadOutput(self, stream):
+        """!Read stream and return list of lines
+
+        @param stream stream to be read
+        """
+        lineList = []
+
+        if stream is None:
+            return lineList
+
+        while True:
+            line = stream.readline()
+            if not line:
+                break
+            line = line.replace('%s' % os.linesep, '').strip()
+            lineList.append(line)
+
+        return lineList
+                    
+    def __ReadErrOutput(self):
+        """!Read standard error output and return list of lines"""
+        return self.__ReadOutput(self.cmdThread.module.stderr)
+
+    def __ProcessStdErr(self):
+        """
+        Read messages/warnings/errors from stderr
+
+        @return list of (type, message)
+        """
+        if self.stderr is None:
+            lines = self.__ReadErrOutput()
+        else:
+            lines = self.cmdThread.error.strip('%s' % os.linesep). \
+                split('%s' % os.linesep)
+        
+        msg = []
+
+        type    = None
+        content = ""
+        for line in lines:
+            if len(line) == 0:
+                continue
+            if 'GRASS_' in line: # error or warning
+                if 'GRASS_INFO_WARNING' in line: # warning
+                    type = "WARNING"
+                elif 'GRASS_INFO_ERROR' in line: # error
+                    type = "ERROR"
+                elif 'GRASS_INFO_END': # end of message
+                    msg.append((type, content))
+                    type = None
+                    content = ""
+                
+                if type:
+                    content += line.split(':', 1)[1].strip()
+            else: # stderr
+                msg.append((None, line.strip()))
+
+        return msg
+
+    def __GetError(self):
+        """!Get error message or ''"""
+        if not self.cmdThread.module:
+            return _("Unable to exectute command: '%s'") % ' '.join(self.cmd)
+
+        for type, msg in self.__ProcessStdErr():
+            if type == 'ERROR':
+                enc = locale.getdefaultlocale()[1]
+                if enc:
+                    return unicode(msg, enc)
+                else:
+                    return msg
+        
+        return ''
+    
+class CommandThread(Thread):
+    """!Create separate thread for command. Used for commands launched
+    on the background."""
+    def __init__ (self, cmd, stdin = None,
+                  stdout = sys.stdout, stderr = sys.stderr):
+        """
+        @param cmd command (given as list)
+        @param stdin standard input stream 
+        @param stdout redirect standard output or None
+        @param stderr redirect standard error output or None
+        """
+        Thread.__init__(self)
+        
+        self.cmd    = cmd
+        self.stdin  = stdin
+        self.stdout = stdout
+        self.stderr = stderr
+        
+        self.module = None
+        self.error  = ''
+        
+        self._want_abort = False
+        self.aborted = False
+        
+        self.setDaemon(True)
+        
+        # set message formatting
+        self.message_format = os.getenv("GRASS_MESSAGE_FORMAT")
+        os.environ["GRASS_MESSAGE_FORMAT"] = "gui"
+        
+    def __del__(self):
+        if self.message_format:
+            os.environ["GRASS_MESSAGE_FORMAT"] = self.message_format
+        else:
+            del os.environ["GRASS_MESSAGE_FORMAT"]
+        
+    def run(self):
+        """!Run command"""
+        if len(self.cmd) == 0:
+            return
+
+        Debug.msg(1, "gcmd.CommandThread(): %s" % ' '.join(self.cmd))
+
+        self.startTime = time.time()
+
+        # TODO: replace ugly hack bellow
+        args = self.cmd
+        if sys.platform == 'win32' and os.path.splitext(self.cmd[0])[1] == '.py':
+            os.chdir(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'scripts'))
+            args = [sys.executable, self.cmd[0]] + self.cmd[1:]
+        
+        try:
+            self.module = Popen(args,
+                                stdin = subprocess.PIPE,
+                                stdout = subprocess.PIPE,
+                                stderr = subprocess.PIPE,
+                                shell = sys.platform == "win32")
+        except OSError, e:
+            self.error = str(e)
+            print >> sys.stderr, e
+            return 1
+        
+        if self.stdin: # read stdin if requested ...
+            self.module.stdin.write(self.stdin)
+            self.module.stdin.close()
+            
+        # redirect standard outputs...
+        self._redirect_stream()
+        
+    def _redirect_stream(self):
+        """!Redirect stream"""
+        if self.stdout:
+            # make module stdout/stderr non-blocking
+            out_fileno = self.module.stdout.fileno()
+	    if not subprocess.mswindows:
+                flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL)
+                fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags| os.O_NONBLOCK)
+                
+        if self.stderr:
+            # make module stdout/stderr non-blocking
+            out_fileno = self.module.stderr.fileno()
+	    if not subprocess.mswindows:
+                flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL)
+                fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags| os.O_NONBLOCK)
+        
+        # wait for the process to end, sucking in stuff until it does end
+        while self.module.poll() is None:
+            if self._want_abort: # abort running process
+                self.module.kill()
+                self.aborted = True
+                return 
+            if self.stdout:
+                line = recv_some(self.module, e = 0, stderr = 0)
+                self.stdout.write(line)
+            if self.stderr:
+                line = recv_some(self.module, e = 0, stderr = 1)
+                self.stderr.write(line)
+                if len(line) > 0:
+                    self.error = line
+
+        # get the last output
+        if self.stdout:
+            line = recv_some(self.module, e = 0, stderr = 0)
+            self.stdout.write(line)
+        if self.stderr:
+            line = recv_some(self.module, e = 0, stderr = 1)
+            self.stderr.write(line)
+            if len(line) > 0:
+                self.error = line
+            
+    def abort(self):
+        """!Abort running process, used by main thread to signal an abort"""
+        self._want_abort = True
+    
+def _formatMsg(text):
+    """!Format error messages for dialogs
+    """
+    message = ''
+    for line in text.splitlines():
+        if len(line) == 0:
+            continue
+        elif 'GRASS_INFO_MESSAGE' in line:
+            message += line.split(':', 1)[1].strip() + '\n'
+        elif 'GRASS_INFO_WARNING' in line:
+            message += line.split(':', 1)[1].strip() + '\n'
+        elif 'GRASS_INFO_ERROR' in line:
+            message += line.split(':', 1)[1].strip() + '\n'
+        elif 'GRASS_INFO_END' in line:
+            return message
+        else:
+            message += line.strip() + '\n'
+    
+    return message
+
+def RunCommand(prog, flags = "", overwrite = False, quiet = False, verbose = False,
+               parent = None, read = False, stdin = None, getErrorMsg = False, **kwargs):
+    """!Run GRASS command
+
+    @param prog program to run
+    @param flags flags given as a string
+    @param overwrite, quiet, verbose flags
+    @param parent parent window for error messages
+    @param read fetch stdout
+    @param stdin stdin or None
+    @param getErrorMsg get error messages on failure
+    @param kwargs program parameters
+    
+    @return returncode (read == False and getErrorMsg == False)
+    @return returncode, messages (read == False and getErrorMsg == True)
+    @return stdout (read == True and getErrorMsg == False)
+    @return returncode, stdout, messages (read == True and getErrorMsg == True)
+    @return stdout, stderr
+    """
+    cmdString = ' '.join(grass.make_command(prog, flags, overwrite,
+                                            quiet, verbose, **kwargs))
+    
+    Debug.msg(1, "gcmd.RunCommand(): %s" % cmdString)
+    
+    kwargs['stderr'] = subprocess.PIPE
+    
+    if read:
+        kwargs['stdout'] = subprocess.PIPE
+    
+    if stdin:
+        kwargs['stdin'] = subprocess.PIPE
+    
+    ps = grass.start_command(GetRealCmd(prog), flags, overwrite, quiet, verbose, **kwargs)
+    
+    Debug.msg(2, "gcmd.RunCommand(): command started")
+
+    if stdin:
+        ps.stdin.write(stdin)
+        ps.stdin.close()
+        ps.stdin = None
+    
+    Debug.msg(3, "gcmd.RunCommand(): decoding string")
+    stdout, stderr = map(DecodeString, ps.communicate())
+    
+    ret = ps.returncode
+    Debug.msg(1, "gcmd.RunCommand(): get return code %d" % ret)
+        
+    Debug.msg(3, "gcmd.RunCommand(): print error")
+    if ret != 0 and parent:
+        Debug.msg(2, "gcmd.RunCommand(): error %s" % stderr)
+        if (stderr == None):
+            Debug.msg(2, "gcmd.RunCommand(): nothing to print ???")
+        else:
+            GError(parent = parent,
+                   message = stderr)
+        
+    Debug.msg(3, "gcmd.RunCommand(): print read error")
+    if not read:
+        if not getErrorMsg:
+            return ret
+        else:
+            return ret, _formatMsg(stderr)
+
+    if stdout:
+        Debug.msg(2, "gcmd.RunCommand(): return stdout\n'%s'" % stdout)
+    else:
+        Debug.msg(2, "gcmd.RunCommand(): return stdout = None")
+    if not getErrorMsg:
+        return stdout
+    
+    Debug.msg(2, "gcmd.RunCommand(): return ret, stdout")
+    if read and getErrorMsg:
+        return ret, stdout, _formatMsg(stderr)
+    
+    Debug.msg(2, "gcmd.RunCommand(): return result")
+    return stdout, _formatMsg(stderr)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/core/globalvar.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/core/globalvar.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/core/globalvar.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,173 @@
+"""!
+ at package core.globalvar
+
+ at brief Global variables used by wxGUI
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+import locale
+
+if not os.getenv("GISBASE"):
+    sys.exit("GRASS is not running. Exiting...")
+
+# 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")
+ETCIMGDIR = os.path.join(ETCDIR, "gui", "images")
+ETCSYMBOLDIR = os.path.join(ETCDIR, "gui", "images", "symbols")
+
+sys.path.append(os.path.join(ETCDIR, "python"))
+import grass.script as grass
+
+def CheckWxVersion(version = [2, 8, 11, 0]):
+    """!Check wx version"""
+    ver = wx.version().split(' ')[0]
+    if map(int, ver.split('.')) < version:
+        return False
+    
+    return True
+
+def CheckForWx():
+    """!Try to import wx module and check its version"""
+    if 'wx' in sys.modules.keys():
+        return
+    
+    minVersion = [2, 8, 1, 1]
+    try:
+        try:
+            import wxversion
+        except ImportError, e:
+            raise ImportError(e)
+        # wxversion.select(str(minVersion[0]) + '.' + str(minVersion[1]))
+        wxversion.ensureMinimal(str(minVersion[0]) + '.' + str(minVersion[1]))
+        import wx
+        version = wx.version().split(' ')[0]
+        
+        if map(int, version.split('.')) < minVersion:
+            raise ValueError('Your wxPython version is %s.%s.%s.%s' % tuple(version.split('.')))
+
+    except ImportError, e:
+        print >> sys.stderr, 'ERROR: wxGUI requires wxPython. %s' % str(e)
+        sys.exit(1)
+    except (ValueError, wxversion.VersionError), e:
+        print >> sys.stderr, 'ERROR: wxGUI requires wxPython >= %d.%d.%d.%d. ' % tuple(minVersion) + \
+            '%s.' % (str(e))
+        sys.exit(1)
+    except locale.Error, e:
+        print >> sys.stderr, "Unable to set locale:", e
+        os.environ['LC_ALL'] = ''
+    
+if not os.getenv("GRASS_WXBUNDLED"):
+    CheckForWx()
+import wx
+import wx.lib.flatnotebook as FN
+
+"""
+Query layer (generated for example by selecting item in the Attribute Table Manager)
+Deleted automatically on re-render action
+"""
+# temporal query layer (removed on re-render action)
+QUERYLAYER = 'qlayer'
+
+"""!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_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 = (800, 600)
+GM_WINDOW_SIZE = (500, 600)
+
+def GetGRASSCommands():
+    """!Create list of available GRASS commands to use when parsing
+    string from the command line
+
+    @return list of commands (set) and directory of scripts (collected
+    by extension - MS Windows only)
+    """
+    gisbase = os.environ['GISBASE']
+    cmd = list()
+    if sys.platform == 'win32':
+        scripts = { '.bat' : list(),
+                    '.py'  : list()
+                    }
+    else:
+        scripts = {}
+    
+    # scan bin/
+    if os.path.exists(os.path.join(gisbase, 'bin')):
+        for fname in os.listdir(os.path.join(gisbase, 'bin')):
+            if scripts: # win32
+                name, ext = os.path.splitext(fname)
+                if ext != '.manifest':
+                    cmd.append(name)
+                if ext in scripts.keys():
+                    scripts[ext].append(name)
+            else:
+                cmd.append(fname)
+    
+    # scan scripts/ (not on MS Windows)
+    if not scripts and os.path.exists(os.path.join(gisbase, 'scripts')):
+        for fname in os.listdir(os.path.join(gisbase, 'scripts')):
+            cmd.append(fname)
+    
+    # scan gui/scripts/
+    if os.path.exists(os.path.join(gisbase, 'etc', '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'))
+    
+    # scan addons (path)
+    if os.getenv('GRASS_ADDON_PATH'):
+        for path in os.getenv('GRASS_ADDON_PATH').split(os.pathsep):
+            if not os.path.exists(path) or not os.path.isdir(path):
+                continue
+            for fname in os.listdir(path):
+                if scripts: # win32
+                    name, ext = os.path.splitext(fname)
+                    cmd.append(name)
+                    if ext in scripts.keys():
+                        scripts[ext].append(name)
+                else:
+                    cmd.append(fname)
+    
+    return set(cmd), scripts
+
+"""@brief Collected GRASS-relared binaries/scripts"""
+grassCmd, grassScripts = GetGRASSCommands()
+
+"""@Toolbar icon size"""
+toolbarSize = (24, 24)
+
+"""@Is g.mlist available?"""
+if 'g.mlist' in grassCmd:
+    have_mlist = True
+else:
+    have_mlist = False
+
+"""@Check version of wxPython, use agwStyle for 2.8.11+"""
+hasAgw = CheckWxVersion()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/core/menudata.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/core/menudata.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/core/menudata.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,233 @@
+"""!
+ at package core.menudata
+
+ at brief Complex list for menu entries for wxGUI
+
+Classes:
+ - menudata::MenuData
+
+Usage:
+ at code
+python menudata.py [action] [manager|modeler]
+ at endcode
+
+where <i>action</i>:
+ - strings (default)
+ - tree
+ - commands
+ - dump
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton (Arizona State University)
+ at author Yann Chemin <yann.chemin gmail.com>
+ at author Martin Landa <landa.martin gmail.com>
+ at author Glynn Clements
+"""
+
+import os
+import sys
+import pprint
+try:
+    import xml.etree.ElementTree   as etree
+except ImportError:
+    import elementtree.ElementTree as etree # Python <= 2.4
+
+import wx
+
+if not os.getenv("GISBASE"):
+    sys.exit("GRASS is not running. Exiting...")
+
+class MenuData:
+    """!Abstract menu data class"""
+    def __init__(self, filename):
+	self.tree = etree.parse(filename)
+
+    def _getMenuItem(self, mi):
+        """!Get menu item
+
+        @param mi menu item instance
+        """
+	if mi.tag == 'separator':
+	    return ('', '', '', '', '')
+	elif mi.tag == 'menuitem':
+	    label    = _(mi.find('label').text)
+	    help     = _(mi.find('help').text)
+	    handler  = mi.find('handler').text
+	    gcmd     = mi.find('command')  # optional
+            keywords = mi.find('keywords') # optional
+            shortcut = mi.find('shortcut') # optional
+            wxId     = mi.find('id')       # optional
+	    if gcmd != None:
+		gcmd = gcmd.text
+	    else:
+		gcmd = ""
+            if keywords != None:
+                keywords = keywords.text
+            else:
+                keywords = ""
+            if shortcut != None:
+                shortcut = shortcut.text
+            else:
+                shortcut = ""
+            if wxId != None:
+                wxId = eval('wx.' + wxId.text)
+            else:
+                wxId = wx.ID_ANY
+	    return (label, help, handler, gcmd, keywords, shortcut, wxId)
+	elif mi.tag == 'menu':
+	    return self._getMenu(mi)
+	else:
+	    raise Exception(_("Unknow tag"))
+
+    def _getMenu(self, m):
+        """!Get menu
+
+        @param m menu
+
+        @return label, menu items
+        """
+	label = _(m.find('label').text)
+	items = m.find('items')
+	return (label, tuple(map(self._getMenuItem, items)))
+    
+    def _getMenuBar(self, mb):
+        """!Get menu bar
+
+        @param mb menu bar instance
+        
+        @return menu items
+        """
+	return tuple(map(self._getMenu, mb.findall('menu')))
+
+    def _getMenuData(self, md):
+        """!Get menu data
+
+        @param md menu data instace
+        
+        @return menu data
+        """
+	return list(map(self._getMenuBar, md.findall('menubar')))
+
+    def GetMenu(self):
+        """!Get menu
+
+        @return menu data
+        """
+	return self._getMenuData(self.tree.getroot())
+
+    def PrintStrings(self, fh):
+        """!Print menu strings to file (used for localization)
+
+        @param fh file descriptor"""
+        className = str(self.__class__).split('.', 1)[1]
+	fh.write('menustrings_%s = [\n' % className)
+	for node in self.tree.getiterator():
+	    if node.tag in ['label', 'help']:
+		fh.write('     _(%r),\n' % node.text)
+	fh.write('    \'\']\n')
+
+    def PrintTree(self, fh):
+        """!Print menu tree to file
+
+        @param fh file descriptor"""
+        level = 0
+        for eachMenuData in self.GetMenu():
+            for label, items in eachMenuData:
+                fh.write('- %s\n' % label.replace('&', ''))
+                self._PrintTreeItems(fh, level + 1, items)
+        
+    def _PrintTreeItems(self, fh, level, menuData):
+        """!Print menu tree items to file (used by PrintTree)
+
+        @param fh file descriptor
+        @param level menu level
+        @param menuData menu data to print out"""
+        for eachItem in menuData:
+            if len(eachItem) == 2:
+                if eachItem[0]:
+                    fh.write('%s - %s\n' % (' ' * level, eachItem[0]))
+                self._PrintTreeItems(fh, level + 1, eachItem[1])
+            else:
+                if eachItem[0]:
+                    fh.write('%s - %s\n' % (' ' * level, eachItem[0]))
+    
+    def PrintCommands(self, fh, itemSep = ' | ', menuSep = ' > '):
+        """!Print commands list (command | menu item > menu item)
+
+        @param fh file descriptor
+        """
+        level = 0
+        for eachMenuData in self.GetMenu():
+            for label, items in eachMenuData:
+                menuItems = [label, ]
+                self._PrintCommandsItems(fh, level + 1, items,
+                                         menuItems, itemSep, menuSep)
+        
+    def _PrintCommandsItems(self, fh, level, menuData,
+                             menuItems, itemSep, menuSep):
+        """!Print commands item (used by PrintCommands)
+
+        @param fh file descriptor
+        @param menuItems list of menu items
+        """
+        for eachItem in menuData:
+            if len(eachItem) == 2:
+                if eachItem[0]:
+                    try:
+                        menuItems[level] = eachItem[0]
+                    except IndexError:
+                        menuItems.append(eachItem[0])
+                self._PrintCommandsItems(fh, level + 1, eachItem[1],
+                                          menuItems, itemSep, menuSep)
+            else:
+                try:
+                    del menuItems[level]
+                except IndexError:
+                    pass
+                
+                if eachItem[3]:
+                    fh.write('%s%s' % (eachItem[3], itemSep))
+                    fh.write(menuSep.join(map(lambda x: x.replace('&', ''), menuItems)))
+                    fh.write('%s%s' % (menuSep, eachItem[0]))
+                    fh.write('\n')
+
+if __name__ == "__main__":
+    import os
+    import sys
+    
+    # i18N
+    import gettext
+    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
+
+    action = 'strings'
+    menu   = 'manager'
+    
+    for arg in sys.argv:
+        if arg in ('strings', 'tree', 'commands', 'dump'):
+            action =  arg
+        elif arg in ('manager', 'modeler'):
+            menu = arg
+    
+    sys.path.append(os.path.join(os.getenv("GISBASE"), "etc", "wxpython"))
+    
+    if menu == 'manager':
+        from lmgr.menudata     import ManagerData
+        data = ManagerData()
+    else:
+        from gmodeler.menudata import ModelerData
+        data = ModelerData()
+    
+    if action == 'strings':
+        data.PrintStrings(sys.stdout)
+    elif action == 'tree':
+        data.PrintTree(sys.stdout)
+    elif action == 'commands':
+        data.PrintCommands(sys.stdout)
+    elif action == 'dump':
+	pprint.pprint(data.GetMenu(), stream = sys.stdout, indent = 2)
+    
+    sys.exit(0)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/core/render.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/core/render.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/core/render.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1316 @@
+"""!
+ at package core.render
+
+ at brief Rendering map layers and overlays into map composition image.
+
+Classes:
+ - render::Layer
+ - render::MapLayer
+ - render::Overlay
+ - render::Map
+
+(C) 2006-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+import glob
+import math
+import copy
+import tempfile
+import types
+
+import wx
+from wx.lib.newevent import NewEvent
+
+from grass.script import core as grass
+
+from core          import utils
+from core.gcmd     import GException, GError, RunCommand
+from core.debug    import Debug
+from core.settings import UserSettings
+
+wxUpdateProgressBar, EVT_UPDATE_PRGBAR = NewEvent()
+
+#
+# use g.pnmcomp for creating image composition or
+# wxPython functionality
+#
+USE_GPNMCOMP = True
+
+class Layer(object):
+    """!Virtual class which stores information about layers (map layers and
+    overlays) of the map composition.
+    
+    For map layer use MapLayer class.
+    For overlays use Overlay class.
+    """
+    def __init__(self, type, cmd, name = None,
+                 active = True, hidden = False, opacity = 1.0):
+        """!
+        @todo pass cmd as tuple instead of list
+        
+        @param type layer type ('raster', 'vector', 'overlay', 'command', etc.)
+        @param cmd GRASS command to render layer,
+        given as list, e.g. ['d.rast', 'map=elevation at PERMANENT']
+        @param name layer name, e.g. 'elevation at PERMANENT' (for layer tree)
+        @param active layer is active, will be rendered only if True
+        @param hidden layer is hidden, won't be listed in Layer Manager if True
+        @param opacity layer opacity <0;1>
+        """
+        self.type  = type
+        self.name  = name
+        
+        if self.type == 'command':
+            self.cmd = list()
+            for c in cmd:
+                self.cmd.append(utils.CmdToTuple(c))
+        else:
+            self.cmd = utils.CmdToTuple(cmd)
+        
+        self.active  = active
+        self.hidden  = hidden
+        self.opacity = opacity
+        
+        self.force_render = True
+        
+        Debug.msg (3, "Layer.__init__(): type=%s, cmd='%s', name=%s, " \
+                       "active=%d, opacity=%d, hidden=%d" % \
+                       (self.type, self.GetCmd(string = True), self.name, self.active,
+                        self.opacity, self.hidden))
+        
+        # generated file for each layer
+        self.gtemp = tempfile.mkstemp()[1]
+        self.maskfile = self.gtemp + ".pgm"
+        if self.type == 'overlay':
+            self.mapfile  = self.gtemp + ".png"
+        else:
+            self.mapfile  = self.gtemp + ".ppm"
+        
+    def __del__(self):
+        Debug.msg (3, "Layer.__del__(): layer=%s, cmd='%s'" %
+                   (self.name, self.GetCmd(string = True)))
+
+    def Render(self):
+        """!Render layer to image
+        
+        @return rendered image filename
+        @return None on error
+        """
+        if not self.cmd:
+            return None
+        
+        # ignore in 2D
+        if self.type == '3d-raster':
+            return None
+        
+        Debug.msg (3, "Layer.Render(): type=%s, name=%s" % \
+                       (self.type, self.name))
+        
+        # prepare command for each layer
+        layertypes = ('raster', 'rgb', 'his', 'shaded', 'rastarrow', 'rastnum',
+                      'vector','thememap','themechart',
+                      'grid', 'geodesic', 'rhumb', 'labels',
+                      'command', 'rastleg',
+                      'overlay')
+        
+        if self.type not in layertypes:
+            raise GException(_("<%(name)s>: layer type <%(type)s> is not supported") % \
+                                 {'type' : self.type, 'name' : self.name})
+        
+        # start monitor
+        if UserSettings.Get(group='display', key='driver', subkey='type') == 'cairo':
+#            os.environ["GRASS_CAIROFILE"] = self.mapfile
+#            if 'cairo' not in gcmd.RunCommand('d.mon',
+#                                              flags='p',
+#                                              read = True):
+#                gcmd.RunCommand('d.mon',
+#                                start = 'cairo')
+            if not self.mapfile:
+                self.gtemp = tempfile.mkstemp()[1]
+                self.maskfile = self.gtemp + ".pgm"
+                if self.type == 'overlay':
+                    self.mapfile  = self.gtemp + ".png"
+                else:
+                    self.mapfile  = self.gtemp + ".ppm"
+
+            if self.mapfile:
+                os.environ["GRASS_CAIROFILE"] = self.mapfile
+        else:
+            if not self.mapfile:
+                self.gtemp = tempfile.mkstemp()[1]
+                self.maskfile = self.gtemp + ".pgm"
+                if self.type == 'overlay':
+                    self.mapfile  = self.gtemp + ".png"
+                else:
+                    self.mapfile  = self.gtemp + ".ppm"
+
+            if self.mapfile:
+                os.environ["GRASS_PNGFILE"] = self.mapfile
+        
+        # execute command
+        try:
+            if self.type == 'command':
+                read = False
+                for c in self.cmd:
+                    ret, msg = RunCommand(c[0],
+                                          getErrorMsg = True,
+                                          quiet = True,
+                                          **c[1])
+                    if ret != 0:
+                        break
+                    if not read:
+                        os.environ["GRASS_PNG_READ"] = "TRUE"
+                
+                os.environ["GRASS_PNG_READ"] = "FALSE"
+            else:
+                ret, msg = RunCommand(self.cmd[0],
+                                      getErrorMsg = True,
+                                      quiet = True,
+                                      **self.cmd[1])
+            
+            if msg:
+                sys.stderr.write(_("Command '%s' failed\n") % self.GetCmd(string = True))
+                sys.stderr.write(_("Details: %s\n") % msg)
+            if ret != 0:
+                raise GException()
+        
+        except GException:
+            # clean up after problems
+            try:
+                os.remove(self.mapfile)
+                os.remove(self.maskfile)
+                os.remove(self.gtemp)
+            except (OSError, TypeError):
+                pass
+            self.mapfile = None
+            self.maskfile = None
+        
+        # stop monitor
+        if UserSettings.Get(group='display', key='driver', subkey='type') == 'cairo':
+#            gcmd.RunCommand('d.mon',
+#                            stop = 'cairo')
+            del os.environ["GRASS_CAIROFILE"]
+        elif "GRASS_PNGFILE" in os.environ:
+            del os.environ["GRASS_PNGFILE"]
+        
+        self.force_render = False
+        
+        return self.mapfile
+    
+    def GetCmd(self, string = False):
+        """!Get GRASS command as list of string.
+        
+        @param string get command as string if True otherwise as list
+        
+        @return command list/string
+        """
+        if string:
+            if self.type == 'command':
+                scmd = []
+                for c in self.cmd:
+                    scmd.append(utils.GetCmdString(c))
+                
+                return ';'.join(scmd)
+            else:
+                return utils.GetCmdString(self.cmd)
+        else:
+            return self.cmd
+
+    def GetType(self):
+        """!Get map layer type"""
+        return self.type
+    
+    def GetElement(self):
+        """!Get map element type"""
+        if self.type == 'raster':
+            return 'cell'
+        return self.type
+    
+    def GetOpacity(self, float = False):
+        """
+        Get layer opacity level
+        
+        @param float get opacity level in <0,1> otherwise <0,100>
+        
+        @return opacity level
+        """
+        if float:
+            return self.opacity
+        
+        return int (self.opacity * 100)
+
+    def GetName(self, fullyQualified = True):
+        """!Get map layer name
+
+        @param fullyQualified True to return fully qualified name as a
+        string 'name at mapset' otherwise directory { 'name', 'mapset' }
+        is returned
+
+        @return string / directory
+        """
+        if fullyQualified:
+            return self.name
+        else:
+            if '@' in self.name:
+                return { 'name' : self.name.split('@')[0],
+                         'mapset' : self.name.split('@')[1] }
+            else:
+                return { 'name' : self.name,
+                         'mapset' : '' }
+        
+    def IsActive(self):
+        """!Check if layer is activated for rendering"""
+        return self.active
+    
+    def SetType(self, type):
+        """!Set layer type"""
+        if type not in ('raster', '3d-raster', 'vector',
+                        'overlay', 'command',
+                        'shaded', 'rgb', 'his', 'rastarrow', 'rastnum',
+                        'thememap', 'themechart', 'grid', 'labels',
+                        'geodesic','rhumb'):
+            raise GException(_("Unsupported map layer type '%s'") % type)
+        
+        self.type = type
+
+    def SetName(self, name):
+        """!Set layer name"""
+        self.name = name
+        
+    def SetActive(self, enable = True):
+        """!Active or deactive layer"""
+        self.active = bool(enable)
+
+    def SetHidden(self, enable = False):
+        """!Hide or show map layer in Layer Manager"""
+        self.hidden = bool(enable)
+
+    def SetOpacity(self, value):
+        """!Set opacity value"""
+        if value < 0:
+            value = 0.
+        elif value > 1:
+            value = 1.
+        
+        self.opacity = float(value)
+        
+    def SetCmd(self, cmd):
+        """!Set new command for layer"""
+        if self.type == 'command':
+            self.cmd = []
+            for c in cmd:
+                self.cmd.append(utils.CmdToTuple(c))
+        else:
+            self.cmd  = utils.CmdToTuple(cmd)
+        Debug.msg(3, "Layer.SetCmd(): cmd='%s'" % self.GetCmd(string = True))
+        
+        # for re-rendering
+        self.force_render = True
+        
+class MapLayer(Layer):
+    def __init__(self, type, cmd, name = None,
+                 active = True, hidden = False, opacity = 1.0): 
+        """!Represents map layer in the map canvas
+        
+        @param type layer type ('raster', 'vector', 'command', etc.)
+        @param cmd GRASS command to render layer,
+        given as list, e.g. ['d.rast', 'map=elevation at PERMANENT']
+        @param name layer name, e.g. 'elevation at PERMANENT' (for layer tree) or None
+        @param active layer is active, will be rendered only if True
+        @param hidden layer is hidden, won't be listed in Layer Manager if True
+        @param opacity layer opacity <0;1>
+        """
+        Layer.__init__(self, type, cmd, name,
+                       active, hidden, opacity)
+        
+    def GetMapset(self):
+        """!Get mapset of map layer
+        
+        @return mapset name
+        @return '' on error (no name given)
+        """
+        if not self.name:
+            return ''
+        
+        try:
+            return self.name.split('@')[1]
+        except IndexError:
+            return self.name
+        
+class Overlay(Layer):
+    def __init__(self, id, type, cmd,
+                 active = True, hidden = True, opacity = 1.0):
+        """!Represents overlay displayed in map canvas
+        
+        @param id overlay id (for PseudoDC)
+        @param type overlay type ('barscale', 'legend', etc.)
+        @param cmd GRASS command to render overlay,
+        given as list, e.g. ['d.legend', 'map=elevation at PERMANENT']
+        @param active layer is active, will be rendered only if True
+        @param hidden layer is hidden, won't be listed in Layer Manager if True
+        @param opacity layer opacity <0;1>
+        """
+        Layer.__init__(self, 'overlay', cmd, type,
+                       active, hidden, opacity)
+        
+        self.id = id
+        
+class Map(object):
+    """!Map composition (stack of map layers and overlays)
+    """
+    def __init__(self, gisrc = None):
+        # region/extent settigns
+        self.wind      = dict() # WIND settings (wind file)
+        self.region    = dict() # region settings (g.region)
+        self.width     = 640    # map width
+        self.height    = 480    # map height
+        
+        # list of layers
+        self.layers    = list()  # stack of available GRASS layer
+        
+        self.overlays  = list()  # stack of available overlays
+        self.ovlookup  = dict()  # lookup dictionary for overlay items and overlays
+        
+        # environment settings
+        # environment variables, like MAPSET, LOCATION_NAME, etc.
+        self.env   = dict()
+        # path to external gisrc
+        self.gisrc = gisrc
+        
+        # generated file for g.pnmcomp output for rendering the map
+        self.mapfile = tempfile.mkstemp(suffix = '.ppm')[1]
+        
+        # setting some initial env. variables
+        self._initGisEnv() # g.gisenv
+        self.GetWindow()
+        # GRASS environment variable (for rendering)
+        os.environ["GRASS_TRANSPARENT"] = "TRUE"
+        os.environ["GRASS_BACKGROUNDCOLOR"] = "ffffff"
+        
+        # projection info
+        self.projinfo = self._projInfo()
+        
+    def _runCommand(self, cmd, **kwargs):
+        """!Run command in environment defined by self.gisrc if
+        defined"""
+        # use external gisrc if defined
+        gisrc_orig = os.getenv("GISRC")
+        if self.gisrc:
+            os.environ["GISRC"] = self.gisrc
+        
+        ret = cmd(**kwargs)
+        
+        # back to original gisrc
+        if self.gisrc:
+            os.environ["GISRC"] = gisrc_orig
+        
+        return ret
+    
+    def _initGisEnv(self):
+        """!Stores GRASS variables (g.gisenv) to self.env variable
+        """
+        if not os.getenv("GISBASE"):
+            sys.exit(_("GISBASE not set. You must be in GRASS GIS to run this program."))
+        
+        self.env = self._runCommand(grass.gisenv)
+            
+    def GetProjInfo(self):
+        """!Get projection info"""
+        return self.projinfo
+    
+    def _projInfo(self):
+        """!Return region projection and map units information
+        """
+        projinfo = dict()
+        if not grass.find_program('g.proj', ['--help']):
+            sys.exit(_("GRASS module '%s' not found. Unable to start map "
+                       "display window.") % 'g.proj')
+        
+        ret = self._runCommand(RunCommand, prog = 'g.proj',
+                               read = True, flags = 'p')
+        
+        if not ret:
+            return projinfo
+        
+        for line in ret.splitlines():
+            if ':' in line:
+                key, val = map(lambda x: x.strip(), line.split(':'))
+                if key in ['units']:
+                    val = val.lower()
+                projinfo[key] = val
+            elif "XY location (unprojected)" in line:
+                projinfo['proj'] = 'xy'
+                projinfo['units'] = ''
+                break
+        
+        return projinfo
+    
+    def GetWindow(self):
+        """!Read WIND file and set up self.wind dictionary"""
+        # FIXME: duplicated region WIND == g.region (at least some values)
+        filename = os.path.join (self.env['GISDBASE'],
+                                 self.env['LOCATION_NAME'],
+                                 self.env['MAPSET'],
+                                 "WIND")
+        try:
+            windfile = open (filename, "r")
+        except IOError, e:
+            sys.exit(_("Error: Unable to open '%(file)s'. Reason: %(ret)s. wxGUI exited.\n") % \
+                         { 'file' : filename, 'ret' : e})
+        
+        for line in windfile.readlines():
+            line = line.strip()
+            key, value = line.split(":", 1)
+            self.wind[key.strip()] = value.strip()
+        
+        windfile.close()
+        
+        return self.wind
+        
+    def AdjustRegion(self):
+        """!Adjusts display resolution to match monitor size in
+        pixels. Maintains constant display resolution, not related to
+        computational region. Do NOT use the display resolution to set
+        computational resolution. Set computational resolution through
+        g.region.
+        """
+        mapwidth    = abs(self.region["e"] - self.region["w"])
+        mapheight   = abs(self.region['n'] - self.region['s'])
+        
+        self.region["nsres"] =  mapheight / self.height
+        self.region["ewres"] =  mapwidth  / self.width
+        self.region['rows']  = round(mapheight / self.region["nsres"])
+        self.region['cols']  = round(mapwidth / self.region["ewres"])
+        self.region['cells'] = self.region['rows'] * self.region['cols']
+        
+        Debug.msg (3, "Map.AdjustRegion(): %s" % self.region)
+        
+        return self.region
+
+    def AlignResolution(self):
+        """!Sets display extents to even multiple of current
+        resolution defined in WIND file from SW corner. This must be
+        done manually as using the -a flag can produce incorrect
+        extents.
+        """
+        # new values to use for saving to region file
+        new = {}
+        n = s = e = w = 0.0
+        nwres = ewres = 0.0
+        
+        # Get current values for region and display
+        reg = self.GetRegion()
+        nsres = reg['nsres']
+        ewres = reg['ewres']
+        
+        n = float(self.region['n'])
+        s = float(self.region['s'])
+        e = float(self.region['e'])
+        w = float(self.region['w'])
+        
+        # Calculate rows, columns, and extents
+        new['rows'] = math.fabs(round((n-s)/nsres))
+        new['cols'] = math.fabs(round((e-w)/ewres))
+        
+        # Calculate new extents
+        new['s'] = nsres * round(s / nsres)
+        new['w'] = ewres * round(w / ewres)
+        new['n'] = new['s'] + (new['rows'] * nsres)
+        new['e'] = new['w'] + (new['cols'] * ewres)
+        
+        return new
+
+    def AlignExtentFromDisplay(self):
+        """!Align region extent based on display size from center
+        point"""
+        # calculate new bounding box based on center of display
+        if self.region["ewres"] > self.region["nsres"]:
+            res = self.region["ewres"]
+        else:
+            res = self.region["nsres"]
+        
+        Debug.msg(3, "Map.AlignExtentFromDisplay(): width=%d, height=%d, res=%f, center=%f,%f" % \
+                      (self.width, self.height, res, self.region['center_easting'],
+                       self.region['center_northing']))
+            
+        ew = (self.width / 2) * res
+        ns = (self.height / 2) * res
+        
+        self.region['n'] = self.region['center_northing'] + ns
+        self.region['s'] = self.region['center_northing'] - ns
+        self.region['e'] = self.region['center_easting'] + ew
+        self.region['w'] = self.region['center_easting'] - ew
+        
+        # LL locations
+        if self.projinfo['proj'] == 'll':
+            self.region['n'] = min(self.region['n'], 90.0)
+            self.region['s'] = max(self.region['s'], -90.0)
+        
+    def ChangeMapSize(self, (width, height)):
+        """!Change size of rendered map.
+        
+        @param width,height map size
+
+        @return True on success
+        @return False on failure
+        """
+        try:
+            self.width  = int(width)
+            self.height = int(height)
+            Debug.msg(2, "Map.ChangeMapSize(): width=%d, height=%d" % \
+                          (self.width, self.height))
+            return True
+        except:
+            self.width  = 640
+            self.height = 480
+            return False
+        
+    def GetRegion(self, rast = [], zoom = False, vect = [], regionName = None,
+                  n = None, s = None, e = None, w = None, default = False,
+                  update = False):
+        """!Get region settings (g.region -upgc)
+        
+        Optionally extent, raster or vector map layer can be given.
+        
+        @param rast list of raster maps
+        @param zoom zoom to raster map (ignore NULLs)
+        @param vect list of vector maps
+        @param regionName  named region or None
+        @param n,s,e,w force extent
+        @param default force default region settings
+        @param update if True update current display region settings
+        
+        @return region settings as directory, e.g. {
+        'n':'4928010', 's':'4913700', 'w':'589980',...}
+        
+        @see GetCurrentRegion()
+        """
+        region = {}
+        
+        tmpreg = os.getenv("GRASS_REGION")
+        if tmpreg:
+            del os.environ["GRASS_REGION"]
+        
+        # use external gisrc if defined
+        gisrc_orig = os.getenv("GISRC")
+        if self.gisrc:
+            os.environ["GISRC"] = self.gisrc
+        
+        # do not update & shell style output
+        cmd = {}
+        cmd['flags'] = 'ugpc'
+        
+        if default:
+            cmd['flags'] += 'd'
+        
+        if regionName:
+            cmd['region'] = regionName
+        
+        if n:
+            cmd['n'] = n
+        if s:
+            cmd['s'] = s
+        if e:
+            cmd['e'] = e
+        if w:
+            cmd['w'] = w
+        
+        if rast:
+            if zoom:
+                cmd['zoom'] = rast[0]
+            else:
+                cmd['rast'] = ','.join(rast)
+        
+        if vect:
+            cmd['vect'] = ','.join(vect)
+        
+        ret, reg, msg = RunCommand('g.region',
+                                   read = True,
+                                   getErrorMsg = True,
+                                   **cmd)
+        
+        if ret != 0:
+            if rast:
+                message = _("Unable to zoom to raster map <%s>.") % rast[0] + \
+                    "\n\n" + _("Details:") + " %s" % msg
+            elif vect:
+                message = _("Unable to zoom to vector map <%s>.") % vect[0] + \
+                    "\n\n" + _("Details:") + " %s" % msg
+            else:
+                message = _("Unable to get current geographic extent. "
+                            "Force quiting wxGUI. Please manually run g.region to "
+                            "fix the problem.")
+            GError(message)
+            return self.region
+        
+        for r in reg.splitlines():
+            key, val = r.split("=", 1)
+            try:
+                region[key] = float(val)
+            except ValueError:
+                region[key] = val
+        
+        # back to original gisrc
+        if self.gisrc:
+            os.environ["GISRC"] = gisrc_orig
+        
+        # restore region
+        if tmpreg:
+            os.environ["GRASS_REGION"] = tmpreg
+        
+        Debug.msg (3, "Map.GetRegion(): %s" % region)
+        
+        if update:
+            self.region = region
+        
+        return region
+
+    def GetCurrentRegion(self):
+        """!Get current display region settings
+        
+        @see GetRegion()
+        """
+        return self.region
+
+    def SetRegion(self, windres = False):
+        """!Render string for GRASS_REGION env. variable, so that the
+        images will be rendered from desired zoom level.
+        
+        @param windres uses resolution from WIND file rather than
+        display (for modules that require set resolution like
+        d.rast.num)
+
+        @return String usable for GRASS_REGION variable or None
+        """
+        grass_region = ""
+        
+        if windres:
+            compRegion = self.GetRegion()
+            region = copy.copy(self.region)
+            for key in ('nsres', 'ewres', 'cells'):
+                region[key] = compRegion[key]
+        else:
+            # adjust region settings to match monitor
+            region = self.AdjustRegion()
+        
+        # read values from wind file
+        try:
+            for key in self.wind.keys():
+                if key == 'north':
+                    grass_region += "north: %s; " % \
+                        (region['n'])
+                    continue
+                elif key == "south":
+                    grass_region += "south: %s; " % \
+                        (region['s'])
+                    continue
+                elif key == "east":
+                    grass_region += "east: %s; " % \
+                        (region['e'])
+                    continue
+                elif key == "west":
+                    grass_region += "west: %s; " % \
+                        (region['w'])
+                    continue
+                elif key == "e-w resol":
+                    grass_region += "e-w resol: %f; " % \
+                        (region['ewres'])
+                    continue
+                elif key == "n-s resol":
+                    grass_region += "n-s resol: %f; " % \
+                        (region['nsres'])
+                    continue
+                elif key == "cols":
+                    if windres:
+                        continue
+                    grass_region += 'cols: %d; ' % \
+                        region['cols']
+                    continue
+                elif key == "rows":
+                    if windres:
+                        continue
+                    grass_region += 'rows: %d; ' % \
+                        region['rows']
+                    continue
+                else:
+                    grass_region += key + ": "  + self.wind[key] + "; "
+            
+            Debug.msg (3, "Map.SetRegion(): %s" % grass_region)
+            
+            return grass_region
+        
+        except:
+            return None
+        
+    def GetListOfLayers(self, l_type = None, l_mapset = None, l_name = None,
+                        l_active = None, l_hidden = None):
+        """!Returns list of layers of selected properties or list of
+        all layers.
+
+        @param l_type layer type, e.g. raster/vector/wms/overlay (value or tuple of values)
+        @param l_mapset all layers from given mapset (only for maplayers)
+        @param l_name all layers with given name
+        @param l_active only layers with 'active' attribute set to True or False
+        @param l_hidden only layers with 'hidden' attribute set to True or False
+        
+        @return list of selected layers
+        """
+        selected = []
+        
+        if type(l_type) == types.StringType:
+            one_type = True
+        else:
+            one_type = False
+        
+        if one_type and l_type == 'overlay':
+            llist = self.overlays
+        else:
+            llist = self.layers
+        
+        # ["raster", "vector", "wms", ... ]
+        for layer in llist:
+            # specified type only
+            if l_type != None:
+                if one_type and layer.type != l_type:
+                    continue
+                elif not one_type and layer.type not in l_type:
+                    continue
+            
+            # mapset
+            if (l_mapset != None and l_type != 'overlay') and \
+                    layer.GetMapset() != l_mapset:
+                continue
+            
+            # name
+            if l_name != None and layer.name != l_name:
+                continue
+            
+            # hidden and active layers
+            if l_active != None and \
+                   l_hidden != None:
+                if layer.active == l_active and \
+                       layer.hidden == l_hidden:
+                    selected.append(layer)
+            
+            # active layers
+            elif l_active != None:
+                if layer.active == l_active:
+                    selected.append(layer)
+            
+            # hidden layers
+            elif l_hidden != None:
+                if layer.hidden == l_hidden:
+                    selected.append(layer)
+            
+            # all layers
+            else:
+                selected.append(layer)
+        
+        Debug.msg (3, "Map.GetListOfLayers(): numberof=%d" % len(selected))
+        
+        return selected
+
+    def _renderLayers(self, force, mapWindow, maps, masks, opacities):
+        # render map layers
+        ilayer = 1
+        for layer in self.layers + self.overlays:
+            # skip dead or disabled map layers
+            if layer == None or layer.active == False:
+                continue
+            
+            # render if there is no mapfile
+            if force or \
+               layer.force_render or \
+               layer.mapfile == None or \
+               (not os.path.isfile(layer.mapfile) or not os.path.getsize(layer.mapfile)):
+                if not layer.Render():
+                    continue
+            
+            if mapWindow:
+                # update progress bar
+                ### wx.SafeYield(mapWindow)
+                event = wxUpdateProgressBar(value = ilayer)
+                wx.PostEvent(mapWindow, event)
+            
+            # add image to compositing list
+            if layer.type != "overlay":
+                maps.append(layer.mapfile)
+                masks.append(layer.maskfile)
+                opacities.append(str(layer.opacity))
+                
+            Debug.msg (3, "Map.Render() type=%s, layer=%s " % (layer.type, layer.name))
+            ilayer += 1
+        
+    def Render(self, force = False, mapWindow = None, windres = False):
+        """!Creates final image composite
+        
+        This function can conditionaly use high-level tools, which
+        should be avaliable in wxPython library
+        
+        @param force force rendering
+        @param reference for MapFrame instance (for progress bar)
+        @param windres use region resolution (True) otherwise display resolution
+        
+        @return name of file with rendered image or None
+        """
+        maps = []
+        masks = []
+        opacities = []
+
+        wx.BeginBusyCursor()
+        # use external gisrc if defined
+        gisrc_orig = os.getenv("GISRC")
+        if self.gisrc:
+            os.environ["GISRC"] = self.gisrc
+        
+        tmp_region = os.getenv("GRASS_REGION")
+        os.environ["GRASS_REGION"] = self.SetRegion(windres)
+        os.environ["GRASS_WIDTH"]  = str(self.width)
+        os.environ["GRASS_HEIGHT"] = str(self.height)
+        if UserSettings.Get(group='display', key='driver', subkey='type') == 'cairo':
+            os.environ["GRASS_AUTO_WRITE"] = "TRUE"
+            if "GRASS_RENDER_IMMEDIATE" in os.environ:
+                del os.environ["GRASS_RENDER_IMMEDIATE"]
+            os.environ["GRASS_RENDER_IMMEDIATE"] = "TRUE"
+        else:
+            os.environ["GRASS_PNG_AUTO_WRITE"] = "TRUE"
+            os.environ["GRASS_PNG_READ"] = "FALSE"
+            os.environ["GRASS_COMPRESSION"] = "0"
+            os.environ["GRASS_TRUECOLOR"] = "TRUE"
+            os.environ["GRASS_RENDER_IMMEDIATE"] = "TRUE"
+        
+        self._renderLayers(force, mapWindow, maps, masks, opacities)
+        
+        # ugly hack for MSYS
+        if sys.platform != 'win32':
+            mapstr = ",".join(maps)
+            maskstr = ",".join(masks)
+            mapoutstr = self.mapfile
+        else:
+            mapstr = ""
+            for item in maps:
+                mapstr += item.replace('\\', '/')		
+            mapstr = mapstr.rstrip(',')
+            maskstr = ""
+            for item in masks:
+                maskstr += item.replace('\\', '/')
+            maskstr = maskstr.rstrip(',')
+            mapoutstr = self.mapfile.replace('\\', '/')
+        
+        # compose command
+        bgcolor = ':'.join(map(str, UserSettings.Get(group = 'display', key = 'bgcolor',
+                                                     subkey = 'color')))
+        
+        # render overlays
+        if tmp_region:
+            os.environ["GRASS_REGION"] = tmp_region
+        else:
+            del os.environ["GRASS_REGION"]
+        
+        if maps:
+            # run g.pngcomp to get composite image
+            ret, msg = RunCommand('g.pnmcomp',
+                                  getErrorMsg = True,
+                                  input = '%s' % ",".join(maps),
+                                  mask = '%s' % ",".join(masks),
+                                  opacity = '%s' % ",".join(opacities),
+                                  background = bgcolor,
+                                  width = self.width,
+                                  height = self.height,
+                                  output = self.mapfile)
+            
+            if ret != 0:
+                print >> sys.stderr, _("ERROR: Rendering failed. Details: %s") % msg
+                wx.EndBusyCursor()
+                return None
+            
+            Debug.msg (3, "Map.Render() force=%s file=%s" % (force, self.mapfile))
+        
+        # back to original gisrc
+        if self.gisrc:
+            os.environ["GISRC"] = gisrc_orig
+        
+        wx.EndBusyCursor()
+        if not maps:
+            return None
+        
+        return self.mapfile
+
+    def AddLayer(self, type, command, name = None,
+                 l_active = True, l_hidden = False, l_opacity = 1.0, l_render = False,
+                 pos = -1):
+        """!Adds generic map layer to list of layers
+        
+        @param type layer type ('raster', 'vector', etc.)
+        @param command  GRASS command given as list
+        @param name layer name
+        @param l_active layer render only if True
+        @param l_hidden layer not displayed in layer tree if True
+        @param l_opacity opacity level range from 0(transparent) - 1(not transparent)
+        @param l_render render an image if True
+        @param pos position in layer list (-1 for append)
+        
+        @return new layer on success
+        @return None on failure
+        """
+        wx.BeginBusyCursor()
+        # l_opacity must be <0;1>
+        if l_opacity < 0: l_opacity = 0
+        elif l_opacity > 1: l_opacity = 1
+        layer = MapLayer(type = type, name = name, cmd = command,
+                         active = l_active, hidden = l_hidden, opacity = l_opacity)
+        
+        # add maplayer to the list of layers
+        if pos > -1:
+            self.layers.insert(pos, layer)
+        else:
+            self.layers.append(layer)
+        
+        Debug.msg (3, "Map.AddLayer(): layer=%s" % layer.name)
+        if l_render:
+            if not layer.Render():
+                raise GException(_("Unable to render map layer <%s>.") % name)
+        
+        wx.EndBusyCursor()
+
+        return layer
+
+    def DeleteLayer(self, layer, overlay = False):
+        """!Removes layer from list of layers
+        
+        @param layer layer instance in layer tree
+        @param overlay delete overlay (use self.DeleteOverlay() instead)
+
+        @return removed layer on success or None
+        """
+        Debug.msg (3, "Map.DeleteLayer(): name=%s" % layer.name)
+        
+        if overlay:
+            list = self.overlays
+        else:
+            list = self.layers
+        
+        if layer in list:
+            if layer.mapfile:
+                base = os.path.split(layer.mapfile)[0]
+                mapfile = os.path.split(layer.mapfile)[1]
+                tempbase = mapfile.split('.')[0]
+                if base == '' or tempbase == '':
+                    return None
+                basefile = os.path.join(base, tempbase) + r'.*'
+                for f in glob.glob(basefile):
+                    os.remove(f)
+            list.remove(layer)
+            
+            return layer
+        
+        return None
+
+    def ReorderLayers(self, layerList):
+        """!Reorder list to match layer tree
+        
+        @param layerList list of layers
+        """
+        self.layers = layerList
+        
+        layerNameList = ""
+        for layer in self.layers:
+            if layer.name:
+                layerNameList += layer.name + ','
+        Debug.msg (4, "Map.ReoderLayers(): layers=%s" % \
+                   (layerNameList))
+        
+    def ChangeLayer(self, layer, render = False, **kargs):
+        """!Change map layer properties
+
+        @param layer map layer instance
+        @param type layer type ('raster', 'vector', etc.)
+        @param command  GRASS command given as list
+        @param name layer name
+        @param active layer render only if True
+        @param hidden layer not displayed in layer tree if True
+        @param opacity opacity level range from 0(transparent) - 1(not transparent)
+        @param render render an image if True
+        """
+        Debug.msg (3, "Map.ChangeLayer(): layer=%s" % layer.name)
+        
+        if 'type' in kargs:
+            layer.SetType(kargs['type']) # check type
+        
+        if 'command' in kargs:
+            layer.SetCmd(kargs['command'])
+        
+        if 'name' in kargs:
+            layer.SetName(kargs['name'])
+        
+        if 'active' in kargs:
+            layer.SetActive(kargs['active'])
+        
+        if 'hidden' in kargs:
+            layer.SetHidden(kargs['hidden'])
+        
+        if 'opacity' in kargs:
+            layer.SetOpacity(kargs['opacity'])
+        
+        if render and not layer.Render():
+            raise GException(_("Unable to render map layer <%s>.") % 
+                             name)
+        
+        return layer
+
+    def ChangeOpacity(self, layer, l_opacity):
+        """!Changes opacity value of map layer
+
+        @param layer layer instance in layer tree
+        @param l_opacity opacity level <0;1>
+        """
+        # l_opacity must be <0;1>
+        if l_opacity < 0: l_opacity = 0
+        elif l_opacity > 1: l_opacity = 1
+        
+        layer.opacity = l_opacity
+        Debug.msg (3, "Map.ChangeOpacity(): layer=%s, opacity=%f" % \
+                   (layer.name, layer.opacity))
+
+    def ChangeLayerActive(self, layer, active):
+        """!Enable or disable map layer
+        
+        @param layer layer instance in layer tree
+        @param active to be rendered (True)
+        """
+        layer.active = active
+        
+        Debug.msg (3, "Map.ChangeLayerActive(): name='%s' -> active=%d" % \
+                   (layer.name, layer.active))
+
+    def ChangeLayerName (self, layer, name):
+        """!Change name of the layer
+        
+        @param layer layer instance in layer tree
+        @param name  layer name to set up
+        """
+        Debug.msg (3, "Map.ChangeLayerName(): from=%s to=%s" % \
+                   (layer.name, name))
+        layer.name =  name
+
+    def RemoveLayer(self, name = None, id = None):
+        """!Removes layer from layer list
+        
+        Layer is defined by name at mapset or id.
+        
+        @param name layer name (must be unique)
+        @param id layer index in layer list
+
+        @return removed layer on success
+        @return None on failure
+        """
+        # delete by name
+        if name:
+            retlayer = None
+            for layer in self.layers:
+                if layer.name == name:
+                    retlayer = layer
+                    os.remove(layer.mapfile)
+                    os.remove(layer.maskfile)
+                    self.layers.remove(layer)
+                    return layer
+        # del by id
+        elif id != None:
+            return self.layers.pop(id)
+        
+        return None
+
+    def GetLayerIndex(self, layer, overlay = False):
+        """!Get index of layer in layer list.
+        
+        @param layer layer instace in layer tree
+        @param overlay use list of overlays instead
+        
+        @return layer index
+        @return -1 if layer not found
+        """
+        if overlay:
+            list = self.overlay
+        else:
+            list = self.layers
+            
+        if layer in list:
+            return list.index(layer)
+        
+        return -1
+
+    def AddOverlay(self, id, type, command,
+                   l_active = True, l_hidden = True, l_opacity = 1.0, l_render = False):
+        """!Adds overlay (grid, barscale, legend, etc.) to list of
+        overlays
+        
+        @param id overlay id (PseudoDC)
+        @param type overlay type (barscale, legend)
+        @param command GRASS command to render overlay
+        @param l_active overlay activated (True) or disabled (False)
+        @param l_hidden overlay is not shown in layer tree (if True)
+        @param l_render render an image (if True)
+        
+        @return new layer on success
+        @retutn None on failure
+        """
+        Debug.msg (2, "Map.AddOverlay(): cmd=%s, render=%d" % (command, l_render))
+        overlay = Overlay(id = id, type = type, cmd = command,
+                          active = l_active, hidden = l_hidden, opacity = l_opacity)
+        
+        # add maplayer to the list of layers
+        self.overlays.append(overlay)
+        
+        if l_render and command != '' and not overlay.Render():
+            raise GException(_("Unable to render overlay <%s>.") % 
+                             name)
+        
+        return self.overlays[-1]
+
+    def ChangeOverlay(self, id, render = False, **kargs):
+        """!Change overlay properities
+        
+        Add new overlay if overlay with 'id' doesn't exist.
+        
+        @param id overlay id (PseudoDC)
+        @param type overlay type (barscale, legend)
+        @param command GRASS command to render overlay
+        @param l_active overlay activated (True) or disabled (False)
+        @param l_hidden overlay is not shown in layer tree (if True)
+        @param l_render render an image (if True)
+        
+        @return new layer on success
+        """
+        overlay = self.GetOverlay(id, list = False)
+        if  overlay is None:
+            overlay = Overlay(id, type = None, cmd = None)
+        
+        if 'type' in kargs:
+            overlay.SetName(kargs['type']) # type -> overlay
+        
+        if 'command' in kargs:
+            overlay.SetCmd(kargs['command'])
+        
+        if 'active' in kargs:
+            overlay.SetActive(kargs['active'])
+        
+        if 'hidden' in kargs:
+            overlay.SetHidden(kargs['hidden'])
+        
+        if 'opacity' in kargs:
+            overlay.SetOpacity(kargs['opacity'])
+        
+        if render and overlay.GetCmd() != [] and not overlay.Render():
+            raise GException(_("Unable to render overlay <%s>.") % 
+                             name)
+        
+        return overlay
+
+    def GetOverlay(self, id, list = False):
+        """!Return overlay(s) with 'id'
+        
+        @param id overlay id
+        @param list return list of overlays of True
+        otherwise suppose 'id' to be unique
+        
+        @return list of overlays (list=True)
+        @return overlay (list=False)
+        @retur None (list=False) if no overlay or more overlays found
+        """
+        ovl = []
+        for overlay in self.overlays:
+            if overlay.id == id:
+                ovl.append(overlay)
+                
+        if not list:
+            if len(ovl) != 1:
+                return None
+            else:
+                return ovl[0]
+        
+        return ovl
+
+    def DeleteOverlay(self, overlay):
+        """!Delete overlay
+        
+        @param overlay overlay layer
+        
+        @return removed overlay on success or None
+        """
+        return self.DeleteLayer(overlay, overlay = True)
+
+    def Clean(self):
+        """!Clean layer stack - go trough all layers and remove them
+        from layer list.
+
+        Removes also l_mapfile and l_maskfile
+        
+        @return False on failure
+        @return True on success
+        """
+        try:
+            dir = os.path.dirname(self.mapfile)
+            base = os.path.basename(self.mapfile).split('.')[0]
+            removepath = os.path.join(dir,base)+r'*'
+            for f in glob.glob(removepath):
+                os.remove(f)
+            for layer in self.layers:
+                if layer.mapfile:
+                    dir = os.path.dirname(layer.mapfile)
+                    base = os.path.basename(layer.mapfile).split('.')[0]
+                    removepath = os.path.join(dir,base)+r'*'
+                    for f in glob.glob(removepath):
+                        os.remove(f)
+                self.layers.remove(layer)
+            
+            for overlay in self.overlays:
+                if overlay.mapfile:
+                    dir = os.path.dirname(overlay.mapfile)
+                    base = os.path.basename(overlay.mapfile).split('.')[0]
+                    removepath = os.path.join(dir,base)+r'*'
+                    for f in glob.glob(removepath):
+                        os.remove(f)
+                self.overlays.remove(overlay)
+        except:
+            return False
+        
+        return True
+    
+    def ReverseListOfLayers(self):
+        """!Reverse list of layers"""
+        return self.layers.reverse()
+
+    def RenderOverlays(self, force):
+        """!Render overlays only (for nviz)"""
+        for layer in self.overlays:
+            if force or layer.force_render:
+                layer.Render()
+                
+if __name__ == "__main__":
+    import gettext
+    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+    
+    Map = Map()
+    Map.GetRegion(update = True)
+    
+    Map.AddLayer(type = "raster",
+                 name = "elevation",
+                 command = ["d.rast", "map=elevation at PERMANENT"],
+                 l_opacity = .7)
+    
+    Map.AddLayer(type = "vector",
+                 name = "roadsmajor",
+                 command = ["d.vect", "map=roadsmajor at PERMANENT", "color=red", "width=3", "type=line"])
+    
+    image = Map.Render(force = True)
+    
+    if image:
+        grass.call(["display", image])


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/core/settings.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/core/settings.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/core/settings.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,996 @@
+"""!
+ at package core.settings
+
+ at brief Default GUI settings
+
+List of classes:
+ - settings::Settings
+
+Usage:
+ at code
+from core.settings import UserSettings
+ at endcode
+
+(C) 2007-2011 by the GRASS Development Team
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+import copy
+import types
+
+from core       import globalvar
+from core.gcmd  import GException, GError
+from core.utils import GetSettingsPath, PathJoin
+
+class Settings:
+    """!Generic class where to store settings"""
+    def __init__(self):
+        # settings file
+        self.filePath = os.path.join(GetSettingsPath(), 'wx')
+        
+        # key/value separator
+        self.sep = ';'
+        
+        # define default settings
+        self._defaultSettings()  # -> self.defaultSettings
+        
+        # read settings from the file
+        self.userSettings = copy.deepcopy(self.defaultSettings)
+        try:
+            self.ReadSettingsFile()
+        except GException, e:
+            print >> sys.stderr, e.value
+        
+        # define internal settings
+        self._internalSettings() # -> self.internalSettings
+        
+    def _defaultSettings(self):
+        """!Define default settings
+        """
+        try:
+            projFile = PathJoin(os.environ["GRASS_PROJSHARE"], 'epsg')
+        except KeyError:
+            projFile = ''
+        
+        self.defaultSettings = {
+            #
+            # general
+            #
+            'general': {
+                # use default window layout (layer manager, displays, ...)
+                'defWindowPos' : {
+                    'enabled' : True,
+                    'dim' : '0,0,%d,%d,%d,0,%d,%d' % \
+                        (globalvar.GM_WINDOW_SIZE[0],
+                         globalvar.GM_WINDOW_SIZE[1],
+                         globalvar.GM_WINDOW_SIZE[0],
+                         globalvar.MAP_WINDOW_SIZE[0],
+                         globalvar.MAP_WINDOW_SIZE[1])
+                    },
+                # workspace
+                'workspace' : {
+                    'posDisplay' : {
+                        'enabled' : False
+                        },
+                    'posManager' : {
+                        'enabled' : False
+                        },
+                    },
+                },
+            'manager' : {
+                # show opacity level widget
+                'changeOpacityLevel' : {
+                    'enabled' : False
+                    }, 
+                # ask when removing layer from layer tree
+                'askOnRemoveLayer' : {
+                    'enabled' : True
+                    },
+                # ask when quiting wxGUI or closing display
+                'askOnQuit' : {
+                    'enabled' : True
+                    },
+                # hide tabs
+                'hideTabs' : {
+                    'search' : False,
+                    'pyshell' : False,
+                    },
+                'copySelectedTextToClipboard' : {
+                    'enabled' : False
+                    },
+                },
+            #
+            # appearance
+            #
+            'appearance': {
+                'outputfont' : {
+                    'type' : 'Courier New',
+                    'size': '10',
+                    },
+                # expand/collapse element list
+                'elementListExpand' : {
+                    'selection' : 0 
+                    },
+                'menustyle' : {
+                    'selection' : 1
+                    },
+                'gSelectPopupHeight' : {
+                    'value' : 200
+                    },
+                'iconTheme' : {
+                    'type' : 'grass2'
+                    }, # grass2, grass, silk
+                },
+            #
+            # display
+            #
+            'display': {
+                'font' : {
+                    'type' : '',
+                    'encoding': 'ISO-8859-1',
+                    },
+                'driver': {
+                    'type': 'default'
+                    },
+                'alignExtent' : {
+                    'enabled' : True
+                    },
+                'compResolution' : {
+                    'enabled' : False
+                    },
+                'autoRendering': {
+                    'enabled' : True
+                    },
+                'autoZooming' : {
+                    'enabled' : False
+                    },
+                'statusbarMode': {
+                    'selection' : 0
+                    },
+                'bgcolor': {
+                    'color' : (255, 255, 255, 255),
+                    },
+                'mouseWheelZoom' : {
+                    'enabled' : True,
+                    'selection' : 0,
+                    },
+                },
+            #
+            # projection
+            #
+            'projection' : {
+                'statusbar' : {
+                    'proj4'    : '',
+                    'epsg'     : '',
+                    'projFile' : projFile,
+                    },
+                'format' : {
+                    'll'  : 'DMS',
+                    'precision' : 2,
+                    },
+                },
+            #
+            # Attribute Table Manager
+            #
+            'atm' : {
+                'highlight' : {
+                    'color' : (255, 255, 0, 255),
+                    'width' : 2
+                    },
+                'leftDbClick' : {
+                    'selection' : 1 # draw selected
+                    },
+                'askOnDeleteRec' : {
+                    'enabled' : True
+                    },
+                'keycolumn' : {
+                    'value' : 'cat'
+                    },
+                'encoding' : {
+                    'value' : '',
+                    }
+                },
+            #
+            # Command
+            #
+            'cmd': {
+                'overwrite' : {
+                    'enabled' : False
+                    },
+                'closeDlg' : {
+                    'enabled' : False
+                    },
+                'verbosity' : {
+                    'selection' : 'grassenv'
+                    },
+                # d.rast
+                'rasterOverlay' : {
+                    'enabled' : True
+                    },
+                'rasterColorTable' : {
+                    'enabled'   : False,
+                    'selection' : 'rainbow',
+                    },
+                # d.vect
+                'showType': {
+                    'point' : {
+                        'enabled' : True
+                        },
+                    'line' : {
+                        'enabled' : True
+                        },
+                    'centroid' : {
+                        'enabled' : True
+                        },
+                    'boundary' : {
+                        'enabled' : True
+                        },
+                    'area' : {
+                        'enabled' : True
+                        },
+                    'face' : {
+                        'enabled' : True
+                        },
+                    },
+                'addNewLayer' : {
+                    'enabled' : True,
+                    },
+                'interactiveInput' : {
+                    'enabled' : True,
+                    },
+                },
+            #
+            # vdigit
+            #
+            'vdigit' : {
+                # symbology
+                'symbol' : {
+                    'highlight' : {
+                        'enabled' : None,
+                        'color' : (255, 255, 0, 255)
+                        }, # yellow
+                    'highlightDupl' : {
+                        'enabled' : None,
+                        'color' : (255, 72, 0, 255)
+                        }, # red
+                    'point' : {
+                        'enabled' : True,
+                        'color' : (0, 0, 0, 255)
+                        }, # black
+                    'line' : {
+                        'enabled' : True,
+                        'color' : (0, 0, 0, 255)
+                        }, # black
+                    'boundaryNo' : {
+                        'enabled' : True,
+                        'color' : (126, 126, 126, 255)
+                        }, # grey
+                    'boundaryOne' : {
+                        'enabled' : True,
+                        'color' : (0, 255, 0, 255)
+                        }, # green
+                    'boundaryTwo' : {
+                        'enabled' : True,
+                        'color' : (255, 135, 0, 255)
+                        }, # orange
+                    'centroidIn' : {
+                        'enabled' : True,
+                        'color' : (0, 0, 255, 255)
+                        }, # blue
+                    'centroidOut' : {
+                        'enabled' : True,
+                        'color' : (165, 42, 42, 255)
+                        }, # brown
+                    'centroidDup' : {
+                        'enabled' : True,
+                        'color' : (156, 62, 206, 255)
+                        }, # violet
+                    'nodeOne' : {
+                        'enabled' : True,
+                        'color' : (255, 0, 0, 255)
+                        }, # red
+                    'nodeTwo' : {
+                        'enabled' : True,
+                        'color' : (0, 86, 45, 255)
+                        }, # dark green
+                    'vertex' : {
+                        'enabled' : False,
+                        'color' : (255, 20, 147, 255)
+                        }, # deep pink
+                    'area' : {
+                        'enabled' : True,
+                        'color' : (217, 255, 217, 255)
+                        }, # green
+                    'direction' : {
+                        'enabled' : False,
+                        'color' : (255, 0, 0, 255)
+                        }, # red
+                    },
+                # display
+                'lineWidth' : {
+                    'value' : 2,
+                    'units' : 'screen pixels'
+                    },
+                # snapping
+                'snapping' : {
+                    'value' : 10,
+                    'units' : 'screen pixels'
+                    },
+                'snapToVertex' : {
+                    'enabled' : False
+                    },
+                # digitize new record
+                'addRecord' : {
+                    'enabled' : True
+                    },
+                'layer' :{
+                    'value' : 1
+                    },
+                'category' : {
+                    'value' : 1
+                    },
+                'categoryMode' : {
+                    'selection' : 0
+                    },
+                # delete existing feature(s)
+                'delRecord' : {
+                    'enabled' : True
+                    },
+                # query tool
+                'query' : {
+                    'selection' : 0,
+                    'box' : True
+                    },
+                'queryLength' : {
+                    'than-selection' : 0,
+                    'thresh' : 0
+                    },
+                'queryDangle' : {
+                    'than-selection' : 0,
+                    'thresh' : 0
+                    },
+                # select feature (point, line, centroid, boundary)
+                'selectType': {
+                    'point' : {
+                        'enabled' : True
+                        },
+                    'line' : {
+                        'enabled' : True
+                        },
+                    'centroid' : {
+                        'enabled' : True
+                        },
+                    'boundary' : {
+                        'enabled' : True
+                        },
+                    },
+                'selectThresh' : {
+                    'value' : 10,
+                    'units' : 'screen pixels'
+                    },
+                'checkForDupl' : {
+                    'enabled' : False
+                    },
+                'selectInside' : {
+                    'enabled' : False
+                    },
+                # exit
+                'saveOnExit' : {
+                    'enabled' : False,
+                    },
+                # break lines on intersection
+                'breakLines' : {
+                    'enabled' : False,
+                    },
+                },
+            'profile': {
+                'raster0' : {
+                    'pcolor' : (0, 0, 255, 255), # profile line color
+                    'pwidth' : 1, # profile line width
+                    'pstyle' : 'solid', # profile line pen style
+                    },
+                'raster1' : {
+                    'pcolor' : (255, 0, 0, 255), 
+                    'pwidth' : 1, 
+                    'pstyle' : 'solid', 
+                    },
+                'raster2' : {
+                    'pcolor' : (0, 255, 0, 255), 
+                    'pwidth' : 1, 
+                    'pstyle' : 'solid', 
+                    },
+                'font' : {
+                    'titleSize' : 12,
+                    'axisSize' : 11,
+                    'legendSize' : 10,
+                    },
+                'marker' : {
+                    'color' : (0, 0, 0, 255),
+                    'fill' : 'transparent',
+                    'size' : 2,
+                    'type' : 'triangle',
+                    'legend' : _('Segment break'),
+                    },
+                'grid' : {
+                    'color' : (200, 200, 200, 255),
+                    'enabled' : True,
+                    },
+                'x-axis' : {
+                    'type' : 'auto', # axis format
+                    'min' : 0, # axis min for custom axis range
+                    'max': 0, # axis max for custom axis range
+                    'log' : False,
+                    },
+                'y-axis' : {
+                    'type' : 'auto', # axis format
+                    'min' : 0, # axis min for custom axis range
+                    'max': 0, # axis max for custom axis range
+                    'log' : False,
+                    },
+                'legend' : {
+                    'enabled' : True
+                    },
+                },
+            'gcpman' : {
+                'rms' : {
+                    'highestonly' : True,
+                    'sdfactor' : 1,
+                    },
+                'symbol' : {
+                    'color' : (0, 0, 255, 255),
+                    'hcolor' : (255, 0, 0, 255),
+                    'scolor' : (0, 255, 0, 255),
+                    'ucolor' : (255, 165, 0, 255),
+                    'unused' : True,
+                    'size' : 8,
+                    'width' : 2,
+                    },
+                },
+            'nviz' : {
+                'view' : {
+                    'persp' : {
+                        'value' : 20,
+                        'step' : 2,
+                        },
+                    'position' : {
+                        'x' : 0.84,
+                        'y' : 0.16,
+                        },
+                    'twist' : {
+                        'value' : 0,
+                        },
+                    'z-exag' : {
+                        'min' : 0,
+                        'max' : 10,
+                        'value': 1,
+                        },
+                    'background' : {
+                        'color' : (255, 255, 255, 255), # white
+                        },
+                    },
+                'fly' : {
+                    'exag' : {
+                        'move' : 5,
+                        'turn' : 5,
+                        }
+                    },
+                'animation' : {
+                    'fps' : 24,
+                    'prefix' : _("animation")
+                    },
+                'surface' : {
+                    'shine': {
+                        'map' : False,
+                        'value' : 60.0,
+                        },
+                    'color' : {
+                        'map' : True,
+                        'value' : (100, 100, 100, 255), # constant: grey
+                        },
+                    'draw' : {
+                        'wire-color' : (136, 136, 136, 255),
+                        'mode' : 1, # fine
+                        'style' : 1, # surface
+                        'shading' : 1, # gouraud
+                        'res-fine' : 6,
+                        'res-coarse' : 9,
+                        },
+                    'position' : {
+                        'x' : 0,
+                        'y' : 0,
+                        'z' : 0,
+                        },
+                    },
+                'constant' : {
+                    'color' : (100, 100, 100, 255),
+                    'value' : 0.0,
+                    'transp' : 0,
+                    'resolution': 6
+                },
+                'vector' : {
+                    'lines' : {
+                        'show' : False,
+                        'width' : 2,
+                        'color' : (0, 0, 255, 255), # blue
+                        'flat' : False,
+                        'height' : 0,
+                        },
+                    'points' : {
+                        'show' : False,
+                        'size' : 100,
+                        'width' : 2,
+                        'marker' : 2,
+                        'color' : (0, 0, 255, 255), # blue
+                        'height' : 0,
+                        }
+                    },
+                'volume' : {
+                    'color' : {
+                        'map' : True,
+                        'value' : (100, 100, 100, 255), # constant: grey
+                        },
+                    'draw' : {
+                        'mode'       : 0, # isosurfaces
+                        'shading'    : 1, # gouraud
+                        'resolution' : 3, # polygon resolution
+                        },
+                    'shine': {
+                        'map' : False,
+                        'value' : 60,
+                        },
+                    'topo': {
+                        'map' : None,
+                        'value' : 0.0
+                        },
+                    'transp': {
+                        'map' : None,
+                        'value': 0
+                        },
+                    'mask': {
+                        'map' : None,
+                        'value': ''
+                        },
+                    'slice_position': {
+                        'x1' : 0,
+                        'x2' : 1,
+                        'y1' : 0,
+                        'y2' : 1,
+                        'z1' : 0,
+                        'z2' : 1,
+                        'axis' : 0,
+                        }
+                    },
+                'cplane' : {
+                    'shading': 4,
+                    'rotation':{
+                        'rot': 0, 
+                        'tilt': 0
+                        }, 
+                    'position':{
+                        'x' : 0,
+                        'y' : 0,
+                        'z' : 0
+                    }   
+                },
+                'light' : {
+                    'position' : {
+                        'x' : 0.68,
+                        'y' : -0.68,
+                        'z' : 80,
+                        },
+                    'bright'  : 80,
+                    'color'   : (255, 255, 255, 255), # white
+                    'ambient' : 20,
+                    },
+                'fringe' : {
+                    'elev'   : 55,
+                    'color'  : (128, 128, 128, 255), # grey
+                    },
+                'arrow': {
+                    'color': (0, 0, 0),
+                    },
+                'scalebar': {
+                    'color': (0, 0, 0),
+                    }
+                },
+            'modeler' : {
+                'disabled': {
+                    'color': (211, 211, 211, 255), # light grey
+                    },
+                'action' : {
+                    'color' : {
+                        'valid'   :  (180, 234, 154, 255), # light green
+                        'invalid' :  (255, 255, 255, 255), # white
+                        'running' :  (255, 0, 0, 255),     # red
+                        },
+                    'size' : {
+                        'width'  : 125,
+                        'height' : 50,
+                        },
+                    'width': {
+                        'parameterized' : 2,
+                        'default'       : 1,
+                        },
+                    },
+                'data' : { 
+                    'color': {
+                        'raster'   : (215, 215, 248, 255), # light blue
+                        'raster3d' : (215, 248, 215, 255), # light green
+                        'vector'   : (248, 215, 215, 255), # light red
+                        },
+                    'size' : {
+                        'width' : 175,
+                        'height' : 50,
+                        },
+                    },
+                'loop' : {
+                    'color' : {
+                        'valid'   :  (234, 226, 154, 255), # light yellow
+                        },
+                    'size' : {
+                        'width' : 175,
+                        'height' : 40,
+                        },
+                    },
+                'if-else' : {
+                    'size' : {
+                        'width' : 150,
+                        'height' : 40,
+                        },
+                    },
+                },
+            }
+
+        # quick fix, http://trac.osgeo.org/grass/ticket/1233
+        # TODO
+        if sys.platform == 'darwin':
+            self.defaultSettings['general']['defWindowPos']['enabled'] = False
+
+    def _internalSettings(self):
+        """!Define internal settings (based on user settings)
+        """
+        self.internalSettings = {}
+        for group in self.userSettings.keys():
+            self.internalSettings[group] = {}
+            for key in self.userSettings[group].keys():
+                self.internalSettings[group][key] = {}
+
+        # self.internalSettings['general']["mapsetPath"]['value'] = self.GetMapsetPath()
+        self.internalSettings['appearance']['elementListExpand']['choices'] = \
+            (_("Collapse all except PERMANENT and current"),
+             _("Collapse all except PERMANENT"),
+             _("Collapse all except current"),
+             _("Collapse all"),
+             _("Expand all"))
+        self.internalSettings['atm']['leftDbClick']['choices'] = (_('Edit selected record'),
+                                                                  _('Display selected'))
+        
+        self.internalSettings['cmd']['verbosity']['choices'] = ('grassenv',
+                                                                'verbose',
+                                                                'quiet')
+        
+        self.internalSettings['appearance']['iconTheme']['choices'] = ('grass',
+                                                                       'grass2',
+                                                                       'silk')
+        self.internalSettings['appearance']['menustyle']['choices'] = \
+                   (_("Classic (labels only)"),
+                    _("Combined (labels and module names)"),
+                    _("Professional (module names only)"))
+        self.internalSettings['appearance']['gSelectPopupHeight']['min'] = 50
+        # there is also maxHeight given to TreeCtrlComboPopup.GetAdjustedSize
+        self.internalSettings['appearance']['gSelectPopupHeight']['max'] = 1000
+        
+        self.internalSettings['display']['driver']['choices'] = ['default']
+        self.internalSettings['display']['statusbarMode']['choices'] = None # set during MapFrame init
+        self.internalSettings['display']['mouseWheelZoom']['choices'] = (_('Scroll forward to zoom in'),
+                                                                         _('Scroll back to zoom in'))
+
+        self.internalSettings['nviz']['view'] = {}
+        self.internalSettings['nviz']['view']['twist'] = {}
+        self.internalSettings['nviz']['view']['twist']['min'] = -180
+        self.internalSettings['nviz']['view']['twist']['max'] = 180
+        self.internalSettings['nviz']['view']['persp'] = {}
+        self.internalSettings['nviz']['view']['persp']['min'] = 1
+        self.internalSettings['nviz']['view']['persp']['max'] = 100
+        self.internalSettings['nviz']['view']['height'] = {}
+        self.internalSettings['nviz']['view']['height']['value'] = -1
+        self.internalSettings['nviz']['view']['z-exag'] = {}
+        self.internalSettings['nviz']['view']['z-exag']['original'] = 1
+        self.internalSettings['nviz']['view']['rotation'] = None
+        self.internalSettings['nviz']['view']['focus'] = {}
+        self.internalSettings['nviz']['view']['focus']['x'] = -1
+        self.internalSettings['nviz']['view']['focus']['y'] = -1
+        self.internalSettings['nviz']['view']['focus']['z'] = -1
+        self.internalSettings['nviz']['view']['dir'] = {}
+        self.internalSettings['nviz']['view']['dir']['x'] = -1
+        self.internalSettings['nviz']['view']['dir']['y'] = -1
+        self.internalSettings['nviz']['view']['dir']['z'] = -1
+        self.internalSettings['nviz']['view']['dir']['use'] = False
+        
+        for decor in ('arrow', 'scalebar'):
+            self.internalSettings['nviz'][decor] = {}
+            self.internalSettings['nviz'][decor]['position'] = {}
+            self.internalSettings['nviz'][decor]['position']['x'] = 0
+            self.internalSettings['nviz'][decor]['position']['y'] = 0
+            self.internalSettings['nviz'][decor]['size'] = 100
+        self.internalSettings['nviz']['vector'] = {}
+        self.internalSettings['nviz']['vector']['points'] = {}
+        self.internalSettings['nviz']['vector']['points']['marker'] = ("x",
+                                                                       _("box"),
+                                                                       _("sphere"),
+                                                                       _("cube"),
+                                                                       _("diamond"),
+                                                                       _("dtree"),
+                                                                       _("ctree"),
+                                                                       _("aster"),
+                                                                       _("gyro"),
+                                                                       _("histogram"))
+        self.internalSettings['vdigit']['bgmap'] = {}
+        self.internalSettings['vdigit']['bgmap']['value'] = ''
+        
+    def ReadSettingsFile(self, settings = None):
+        """!Reads settings file (mapset, location, gisdbase)"""
+        if settings is None:
+            settings = self.userSettings
+        
+        self._readFile(self.filePath, settings)
+        
+        # set environment variables
+        font = self.Get(group = 'display', key = 'font', subkey = 'type')
+        enc  = self.Get(group = 'display', key = 'font', subkey = 'encoding')
+        if font:
+            os.environ["GRASS_FONT"] = font
+        if enc:
+            os.environ["GRASS_ENCODING"] = enc
+        
+    def _readFile(self, filename, settings = None):
+        """!Read settings from file to dict
+
+        @param filename settings file path
+        @param settings dict where to store settings (None for self.userSettings)
+        """
+        if settings is None:
+            settings = self.userSettings
+        
+        if not os.path.exists(filename):
+            # try alternative path
+            filename = os.path.join(os.path.expanduser("~"), '.grasswx6')
+            if not os.path.exists(filename):
+                return
+        
+        try:
+            fd = open(filename, "r")
+        except IOError:
+            sys.stderr.write(_("Unable to read settings file <%s>\n") % filename)
+            return
+        
+        try:
+            line = ''
+            for line in fd.readlines():
+                line = line.rstrip('%s' % os.linesep)
+                group, key = line.split(self.sep)[0:2]
+                kv = line.split(self.sep)[2:]
+                subkeyMaster = None
+                if len(kv) % 2 != 0: # multiple (e.g. nviz)
+                    subkeyMaster = kv[0]
+                    del kv[0]
+                idx = 0
+                while idx < len(kv):
+                    if subkeyMaster:
+                        subkey = [subkeyMaster, kv[idx]]
+                    else:
+                        subkey = kv[idx]
+                    value = kv[idx+1]
+                    value = self._parseValue(value, read = True)
+                    self.Append(settings, group, key, subkey, value)
+                    idx += 2
+        except ValueError, e:
+            print >> sys.stderr, _("Error: Reading settings from file <%(file)s> failed.\n"
+                                   "\t\tDetails: %(detail)s\n"
+                                   "\t\tLine: '%(line)s'\n") % { 'file' : filename,
+                                                                 'detail' : e,
+                                                                 'line' : line }
+            fd.close()
+        
+        fd.close()
+        
+    def SaveToFile(self, settings = None):
+        """!Save settings to the file"""
+        if settings is None:
+            settings = self.userSettings
+        
+        dirPath = GetSettingsPath()
+        if not os.path.exists(dirPath):
+            try:
+                os.mkdir(dirPath)
+            except:
+                GError(_('Unable to create settings directory'))
+                return
+        
+        try:
+            file = open(self.filePath, "w")
+            for group in settings.keys():
+                for key in settings[group].keys():
+                    subkeys = settings[group][key].keys()
+                    file.write('%s%s%s%s' % (group, self.sep, key, self.sep))
+                    for idx in range(len(subkeys)):
+                        value = settings[group][key][subkeys[idx]]
+                        if type(value) == types.DictType:
+                            if idx > 0:
+                                file.write('%s%s%s%s%s' % (os.linesep, group, self.sep, key, self.sep))
+                            file.write('%s%s' % (subkeys[idx], self.sep))
+                            kvalues = settings[group][key][subkeys[idx]].keys()
+                            srange = range(len(kvalues))
+                            for sidx in srange:
+                                svalue = self._parseValue(settings[group][key][subkeys[idx]][kvalues[sidx]])
+                                file.write('%s%s%s' % (kvalues[sidx], self.sep,
+                                                       svalue))
+                                if sidx < len(kvalues) - 1:
+                                    file.write('%s' % self.sep)
+                        else:
+                            if idx > 0 and \
+                                    type(settings[group][key][subkeys[idx - 1]]) == types.DictType:
+                                file.write('%s%s%s%s%s' % (os.linesep, group, self.sep, key, self.sep))
+                            value = self._parseValue(settings[group][key][subkeys[idx]])
+                            file.write('%s%s%s' % (subkeys[idx], self.sep, value))
+                            if idx < len(subkeys) - 1 and \
+                                    type(settings[group][key][subkeys[idx + 1]]) != types.DictType:
+                                file.write('%s' % self.sep)
+                    file.write(os.linesep)
+        except IOError, e:
+            raise GException(e)
+        except StandardError, e:
+            raise GException(_('Writing settings to file <%(file)s> failed.'
+                               '\n\nDetails: %(detail)s') % { 'file' : self.filePath,
+                                                              'detail' : e })
+        
+        file.close()
+        
+    def _parseValue(self, value, read = False):
+        """!Parse value to be store in settings file"""
+        if read: # -> read settings (cast values)
+            if value == 'True':
+                value = True
+            elif value == 'False':
+                value = False
+            elif value == 'None':
+                value = None
+            elif ':' in value: # -> color
+                try:
+                    value = tuple(map(int, value.split(':')))
+                except ValueError: # -> string
+                    pass
+            else:
+                try:
+                    value = int(value)
+                except ValueError:
+                    try:
+                        value = float(value)
+                    except ValueError:
+                        pass
+        else: # -> write settings
+            if type(value) == type(()): # -> color
+                value = str(value[0]) + ':' +\
+                    str(value[1]) + ':' + \
+                    str(value[2])
+                
+        return value
+
+    def Get(self, group, key = None, subkey = None, internal = False):
+        """!Get value by key/subkey
+
+        Raise KeyError if key is not found
+        
+        @param group settings group
+        @param key (value, None)
+        @param subkey (value, list or None)
+        @param internal use internal settings instead
+
+        @return value
+        """
+        if internal is True:
+            settings = self.internalSettings
+        else:
+            settings = self.userSettings
+            
+        try:
+            if subkey is None:
+                if key is None:
+                    return settings[group]
+                else:
+                    return settings[group][key]
+            else:
+                if type(subkey) == type(tuple()) or \
+                        type(subkey) == type(list()):
+                    return settings[group][key][subkey[0]][subkey[1]]
+                else:
+                    return settings[group][key][subkey]  
+
+        except KeyError:
+            print >> sys.stderr, "Settings: unable to get value '%s:%s:%s'\n" % \
+                (group, key, subkey)
+        
+    def Set(self, group, value, key = None, subkey = None, internal = False):
+        """!Set value of key/subkey
+        
+        Raise KeyError if group/key is not found
+        
+        @param group settings group
+        @param key key (value, None)
+        @param subkey subkey (value, list or None)
+        @param value value
+        @param internal use internal settings instead
+        """
+        if internal is True:
+            settings = self.internalSettings
+        else:
+            settings = self.userSettings
+        
+        try:
+            if subkey is None:
+                if key is None:
+                    settings[group] = value
+                else:
+                    settings[group][key] = value
+            else:
+                if type(subkey) == type(tuple()) or \
+                        type(subkey) == type(list()):
+                    settings[group][key][subkey[0]][subkey[1]] = value
+                else:
+                    settings[group][key][subkey] = value
+        except KeyError:
+            raise GException("%s '%s:%s:%s'" % (_("Unable to set "), group, key, subkey))
+        
+    def Append(self, dict, group, key, subkey, value):
+        """!Set value of key/subkey
+
+        Create group/key/subkey if not exists
+        
+        @param dict settings dictionary to use
+        @param group settings group
+        @param key key
+        @param subkey subkey (value or list)
+        @param value value
+        """
+        if group not in dict:
+            dict[group] = {}
+        
+        if key not in dict[group]:
+            dict[group][key] = {}
+        
+        if type(subkey) == types.ListType:
+            # TODO: len(subkey) > 2
+            if subkey[0] not in dict[group][key]:
+                dict[group][key][subkey[0]] = {}
+            try:
+                dict[group][key][subkey[0]][subkey[1]] = value
+            except TypeError:
+                print >> sys.stderr, _("Unable to parse settings '%s'") % value + \
+                    ' (' + group + ':' + key + ':' + subkey[0] + ':' + subkey[1] + ')'
+        else:
+            try:
+                dict[group][key][subkey] = value
+            except TypeError:
+                print >> sys.stderr, _("Unable to parse settings '%s'") % value + \
+                    ' (' + group + ':' + key + ':' + subkey + ')'
+        
+    def GetDefaultSettings(self):
+        """!Get default user settings"""
+        return self.defaultSettings
+
+    def Reset(self, key = None):
+        """!Reset to default settings
+
+        @key key in settings dict (None for all keys)
+        """
+        if not key:
+            self.userSettings = copy.deepcopy(self.defaultSettings)
+        else:
+            self.userSettings[key] = copy.deepcopy(self.defaultSettings[key])
+        
+UserSettings = Settings()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/core/units.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/core/units.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/core/units.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,117 @@
+"""!
+ at package core.units
+
+ at brief Units management
+
+ at todo Probably will be replaced by Python ctypes fns in the near
+future(?)
+
+Usage:
+ at code
+from core.units import Units
+ at endcode
+
+Classes:
+ - units::BaseUnits
+
+(C) 2009, 2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+class BaseUnits:
+    def __init__(self):
+        self._units = dict()
+        self._units['length'] = { 0 : { 'key' : 'mu', 'label' : _('map units') },
+                             1 : { 'key' : 'me', 'label' : _('meters') },
+                             2 : { 'key' : 'km', 'label' : _('kilometers') },
+                             3 : { 'key' : 'mi', 'label' : _('miles') },
+                             4 : { 'key' : 'ft', 'label' : _('feet') } }
+        
+        self._units['area']   = { 0 : { 'key' : 'mu', 'label' : _('sq map units') },
+                             1 : { 'key' : 'me', 'label' : _('sq meters') },
+                             2 : { 'key' : 'km', 'label' : _('sq kilometers') },
+                             3 : { 'key' : 'ar', 'label' : _('acres') },
+                             4 : { 'key' : 'ht', 'label' : _('hectares') } }
+
+    def GetUnitsList(self, type):
+        """!Get list of units (their labels)
+        
+        @param type units type ('length' or 'area')
+        
+        @return list of units labels
+        """
+        result = list()
+        try:
+            keys = self._units[type].keys()
+            keys.sort()
+            for idx in keys:
+                result.append(self._units[type][idx]['label'])
+        except KeyError:
+            pass
+        
+        return result
+
+    def GetUnitsKey(self, type, index):
+        """!Get units key based on index
+        
+        @param type units type ('length' or 'area')
+        @param index units index
+        """
+        return self._units[type][index]['key']
+
+    def GetUnitsIndex(self, type, key):
+        """!Get units index based on key
+        
+        @param type units type ('length' or 'area')
+        @param key units key, e.g. 'me' for meters
+
+        @return index
+        """
+        for k, u in self._units[type].iteritems():
+            if u['key'] == key:
+                return k
+        return 0
+
+Units = BaseUnits()
+
+def ConvertValue(value, type, units):
+    """!Convert value from map units to given units
+
+    Inspired by vector/v.to.db/units.c
+
+    @param value value to be converted
+    @param type units type ('length', 'area')
+    @param unit  destination units
+    """
+    # get map units
+    # TODO
+    
+    f = 1
+    if type == 'length':
+        if units == 'me':
+            f = 1.0
+        elif units == 'km':
+            f = 1.0e-3
+        elif units == 'mi':
+            f = 6.21371192237334e-4
+        elif units == 'ft':
+            f = 3.28083989501312
+    else: # -> area
+        if units == 'me':
+            f = 1.0
+        elif units == 'km':
+            f = 1.0e-6
+        elif units == 'mi':
+            f = 3.86102158542446e-7
+        elif units == 'ft':
+            f = 10.7639104167097
+        elif units == 'ar':
+            f = 2.47105381467165e-4
+        elif units == 'ht':
+            f = 1.0e-4
+
+    return f * value


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/core/utils.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/core/utils.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/core/utils.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,766 @@
+"""!
+ at package core.utils
+
+ at brief Misc utilities for wxGUI
+
+(C) 2007-2009, 2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+ at author Jachym Cepicky
+"""
+
+import os
+import sys
+import platform
+import string
+import glob
+import shlex
+import re
+import locale
+
+from core.globalvar import ETCDIR
+sys.path.append(os.path.join(ETCDIR, "python"))
+
+from grass.script import core as grass
+from grass.script import task as gtask
+
+from core.gcmd  import RunCommand
+from core.debug import Debug
+
+def normalize_whitespace(text):
+    """!Remove redundant whitespace from a string"""
+    return string.join(string.split(text), ' ')
+
+def split(s):
+    """!Platform spefic shlex.split"""
+    if sys.version_info >= (2, 6):
+        return shlex.split(s, posix = (sys.platform != "win32"))
+    elif sys.platform == "win32":
+        return shlex.split(s.replace('\\', r'\\'))
+    else:
+        return shlex.split(s)
+
+def GetTempfile(pref=None):
+    """!Creates GRASS temporary file using defined prefix.
+
+    @todo Fix path on MS Windows/MSYS
+
+    @param pref prefer the given path
+
+    @return Path to file name (string) or None
+    """
+    ret = RunCommand('g.tempfile',
+                     read = True,
+                     pid = os.getpid())
+
+    tempfile = ret.splitlines()[0].strip()
+
+    # FIXME
+    # ugly hack for MSYS (MS Windows)
+    if platform.system() == 'Windows':
+	tempfile = tempfile.replace("/", "\\")
+    try:
+        path, file = os.path.split(tempfile)
+        if pref:
+            return os.path.join(pref, file)
+	else:
+	    return tempfile
+    except:
+        return None
+
+def GetLayerNameFromCmd(dcmd, fullyQualified = False, param = None,
+                        layerType = None):
+    """!Get map name from GRASS command
+    
+    Parameter dcmd can be modified when first parameter is not
+    defined.
+    
+    @param dcmd GRASS command (given as list)
+    @param fullyQualified change map name to be fully qualified
+    @param param params directory
+    @param layerType check also layer type ('raster', 'vector', '3d-raster', ...)
+    
+    @return tuple (name, found)
+    """
+    mapname = ''
+    found   = True
+    
+    if len(dcmd) < 1:
+        return mapname, False
+    
+    if 'd.grid' == dcmd[0]:
+        mapname = 'grid'
+    elif 'd.geodesic' in dcmd[0]:
+        mapname = 'geodesic'
+    elif 'd.rhumbline' in dcmd[0]:
+        mapname = 'rhumb'
+    elif 'labels=' in dcmd[0]:
+        mapname = dcmd[idx].split('=')[1] + ' labels'
+    else:
+        params = list()
+        for idx in range(len(dcmd)):
+            try:
+                p, v = dcmd[idx].split('=', 1)
+            except ValueError:
+                continue
+            
+            if p == param:
+                params = [(idx, p, v)]
+                break
+            
+            if p in ('map', 'input',
+                     'red', 'blue', 'green',
+                     'h_map', 's_map', 'i_map',
+                     'reliefmap', 'labels'):
+                params.append((idx, p, v))
+        
+        if len(params) < 1:
+            if len(dcmd) > 1 and '=' not in dcmd[1]:
+                task = gtask.parse_interface(dcmd[0])
+                p = task.get_options()['params'][0].get('name', '')
+                params.append((1, p, dcmd[1]))
+            else:
+                return mapname, False
+        
+        mapname = params[0][2]
+        mapset = ''
+        if fullyQualified and '@' not in mapname:
+            if layerType in ('raster', 'vector', '3d-raster', 'rgb', 'his'):
+                try:
+                    if layerType in ('raster', 'rgb', 'his'):
+                        findType = 'cell'
+                    else:
+                        findType = layerType
+                    mapset = grass.find_file(mapname, element = findType)['mapset']
+                except AttributeError, e: # not found
+                    return '', False
+                if not mapset:
+                    found = False
+            else:
+                mapset = grass.gisenv()['MAPSET']
+            
+            # update dcmd
+            for i, p, v in params:
+                if p:
+                    dcmd[i] = p + '=' + v
+                else:
+                    dcmd[i] = v
+                if mapset:
+                    dcmd[i] += '@' + mapset
+        
+        maps = list()
+        for i, p, v in params:
+            if not p:
+                maps.append(v)
+            else:
+                maps.append(dcmd[i].split('=', 1)[1])
+        mapname = '\n'.join(maps)
+    
+    return mapname, found
+
+def GetValidLayerName(name):
+    """!Make layer name SQL compliant, based on G_str_to_sql()
+    
+    @todo: Better use directly GRASS Python SWIG...
+    """
+    retName = str(name).strip()
+    
+    # check if name is fully qualified
+    if '@' in retName:
+        retName, mapset = retName.split('@')
+    else:
+        mapset = None
+    
+    cIdx = 0
+    retNameList = list(retName)
+    for c in retNameList:
+        if not (c >= 'A' and c <= 'Z') and \
+               not (c >= 'a' and c <= 'z') and \
+               not (c >= '0' and c <= '9'):
+            retNameList[cIdx] = '_'
+        cIdx += 1
+    retName = ''.join(retNameList)
+    
+    if not (retName[0] >= 'A' and retName[0] <= 'Z') and \
+           not (retName[0] >= 'a' and retName[0] <= 'z'):
+        retName = 'x' + retName[1:]
+
+    if mapset:
+        retName = retName + '@' + mapset
+        
+    return retName
+
+def ListOfCatsToRange(cats):
+    """!Convert list of category number to range(s)
+
+    Used for example for d.vect cats=[range]
+
+    @param cats category list
+
+    @return category range string
+    @return '' on error
+    """
+
+    catstr = ''
+
+    try:
+        cats = map(int, cats)
+    except:
+        return catstr
+
+    i = 0
+    while i < len(cats):
+        next = 0
+        j = i + 1
+        while j < len(cats):
+            if cats[i + next] == cats[j] - 1:
+                next += 1
+            else:
+                break
+            j += 1
+
+        if next > 1:
+            catstr += '%d-%d,' % (cats[i], cats[i + next])
+            i += next + 1
+        else:
+            catstr += '%d,' % (cats[i])
+            i += 1
+        
+    return catstr.strip(',')
+
+def ListOfMapsets(get = 'ordered'):
+    """!Get list of available/accessible mapsets
+
+    @param get method ('all', 'accessible', 'ordered')
+    
+    @return list of mapsets
+    @return None on error
+    """
+    mapsets = []
+    
+    if get == 'all' or get == 'ordered':
+        ret = RunCommand('g.mapsets',
+                         read = True,
+                         quiet = True,
+                         flags = 'l',
+                         fs = 'newline')
+        
+        if ret:
+            mapsets = ret.splitlines()
+            ListSortLower(mapsets)
+        else:
+            return None
+        
+    if get == 'accessible' or get == 'ordered':
+        ret = RunCommand('g.mapsets',
+                         read = True,
+                         quiet = True,
+                         flags = 'p',
+                         fs = 'newline')
+        if ret:
+            if get == 'accessible':
+                mapsets = ret.splitlines()
+            else:
+                mapsets_accessible = ret.splitlines()
+                for mapset in mapsets_accessible:
+                    mapsets.remove(mapset)
+                mapsets = mapsets_accessible + mapsets
+        else:
+            return None
+    
+    return mapsets
+
+def ListSortLower(list):
+    """!Sort list items (not case-sensitive)"""
+    list.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
+
+def GetVectorNumberOfLayers(vector, parent = None):
+    """!Get list of vector layers
+
+    @param vector name of vector map
+    @param parent parent window (to show dialog) or None
+    """
+    layers = []
+    if not vector:
+        return layers
+    
+    fullname = grass.find_file(name = vector, element = 'vector')['fullname']
+    if not fullname:
+        Debug.msg(5, "utils.GetVectorNumberOfLayers(): vector map '%s' not found" % vector)
+        return layers
+    
+    ret, out, msg = RunCommand('v.db.connect',
+                               getErrorMsg = True,
+                               read = True,
+                               flags = 'g',
+                               map = fullname,
+                               fs = ';')
+    if ret != 0:
+        sys.stderr.write(_("Vector map <%(map)s>: %(msg)s\n") % { 'map' : fullname, 'msg' : msg })
+        return layers
+    
+    Debug.msg(1, "GetVectorNumberOfLayers(): ret %s" % ret)
+    
+    for line in out.splitlines():
+        try:
+            layer = line.split(';')[0]
+            if '/' in layer:
+                layer = layer.split('/')[0]
+            layers.append(layer)
+        except IndexError:
+            pass
+    
+    Debug.msg(3, "utils.GetVectorNumberOfLayers(): vector=%s -> %s" % \
+                  (fullname, ','.join(layers)))
+    
+    return layers
+
+def Deg2DMS(lon, lat, string = True, hemisphere = True, precision = 3):
+    """!Convert deg value to dms string
+
+    @param lon longitude (x)
+    @param lat latitude (y)
+    @param string True to return string otherwise tuple
+    @param hemisphere print hemisphere
+    @param precision seconds precision
+    
+    @return DMS string or tuple of values
+    @return empty string on error
+    """
+    try:
+        flat = float(lat)
+        flon = float(lon)
+    except ValueError:
+        if string:
+            return ''
+        else:
+            return None
+
+    # fix longitude
+    while flon > 180.0:
+        flon -= 360.0
+    while flon < -180.0:
+        flon += 360.0
+
+    # hemisphere
+    if hemisphere:
+        if flat < 0.0:
+            flat = abs(flat)
+            hlat = 'S'
+        else:
+            hlat = 'N'
+
+        if flon < 0.0:
+            hlon = 'W'
+            flon = abs(flon)
+        else:
+            hlon = 'E'
+    else:
+        flat = abs(flat)
+        flon = abs(flon)
+        hlon = ''
+        hlat = ''
+    
+    slat = __ll_parts(flat, precision = precision)
+    slon = __ll_parts(flon, precision = precision)
+
+    if string:
+        return slon + hlon + '; ' + slat + hlat
+    
+    return (slon + hlon, slat + hlat)
+
+def DMS2Deg(lon, lat):
+    """!Convert dms value to deg
+
+    @param lon longitude (x)
+    @param lat latitude (y)
+    
+    @return tuple of converted values
+    @return ValueError on error
+    """
+    x = __ll_parts(lon, reverse = True)
+    y = __ll_parts(lat, reverse = True)
+    
+    return (x, y)
+
+def __ll_parts(value, reverse = False, precision = 3):
+    """!Converts deg to d:m:s string
+
+    @param value value to be converted
+    @param reverse True to convert from d:m:s to deg
+    @param precision seconds precision (ignored if reverse is True)
+    
+    @return converted value (string/float)
+    @return ValueError on error (reverse == True)
+    """
+    if not reverse:
+        if value == 0.0:
+            return '%s%.*f' % ('00:00:0', precision, 0.0)
+    
+        d = int(int(value))
+        m = int((value - d) * 60)
+        s = ((value - d) * 60 - m) * 60
+        if m < 0:
+            m = '00'
+        elif m < 10:
+            m = '0' + str(m)
+        else:
+            m = str(m)
+        if s < 0:
+            s = '00.0000'
+        elif s < 10.0:
+            s = '0%.*f' % (precision, s)
+        else:
+            s = '%.*f' % (precision, s)
+        
+        return str(d) + ':' + m + ':' + s
+    else: # -> reverse
+        try:
+            d, m, s = value.split(':')
+            hs = s[-1]
+            s = s[:-1]
+        except ValueError:
+            try:
+                d, m = value.split(':')
+                hs = m[-1]
+                m = m[:-1]
+                s = '0.0'
+            except ValueError:
+                try:
+                    d = value
+                    hs = d[-1]
+                    d = d[:-1]
+                    m = '0'
+                    s = '0.0'
+                except ValueError:
+                    raise ValueError
+        
+        if hs not in ('N', 'S', 'E', 'W'):
+            raise ValueError
+        
+        coef = 1.0
+        if hs in ('S', 'W'):
+            coef = -1.0
+        
+        fm = int(m) / 60.0
+        fs = float(s) / (60 * 60)
+        
+        return coef * (float(d) + fm + fs)
+    
+def GetCmdString(cmd):
+    """
+    Get GRASS command as string.
+    
+    @param cmd GRASS command given as dictionary
+    
+    @return command string
+    """
+    scmd = ''
+    if not cmd:
+        return scmd
+    
+    scmd = cmd[0]
+    
+    if 'flags' in cmd[1]:
+        for flag in cmd[1]['flags']:
+            scmd += ' -' + flag
+    for flag in ('verbose', 'quiet', 'overwrite'):
+        if flag in cmd[1] and cmd[1][flag] is True:
+            scmd += ' --' + flag
+    
+    for k, v in cmd[1].iteritems():
+        if k in ('flags', 'verbose', 'quiet', 'overwrite'):
+            continue
+        scmd += ' %s=%s' % (k, v)
+            
+    return scmd
+
+def CmdToTuple(cmd):
+    """!Convert command list to tuple for gcmd.RunCommand()"""
+    if len(cmd) < 1:
+        return None
+        
+    dcmd = {}
+    for item in cmd[1:]:
+        if '=' in item: # params
+            key, value = item.split('=', 1)
+            dcmd[str(key)] = str(value)
+        elif item[:2] == '--': # long flags
+            flag = item[2:]
+            if flag in ('verbose', 'quiet', 'overwrite'):
+                dcmd[str(flag)] = True
+        else: # -> flags
+            if 'flags' not in dcmd:
+                dcmd['flags'] = ''
+            dcmd['flags'] += item.replace('-', '')
+                
+    return (cmd[0],
+            dcmd)
+
+def PathJoin(*args):
+    """!Check path created by os.path.join"""
+    path = os.path.join(*args)
+    if platform.system() == 'Windows' and \
+            '/' in path:
+        return path[1].upper() + ':\\' + path[3:].replace('/', '\\')
+    
+    return path
+
+def ReadEpsgCodes(path):
+    """!Read EPSG code from the file
+
+    @param path full path to the file with EPSG codes
+
+    @return dictionary of EPSG code
+    @return string on error
+    """
+    epsgCodeDict = dict()
+    try:
+        try:
+            f = open(path, "r")
+        except IOError:
+            return _("failed to open '%s'" % path)
+        
+        i = 0
+        code = None
+        for line in f.readlines():
+            line = line.strip()
+            if len(line) < 1:
+                continue
+                
+            if line[0] == '#':
+                descr = line[1:].strip()
+            elif line[0] == '<':
+                code, params = line.split(" ", 1)
+                try:
+                    code = int(code.replace('<', '').replace('>', ''))
+                except ValueError:
+                    return e
+            
+            if code is not None:
+                epsgCodeDict[code] = (descr, params)
+                code = None
+            i += 1
+        
+        f.close()
+    except StandardError, e:
+        return e
+    
+    return epsgCodeDict
+
+def ReprojectCoordinates(coord, projOut, projIn = None, flags = ''):
+    """!Reproject coordinates
+
+    @param coord coordinates given as tuple
+    @param projOut output projection
+    @param projIn input projection (use location projection settings)
+
+    @return reprojected coordinates (returned as tuple)
+    """
+    if not projIn:
+        projIn = RunCommand('g.proj',
+                            flags = 'jf',
+                            read = True)
+    coors = RunCommand('m.proj',
+                       flags = flags,
+                       proj_in = projIn,
+                       proj_out = projOut,
+                       stdin = '%f|%f' % (coord[0], coord[1]),
+                       read = True)
+    if coors:
+        coors = coors.split('\t')
+        e = coors[0]
+        n = coors[1].split(' ')[0].strip()
+        try:
+            proj = projOut.split(' ')[0].split('=')[1]
+        except IndexError:
+            proj = ''
+        if proj in ('ll', 'latlong', 'longlat') and 'd' not in flags:
+            return (proj, (e, n))
+        else:
+            try:
+                return (proj, (float(e), float(n)))
+            except ValueError:
+                return (None, None)
+    
+    return (None, None)
+
+def GetListOfLocations(dbase):
+    """!Get list of GRASS locations in given dbase
+
+    @param dbase GRASS database path
+
+    @return list of locations (sorted)
+    """
+    listOfLocations = list()
+
+    try:
+        for location in glob.glob(os.path.join(dbase, "*")):
+            try:
+                if os.path.join(location, "PERMANENT") in glob.glob(os.path.join(location, "*")):
+                    listOfLocations.append(os.path.basename(location))
+            except:
+                pass
+    except UnicodeEncodeError, e:
+        raise e
+    
+    ListSortLower(listOfLocations)
+    
+    return listOfLocations
+
+def GetListOfMapsets(dbase, location, selectable = False):
+    """!Get list of mapsets in given GRASS location
+
+    @param dbase      GRASS database path
+    @param location   GRASS location
+    @param selectable True to get list of selectable mapsets, otherwise all
+
+    @return list of mapsets - sorted (PERMANENT first)
+    """
+    listOfMapsets = list()
+    
+    if selectable:
+        ret = RunCommand('g.mapset',
+                         read = True,
+                         flags = 'l',
+                         location = location,
+                         gisdbase = dbase)
+        
+        if not ret:
+            return listOfMapsets
+        
+        for line in ret.rstrip().splitlines():
+            listOfMapsets += line.split(' ')
+    else:
+        for mapset in glob.glob(os.path.join(dbase, location, "*")):
+            if os.path.isdir(mapset) and \
+                    os.path.isfile(os.path.join(dbase, location, mapset, "WIND")):
+                listOfMapsets.append(os.path.basename(mapset))
+    
+    ListSortLower(listOfMapsets)
+    return listOfMapsets
+
+def GetColorTables():
+    """!Get list of color tables"""
+    ret = RunCommand('r.colors',
+                     read = True,
+                     flags = 'l')
+    if not ret:
+        return list()
+    
+    return ret.splitlines()
+
+def DecodeString(string):
+    """!Decode string using system encoding
+    
+    @param string string to be decoded
+    
+    @return decoded string
+    """
+    if not string:
+        return string
+    
+    enc = locale.getdefaultlocale()[1]
+    if enc:
+        Debug.msg(5, "DecodeString(): enc=%s" % enc)
+        return string.decode(enc)
+    
+    return string
+
+def EncodeString(string):
+    """!Return encoded string using system locales
+    
+    @param string string to be encoded
+    
+    @return encoded string
+    """
+    if not string:
+        return string
+    enc = locale.getdefaultlocale()[1]
+    if enc:
+        Debug.msg(5, "EncodeString(): enc=%s" % enc)
+        return string.encode(enc)
+    
+    return string
+
+def _getGDALFormats():
+    """!Get dictionary of avaialble GDAL drivers"""
+    ret = grass.read_command('r.in.gdal',
+                             quiet = True,
+                             flags = 'f')
+    
+    return _parseFormats(ret)
+
+def _getOGRFormats():
+    """!Get dictionary of avaialble OGR drivers"""
+    ret = grass.read_command('v.in.ogr',
+                             quiet = True,
+                             flags = 'f')
+    
+    return _parseFormats(ret)
+
+def _parseFormats(output):
+    """!Parse r.in.gdal/v.in.ogr -f output"""
+    formats = { 'file'     : list(),
+                'database' : list(),
+                'protocol' : list()
+                }
+    
+    if not output:
+        return formats
+    
+    for line in output.splitlines():
+        format = line.strip().rsplit(':', -1)[1].strip()
+        if format in ('Memory', 'Virtual Raster', 'In Memory Raster'):
+            continue
+        if format in ('PostgreSQL', 'SQLite',
+                      'ODBC', 'ESRI Personal GeoDatabase',
+                      'Rasterlite',
+                      'PostGIS WKT Raster driver'):
+            formats['database'].append(format)
+        elif format in ('GeoJSON',
+                        'OGC Web Coverage Service',
+                        'OGC Web Map Service',
+                        'HTTP Fetching Wrapper'):
+            formats['protocol'].append(format)
+        else:
+            formats['file'].append(format)
+    
+    for items in formats.itervalues():
+        items.sort()
+    
+    return formats
+
+formats = None
+
+def GetFormats():
+    """!Get GDAL/OGR formats"""
+    global formats
+    if not formats:
+        formats = {
+            'gdal' : _getGDALFormats(),
+            'ogr' : _getOGRFormats()
+            }
+    
+    return formats
+
+def GetSettingsPath():
+    """!Get full path to the settings directory
+    """
+    try:
+        verFd = open(os.path.join(ETCDIR, "VERSIONNUMBER"))
+        version = int(verFd.readlines()[0].split(' ')[0].split('.')[0])
+    except (IOError, ValueError, TypeError, IndexError), e:
+        sys.exit(_("ERROR: Unable to determine GRASS version. Details: %s") % e)
+    
+    verFd.close()
+    
+    # keep location of settings files rc and wx in sync with
+    # lib/init/init.sh and init.bat
+    if sys.platform == 'win32':
+        return os.path.join(os.getenv('APPDATA'), 'grass%d' % version)
+    
+    return os.path.join(os.getenv('HOME'), '.grass%d' % version)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/core/workspace.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/core/workspace.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/core/workspace.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1267 @@
+"""!
+ at package core.workspace
+
+ at brief Open/save workspace definition file
+
+Classes:
+ - workspace::ProcessWorkspaceFile
+ - workspace::WriteWorkspaceFile
+ - workspace::ProcessGrcFile
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+
+import wx
+
+from core.utils     import normalize_whitespace
+from core.settings  import UserSettings
+from nviz.main      import NvizSettings
+
+class ProcessWorkspaceFile:
+    def __init__(self, tree):
+        """!A ElementTree handler for the GXW XML file, as defined in
+        grass-gxw.dtd.
+        """
+        self.tree = tree
+        self.root = self.tree.getroot()
+        
+        #
+        # layer manager properties
+        #
+        self.layerManager = {}
+        self.layerManager['pos']  = None # window position
+        self.layerManager['size'] = None # window size
+        
+        #
+        # list of mapdisplays
+        #
+        self.displays = []
+        #
+        # list of map layers
+        #
+        self.layers = []
+        #
+        # nviz state
+        #
+        self.nviz_state = {}
+        
+        self.displayIndex = -1 # first display has index '0'
+        
+        self.__processFile()
+
+        if NvizSettings:
+            self.nvizDefault = NvizSettings()
+        else:
+            self.nvizDefault = None
+        
+    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:
+            return normalize_whitespace(p.text)
+        
+        return default
+    
+    def __processFile(self):
+        """!Process workspace file"""
+        #
+        # layer manager
+        #
+        node_lm = self.root.find('layer_manager')
+        if node_lm is not None:
+            posAttr = node_lm.get('dim', '')
+            if posAttr:
+                posVal = map(int, posAttr.split(','))
+                try:
+                    self.layerManager['pos']  = (posVal[0], posVal[1])
+                    self.layerManager['size'] = (posVal[2], posVal[3])
+                except:
+                    pass
+        
+        #
+        # displays
+        #
+        for display in self.root.findall('display'):
+            self.displayIndex += 1
+            
+            # window position and size
+            posAttr = display.get('dim', '')
+            if posAttr:
+                posVal = map(int, posAttr.split(','))
+                try:
+                    pos  = (posVal[0], posVal[1])
+                    size = (posVal[2], posVal[3])
+                except:
+                    pos  = None
+                    size = None
+            else:
+                pos  = None
+                size = None
+            
+            extentAttr = display.get('extent', '')
+            if extentAttr:
+                # w, s, e, n
+                extent = map(float, extentAttr.split(','))
+            else:
+                extent = None
+            
+            # projection
+            node_projection = display.find('projection')
+            if node_projection is not None:
+                projection = { 'enabled' : True,
+                               'epsg' : node_projection.get('epsg', ''),
+                               'proj' : self.__getNodeText(node_projection, 'value') }
+            else:
+                projection = { 'enabled' : False }
+            
+            self.displays.append( {
+                    "name"           : display.get('name'),
+                    "render"         : bool(int(display.get('render', "0"))),
+                    "mode"           : int(display.get('mode', 0)),
+                    "showCompExtent" : bool(int(display.get('showCompExtent', "0"))),
+                    "pos"            : pos,
+                    "size"           : size,
+                    "extent"         : extent,
+                    "alignExtent"    : bool(int(display.get('alignExtent', "0"))),
+                    "constrainRes"   : bool(int(display.get('constrainRes', "0"))),
+                    "projection"     : projection,
+                    "viewMode"       : display.get('viewMode', '2d')} )
+            
+            # process all layers/groups in the display
+            self.__processLayers(display)
+            # process nviz_state
+            self.__processNvizState(display)
+
+    def __processLayers(self, node, inGroup = -1):
+        """!Process layers/groups of selected display
+        
+        @param node display tree node
+        @param inGroup in group -> index of group item otherwise -1
+        """
+        for item in node.getchildren():
+            if item.tag == 'group':
+                # -> group
+                self.layers.append( {
+                        "type"    : 'group',
+                        "name"    : item.get('name', ''),
+                        "checked" : bool(int(item.get('checked', "0"))),
+                        "opacity" : None,
+                        "cmd"     : None,
+                        "group"   : inGroup,
+                        "display" : self.displayIndex,
+                        "vdigit"  : None,
+                        "nviz"    : None})
+                
+                self.__processLayers(item, inGroup = len(self.layers) - 1) # process items in group
+                
+            elif item.tag == 'layer':
+                cmd, selected, vdigit, nviz = self.__processLayer(item)
+                lname = item.get('name', None)
+                if lname and '\\n' in lname:
+                    lname = lname.replace('\\n', os.linesep)
+                
+                self.layers.append( {
+                        "type"     : item.get('type', None),
+                        "name"     : lname,
+                        "checked"  : bool(int(item.get('checked', "0"))),
+                        "opacity"  : float(item.get('opacity', '1.0')),
+                        "cmd"      : cmd,
+                        "group"    : inGroup,
+                        "display"  : self.displayIndex,
+                        "selected" : selected,
+                        "vdigit"   : vdigit,
+                        "nviz"     : nviz } )
+            
+    def __processLayer(self, layer):
+        """!Process layer item
+
+        @param layer tree node
+        """
+        cmd = list()
+        
+        #
+        # layer attributes (task) - 2D settings
+        #
+        node_task = layer.find('task')
+        cmd.append(node_task.get('name', "unknown"))
+        
+        # flags
+        for p in node_task.findall('flag'):
+            flag = p.get('name', '')
+            if len(flag) > 1:
+                cmd.append('--' + flag)
+            else:
+                cmd.append('-' + flag)
+        
+        # parameters
+        for p in node_task.findall('parameter'):
+            cmd.append('%s=%s' % (p.get('name', ''),
+                                  self.__filterValue(self.__getNodeText(p, 'value'))))
+        
+        if layer.find('selected') is not None:
+            selected = True
+        else:
+            selected = False
+        
+        #
+        # Vector digitizer settings
+        #
+        node_vdigit = layer.find('vdigit')
+        if node_vdigit is not None:
+            vdigit = self.__processLayerVdigit(node_vdigit)
+        else:
+            vdigit = None
+        
+        #
+        # Nviz (3D settings)
+        #
+        node_nviz = layer.find('nviz')
+        if node_nviz is not None:
+            nviz = self.__processLayerNviz(node_nviz)
+        else:
+            nviz = None
+        
+        return (cmd, selected, vdigit, nviz)
+
+    def __processLayerVdigit(self, node_vdigit):
+        """!Process vector digitizer layer settings
+
+        @param node_vdigit vdigit node
+        """
+        # init nviz layer properties
+        vdigit = dict()
+        for node in node_vdigit.findall('geometryAttribute'):
+            if 'geomAttr' not in vdigit:
+                vdigit['geomAttr'] = dict()
+            type = node.get('type')
+            vdigit['geomAttr'][type] = dict()
+            vdigit['geomAttr'][type]['column'] = node.get('column') # required
+            # default map units
+            vdigit['geomAttr'][type]['units'] = node.get('units', 'mu')
+        
+        return vdigit
+    
+    def __processLayerNviz(self, node_nviz):
+        """!Process 3D layer settings
+
+        @param node_nviz nviz node
+        """
+        # init nviz layer properties
+        nviz = {}
+        if node_nviz.find('surface') is not None: # -> raster
+            nviz['surface'] = {}
+            for sec in ('attribute', 'draw', 'mask', 'position'):
+                nviz['surface'][sec] = {}
+        elif node_nviz.find('vlines') is not None or \
+                node_nviz.find('vpoints') is not None: # -> vector
+            nviz['vector'] = {}
+            for sec in ('lines', 'points'):
+                nviz['vector'][sec] = {}
+        
+        if 'surface' in nviz:
+            node_surface = node_nviz.find('surface')
+            # attributes
+            for attrb in node_surface.findall('attribute'):
+                tagName = str(attrb.tag)
+                attrbName = attrb.get('name', '')
+                dc = nviz['surface'][tagName][attrbName] = {}
+                if attrb.get('map', '0') == '0':
+                    dc['map'] = False
+                else:
+                    dc['map'] = True
+                value = self.__getNodeText(attrb, 'value')
+                try:
+                    dc['value'] = int(value)
+                except ValueError:
+                    try:
+                        dc['value'] = float(value)
+                    except ValueError:
+                        dc['value'] = str(value)
+            
+            # draw
+            node_draw = node_surface.find('draw')
+            if node_draw is not None:
+                tagName = str(node_draw.tag)
+                nviz['surface'][tagName]['all'] = False
+                nviz['surface'][tagName]['mode'] = {}
+                nviz['surface'][tagName]['mode']['value'] = -1 # to be calculated
+                nviz['surface'][tagName]['mode']['desc'] = {}
+                nviz['surface'][tagName]['mode']['desc']['shading'] = \
+                    str(node_draw.get('shading', ''))
+                nviz['surface'][tagName]['mode']['desc']['style'] = \
+                    str(node_draw.get('style', ''))
+                nviz['surface'][tagName]['mode']['desc']['mode'] = \
+                    str(node_draw.get('mode', ''))
+                
+                # resolution
+                for node_res in node_draw.findall('resolution'):
+                    resType = str(node_res.get('type', ''))
+                    if 'resolution' not in nviz['surface']['draw']:
+                        nviz['surface']['draw']['resolution'] = {}
+                    value = int(self.__getNodeText(node_res, 'value'))
+                    nviz['surface']['draw']['resolution'][resType] = value
+                
+                # wire-color
+                node_wire_color = node_draw.find('wire_color')
+                if node_wire_color is not None:
+                    nviz['surface']['draw']['wire-color'] = {}
+                    value = str(self.__getNodeText(node_wire_color, 'value'))
+                    nviz['surface']['draw']['wire-color']['value'] = value
+                
+            # position
+            node_pos = node_surface.find('position')
+            if node_pos is not None:
+                dc = nviz['surface']['position'] = {}
+                for coor in ['x', 'y', 'z']:
+                    node = node_pos.find(coor)
+                    if node is None:
+                        continue
+                    value = int(self.__getNodeText(node_pos, coor))
+                    dc[coor] = value
+            
+        elif 'vector' in nviz:
+            # vpoints
+            node_vpoints = node_nviz.find('vpoints')
+            if node_vpoints is not None:
+                marker = str(node_vpoints.get('marker', ''))
+                markerId = list(UserSettings.Get(group='nviz', key='vector',
+                                                 subkey=['points', 'marker'], internal=True)).index(marker)
+                nviz['vector']['points']['marker'] = { 'value' : markerId }
+                
+                node_mode = node_vpoints.find('mode')
+                if node_mode is not None:
+                    nviz['vector']['points']['mode'] = {}
+                    nviz['vector']['points']['mode']['type'] = str(node_mode.get('type', 'surface'))
+                    nviz['vector']['points']['mode']['surface'] = {}
+                    nviz['vector']['points']['mode']['surface']['value'] = []
+                    nviz['vector']['points']['mode']['surface']['show'] = []
+                    
+                    # map
+                    for node_map in node_mode.findall('map'):
+                        nviz['vector']['points']['mode']['surface']['value'].append(
+                            self.__processLayerNvizNode(node_map, 'name', str))
+                        nviz['vector']['points']['mode']['surface']['show'].append(bool(
+                            self.__processLayerNvizNode(node_map, 'checked', int)))
+                
+                # color
+                self.__processLayerNvizNode(node_vpoints, 'color', str,
+                                            nviz['vector']['points'])
+                
+                # width
+                self.__processLayerNvizNode(node_vpoints, 'width', int,
+                                            nviz['vector']['points'])
+                
+                # height
+                self.__processLayerNvizNode(node_vpoints, 'height', int,
+                                            nviz['vector']['points'])
+                
+                # height
+                self.__processLayerNvizNode(node_vpoints, 'size', int,
+                                            nviz['vector']['points'])
+                
+            # vlines
+            node_vlines = node_nviz.find('vlines')
+            if node_vlines is not None:
+                node_mode = node_vlines.find('mode')
+                if node_mode is not None:
+                    nviz['vector']['lines']['mode'] = {}
+                    nviz['vector']['lines']['mode']['type'] = str(node_mode.get('type', ''))
+                    nviz['vector']['lines']['mode']['surface'] = {}
+                    nviz['vector']['lines']['mode']['surface']['value'] = []
+                    nviz['vector']['lines']['mode']['surface']['show'] = []
+                    
+                    # map
+                    for node_map in node_mode.findall('map'):
+                        nviz['vector']['lines']['mode']['surface']['value'].append(
+                            self.__processLayerNvizNode(node_map, 'name', str))
+                        nviz['vector']['lines']['mode']['surface']['show'].append(bool(
+                            self.__processLayerNvizNode(node_map, 'checked', int)))
+                
+                # color
+                self.__processLayerNvizNode(node_vlines, 'color', str,
+                                            nviz['vector']['lines'])
+                
+                # width
+                self.__processLayerNvizNode(node_vlines, 'width', int,
+                                            nviz['vector']['lines'])
+                
+                # height
+                self.__processLayerNvizNode(node_vlines, 'height', int,
+                                            nviz['vector']['lines'])
+                
+        return nviz
+    
+    def __processLayerNvizNode(self, node, tag, cast, dc = None):
+        """!Process given tag nviz/vector"""
+        node_tag = node.find(tag)
+        if node_tag is not None:
+            if node_tag.find('value') is not None:
+                value = cast(self.__getNodeText(node_tag, 'value'))
+            else:
+                try:
+                    value = cast(node_tag.text)
+                except ValueError:
+                    if cast == str:
+                        value = ''
+                    else:
+                        value = None
+            if dc:
+                dc[tag] = dict()
+                dc[tag]['value'] = value
+            else:
+                return value
+    
+    def __processNvizState(self, node):
+        """!Process tag nviz_state"""
+        node_state = node.find('nviz_state')
+        if node_state is None:
+            return
+        self.nviz_state['display'] = self.displayIndex
+        #
+        # view
+        #
+        node_view = node_state.find('view')
+        view = {}
+        iview = {}
+        
+        node_position = node_view.find('v_position')
+        view['position'] = {}
+        view['position']['x'] = self.__processLayerNvizNode(node_position, 'x', float)
+        view['position']['y'] = self.__processLayerNvizNode(node_position, 'y', float)
+        node_persp = node_view.find('persp')
+        view['persp'] = {}
+        iview['persp'] = {}
+        view['persp']['value'] = self.__processLayerNvizNode(node_persp, 'value', int)
+        view['persp']['step'] = self.__processLayerNvizNode(node_persp, 'step', int)
+        iview['persp']['min'] = self.__processLayerNvizNode(node_persp, 'min', int)
+        iview['persp']['max'] = self.__processLayerNvizNode(node_persp, 'max', int)
+        node_height = node_view.find('v_height')
+        iview['height'] = {}
+        iview['height']['value'] = self.__processLayerNvizNode(node_height, 'value', int)
+        iview['height']['min'] = self.__processLayerNvizNode(node_height, 'min', int)
+        iview['height']['max'] = self.__processLayerNvizNode(node_height, 'max', int)
+        node_twist = node_view.find('twist')
+        view['twist'] = {}
+        iview['twist'] = {}
+        view['twist']['value'] = self.__processLayerNvizNode(node_twist, 'value', int)
+        iview['twist']['min'] = self.__processLayerNvizNode(node_twist, 'min', int)
+        iview['twist']['max'] = self.__processLayerNvizNode(node_twist, 'max', int)
+        node_zexag = node_view.find('z-exag')
+        view['z-exag'] = {}
+        iview['z-exag'] = {}
+        view['z-exag']['value'] = self.__processLayerNvizNode(node_zexag, 'value', float)
+        view['z-exag']['min'] = self.__processLayerNvizNode(node_zexag, 'min', int)
+        view['z-exag']['max'] = self.__processLayerNvizNode(node_zexag, 'max', int)
+        iview['z-exag']['original'] = self.__processLayerNvizNode(node_zexag, 'original', float)
+        node_focus = node_view.find('focus')
+        iview['focus'] = {}
+        iview['focus']['x'] = self.__processLayerNvizNode(node_focus, 'x', int)
+        iview['focus']['y'] = self.__processLayerNvizNode(node_focus, 'y', int)
+        iview['focus']['z'] = self.__processLayerNvizNode(node_focus, 'z', int)
+        node_dir = node_view.find('dir')
+        if node_dir:
+            iview['dir'] = {}
+            iview['dir']['x'] = self.__processLayerNvizNode(node_dir, 'x', int)
+            iview['dir']['y'] = self.__processLayerNvizNode(node_dir, 'y', int)
+            iview['dir']['z'] = self.__processLayerNvizNode(node_dir, 'z', int)
+            iview['dir']['use'] = True
+        else:
+            iview['dir'] = {}
+            iview['dir']['x'] = -1
+            iview['dir']['y'] = -1
+            iview['dir']['z'] = -1
+            iview['dir']['use'] = False
+        
+        view['background'] = {}
+        color = self.__processLayerNvizNode(node_view, 'background_color', str)
+        view['background']['color'] = tuple(map(int, color.split(':')))
+        
+        self.nviz_state['view'] = view
+        self.nviz_state['iview'] = iview
+        #
+        # light
+        #
+        node_light = node_state.find('light')
+        light = {}
+        
+        node_position = node_light.find('l_position')
+        light['position'] = {}
+        light['position']['x'] = self.__processLayerNvizNode(node_position, 'x', float)
+        light['position']['y'] = self.__processLayerNvizNode(node_position, 'y', float)
+        light['position']['z'] = self.__processLayerNvizNode(node_position, 'z', int)
+        
+        light['bright'] = self.__processLayerNvizNode(node_light, 'bright', int) 
+        light['ambient'] = self.__processLayerNvizNode(node_light, 'ambient', int)
+        color = self.__processLayerNvizNode(node_light, 'color', str)
+        light['color'] = tuple(map(int, color.split(':')))
+        
+        self.nviz_state['light'] = light
+        
+        node_constants = node_state.find('constant_planes')
+        constants = []
+        if node_constants:
+            for i, node_plane in enumerate(node_constants.findall('plane')):
+                plane = {}
+                plane['color'] = self.__processLayerNvizNode(node_plane, 'color', str)                
+                plane['resolution'] = self.__processLayerNvizNode(node_plane, 'fine_resolution', int)
+                plane['value'] = self.__processLayerNvizNode(node_plane, 'height', int)
+                plane['object'] = {}
+                constants.append({'constant': plane})
+        self.nviz_state['constants'] = constants
+
+class WriteWorkspaceFile(object):
+    """!Generic class for writing workspace file"""
+    def __init__(self, lmgr, file):
+        self.file =  file
+        self.lmgr = lmgr
+        self.indent = 0
+        
+        # write header
+        self.file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
+        self.file.write('<!DOCTYPE gxw SYSTEM "grass-gxw.dtd">\n')
+        self.file.write('%s<gxw>\n' % (' ' * self.indent))
+        
+        self.indent =+ 4
+        
+        # layer manager
+        windowPos = self.lmgr.GetPosition()
+        windowSize = self.lmgr.GetSize()
+        file.write('%s<layer_manager dim="%d,%d,%d,%d">\n' % (' ' * self.indent,
+                                                              windowPos[0],
+                                                              windowPos[1],
+                                                              windowSize[0],
+                                                              windowSize[1]
+                                                              ))
+        
+        file.write('%s</layer_manager>\n' % (' ' * self.indent))
+        
+        # list of displays
+        for page in range(0, self.lmgr.gm_cb.GetPageCount()):
+            dispName = self.lmgr.gm_cb.GetPageText(page)
+            mapTree = self.lmgr.gm_cb.GetPage(page).maptree
+            region = mapTree.Map.region
+            
+            displayPos = mapTree.mapdisplay.GetPosition()
+            displaySize = mapTree.mapdisplay.GetSize()
+            if mapTree.mapdisplay.toolbars['map'].combo.GetSelection() == 1:
+                viewmode = '3d'
+            else:
+                viewmode = '2d'
+            
+            file.write('%s<display '
+                       'name="%s" render="%d" '
+                       'mode="%d" showCompExtent="%d" '
+                       'alignExtent="%d" '
+                       'constrainRes="%d" '
+                       'dim="%d,%d,%d,%d" '
+                       'extent="%f,%f,%f,%f" '
+                       'viewMode="%s" >\n' % (' ' * self.indent,
+                                              dispName.encode('utf8'),
+                                              int(mapTree.mapdisplay.GetProperty('render')),
+                                              mapTree.mapdisplay.statusbarManager.GetMode(),
+                                              int(mapTree.mapdisplay.GetProperty('region')),
+                                              int(mapTree.mapdisplay.GetProperty('alignExtent')),
+                                              int(mapTree.mapdisplay.GetProperty('resolution')),
+                                              displayPos[0],
+                                              displayPos[1],
+                                              displaySize[0],
+                                              displaySize[1],
+                                              region['w'],
+                                              region['s'],
+                                              region['e'],
+                                              region['n'],
+                                              viewmode
+                                              ))
+            # projection statusbar info
+            if mapTree.mapdisplay.GetProperty('projection') and \
+                    UserSettings.Get(group='display', key='projection', subkey='proj4'):
+                self.indent += 4
+                file.write('%s<projection' % (' ' * self.indent))
+                epsg = UserSettings.Get(group='display', key='projection', subkey='epsg')
+                if epsg:
+                    file.write(' epsg="%s"' % epsg)
+                file.write('>\n')
+                proj = UserSettings.Get(group='display', key='projection', subkey='proj4')
+                self.indent += 4 
+                file.write('%s<value>%s</value>\n' % (' ' * self.indent, proj))
+                self.indent -= 4
+                file.write('%s</projection>\n' % (' ' * self.indent))
+                self.indent -= 4
+            
+            # list of layers
+            item = mapTree.GetFirstChild(mapTree.root)[0]
+            self.__writeLayer(mapTree, item)
+            
+            if mapTree.mapdisplay.MapWindow3D is not None:
+                nvizDisp = mapTree.mapdisplay.MapWindow3D
+                self.__writeNvizState(view = nvizDisp.view, iview =  nvizDisp.iview, 
+                                      light = nvizDisp.light, constants = nvizDisp.constants)
+            
+            file.write('%s</display>\n' % (' ' * self.indent))
+        
+        self.indent =- 4
+        file.write('%s</gxw>\n' % (' ' * self.indent))
+
+    def __filterValue(self, value):
+        """!Make value XML-valid"""
+        value = value.replace('<', '&lt;')
+        value = value.replace('>', '&gt;')
+        
+        return value
+    
+    def __writeLayer(self, mapTree, item):
+        """!Write bunch of layers to GRASS Workspace XML file"""
+        self.indent += 4
+        itemSelected = mapTree.GetSelections()
+        while item and item.IsOk():
+            type = mapTree.GetPyData(item)[0]['type']
+            if type != 'group':
+                maplayer = mapTree.GetPyData(item)[0]['maplayer']
+            else:
+                maplayer = None
+
+            checked = int(item.IsChecked())
+            if type == 'command':
+                cmd = mapTree.GetPyData(item)[0]['maplayer'].GetCmd(string=True)
+                self.file.write('%s<layer type="%s" name="%s" checked="%d">\n' % \
+                               (' ' * self.indent, type, cmd, checked));
+                self.file.write('%s</layer>\n' % (' ' * self.indent));
+            elif type == 'group':
+                name = mapTree.GetItemText(item)
+                self.file.write('%s<group name="%s" checked="%d">\n' % \
+                               (' ' * self.indent, name.encode('utf8'), checked));
+                self.indent += 4
+                subItem = mapTree.GetFirstChild(item)[0]
+                self.__writeLayer(mapTree, subItem)
+                self.indent -= 4
+                self.file.write('%s</group>\n' % (' ' * self.indent));
+            else:
+                cmd = mapTree.GetPyData(item)[0]['maplayer'].GetCmd(string = False)
+                name = mapTree.GetItemText(item).replace(os.linesep, '\\n')
+                opacity = maplayer.GetOpacity(float = True)
+                # remove 'opacity' part
+                if opacity < 1:
+                    name = name.split('(', -1)[0].strip()
+                self.file.write('%s<layer type="%s" name="%s" checked="%d" opacity="%f">\n' % \
+                                    (' ' * self.indent, type, name.encode('utf8'), checked, opacity));
+                
+                self.indent += 4
+                # selected ?
+                if item in itemSelected:
+                    self.file.write('%s<selected />\n' % (' ' * self.indent))
+                # layer properties
+                self.file.write('%s<task name="%s">\n' % (' ' * self.indent, cmd[0]))
+                self.indent += 4
+                for key, val in cmd[1].iteritems():
+                    if key == 'flags':
+                        for f in val:
+                            self.file.write('%s<flag name="%s" />\n' %
+                                            (' ' * self.indent, f))
+                    elif val in (True, False):
+                        self.file.write('%s<flag name="%s" />\n' %
+                                        (' ' * self.indent, key))
+                    else: # parameter
+                        self.file.write('%s<parameter name="%s">\n' %
+                                        (' ' * self.indent, key))
+                        self.indent += 4
+                        self.file.write('%s<value>%s</value>\n' %
+                                        (' ' * self.indent, self.__filterValue(val)))
+                        self.indent -= 4
+                        self.file.write('%s</parameter>\n' % (' ' * self.indent));
+                self.indent -= 4
+                self.file.write('%s</task>\n' % (' ' * self.indent));
+                # vector digitizer
+                vdigit = mapTree.GetPyData(item)[0]['vdigit']
+                if vdigit:
+                    self.file.write('%s<vdigit>\n' % (' ' * self.indent))
+                    if 'geomAttr' in vdigit:
+                        self.indent += 4
+                        for type, val in vdigit['geomAttr'].iteritems():
+                            units = ''
+                            if val['units'] != 'mu':
+                                units = ' units="%s"' % val['units']
+                            self.file.write('%s<geometryAttribute type="%s" column="%s"%s />\n' % \
+                                                (' ' * self.indent, type, val['column'], units))
+                        self.indent -= 4
+                    self.file.write('%s</vdigit>\n' % (' ' * self.indent))
+                # nviz
+                nviz = mapTree.GetPyData(item)[0]['nviz']
+                if nviz:
+                    self.file.write('%s<nviz>\n' % (' ' * self.indent))
+                    if maplayer.type == 'raster':
+                        self.__writeNvizSurface(nviz['surface'])
+                    elif maplayer.type == 'vector':
+                        self.__writeNvizVector(nviz['vector'])
+                    self.file.write('%s</nviz>\n' % (' ' * self.indent))
+                self.indent -= 4
+                self.file.write('%s</layer>\n' % (' ' * self.indent))
+            item = mapTree.GetNextSibling(item)
+        self.indent -= 4
+        
+    def __writeNvizSurface(self, data):
+        """!Save Nviz raster layer properties to workspace
+
+        @param data Nviz layer properties
+        """
+        if 'object' not in data: # skip disabled
+            return
+        self.indent += 4
+        self.file.write('%s<surface>\n' % (' ' * self.indent))
+        self.indent += 4
+        for attrb in data.iterkeys():
+            if len(data[attrb]) < 1: # skip empty attributes
+                continue
+            if attrb == 'object':
+                continue
+            
+            for name in data[attrb].iterkeys():
+                # surface attribute
+                if attrb == 'attribute':
+                    self.file.write('%s<%s name="%s" map="%d">\n' % \
+                                   (' ' * self.indent, attrb, name, data[attrb][name]['map']))
+                    self.indent += 4
+                    self.file.write('%s<value>%s</value>\n' % (' ' * self.indent, data[attrb][name]['value']))
+                    self.indent -= 4
+                    # end tag
+                    self.file.write('%s</%s>\n' % (' ' * self.indent, attrb))
+
+            # draw mode
+            if attrb == 'draw':
+                self.file.write('%s<%s' %(' ' * self.indent, attrb))
+                if 'mode' in data[attrb]:
+                    for tag, value in data[attrb]['mode']['desc'].iteritems():
+                        self.file.write(' %s="%s"' % (tag, value))
+                self.file.write('>\n') # <draw ...>
+
+                if 'resolution' in data[attrb]:
+                    self.indent += 4
+                    for type in ('coarse', 'fine'):
+                        self.file.write('%s<resolution type="%s">\n' % (' ' * self.indent, type))
+                        self.indent += 4
+                        self.file.write('%s<value>%d</value>\n' % (' ' * self.indent,
+                                                                   data[attrb]['resolution'][type]))
+                        self.indent -= 4
+                        self.file.write('%s</resolution>\n' % (' ' * self.indent))
+
+                if 'wire-color' in data[attrb]:
+                    self.file.write('%s<wire_color>\n' % (' ' * self.indent))
+                    self.indent += 4
+                    self.file.write('%s<value>%s</value>\n' % (' ' * self.indent,
+                                                               data[attrb]['wire-color']['value']))
+                    self.indent -= 4
+                    self.file.write('%s</wire_color>\n' % (' ' * self.indent))
+                self.indent -= 4
+            
+            # position
+            elif attrb == 'position':
+                self.file.write('%s<%s>\n' %(' ' * self.indent, attrb))
+                i = 0
+                for tag in ('x', 'y', 'z'):
+                    self.indent += 4
+                    self.file.write('%s<%s>%d</%s>\n' % (' ' * self.indent, tag,
+                                                        data[attrb][tag], tag))
+                    i += 1
+                    self.indent -= 4
+
+            if attrb != 'attribute':
+                # end tag
+                self.file.write('%s</%s>\n' % (' ' * self.indent, attrb))
+
+        self.indent -= 4
+        self.file.write('%s</surface>\n' % (' ' * self.indent))
+        self.indent -= 4
+
+    def __writeNvizVector(self, data):
+        """!Save Nviz vector layer properties (lines/points) to workspace
+
+        @param data Nviz layer properties
+        """
+        self.indent += 4
+        for attrb in data.iterkeys():
+            if len(data[attrb]) < 1: # skip empty attributes
+                continue
+
+            if 'object' not in data[attrb]: # skip disabled
+                continue
+            if attrb == 'lines':
+                self.file.write('%s<v%s>\n' % (' ' * self.indent, attrb))
+            elif attrb == 'points':
+                markerId = data[attrb]['marker']['value']
+                marker = UserSettings.Get(group = 'nviz', key = 'vector',
+                                          subkey = ['points', 'marker'], internal = True)[markerId]
+                self.file.write('%s<v%s marker="%s">\n' % (' ' * self.indent,
+                                                           attrb,
+                                                           marker))
+            self.indent += 4
+            for name in data[attrb].iterkeys():
+                if name in ('object', 'marker'):
+                    continue
+                if name == 'mode':
+                    self.file.write('%s<%s type="%s">\n' % (' ' * self.indent, name,
+                                                          data[attrb][name]['type']))
+                    if data[attrb][name]['type'] == 'surface':
+                        self.indent += 4
+                        for idx, surface in enumerate(data[attrb][name]['surface']['value']):
+                            checked = data[attrb][name]['surface']['show'][idx]
+                            self.file.write('%s<map>\n' % (' ' * self.indent))
+                            self.indent += 4
+                            self.file.write('%s<name>%s</name>\n' % (' ' * self.indent, surface))
+                            self.file.write('%s<checked>%s</checked>\n' % (' ' * self.indent, int(checked)))
+                            self.indent -= 4
+                            self.file.write('%s</map>\n' % (' ' * self.indent))
+                        self.indent -= 4
+                    self.file.write('%s</%s>\n' % ((' ' * self.indent, name)))
+                else:
+                    self.file.write('%s<%s>\n' % (' ' * self.indent, name))
+                    self.indent += 4
+                    self.file.write('%s<value>%s</value>\n' % (' ' * self.indent, data[attrb][name]['value']))
+                    self.indent -= 4
+                    self.file.write('%s</%s>\n' % (' ' * self.indent, name))
+            self.indent -= 4
+            self.file.write('%s</v%s>\n' % (' ' * self.indent, attrb))
+
+        self.indent -= 4
+
+    def __writeNvizState(self, view, iview, light, constants):
+        """"!Save Nviz properties (view, light) to workspace
+
+        @param view Nviz view properties
+        @param iview Nviz internal view properties
+        @param light Nviz light properties
+        """
+        self.indent += 4
+        self.file.write('%s<nviz_state>\n' % (' ' * self.indent))
+        #
+        # view
+        #
+        self.indent += 4
+        self.file.write('%s<view>\n' % (' ' * self.indent))
+        self.indent += 4
+        # position
+        self.file.write('%s<v_position>\n' % (' ' * self.indent))
+        self.indent += 4
+        self.file.write('%s<x>%.2f</x>\n' % (' ' * self.indent, view['position']['x']))
+        self.file.write('%s<y>%.2f</y>\n' % (' ' * self.indent, view['position']['y']))
+        self.indent -= 4
+        self.file.write('%s</v_position>\n' % (' ' * self.indent))
+        # perspective
+        self.file.write('%s<persp>\n' % (' ' * self.indent))
+        self.indent += 4
+        self.file.write('%s<value>%d</value>\n' % (' ' * self.indent, view['persp']['value']))
+        self.file.write('%s<step>%d</step>\n' % (' ' * self.indent, view['persp']['step']))
+        self.file.write('%s<min>%d</min>\n' % (' ' * self.indent, iview['persp']['min']))
+        self.file.write('%s<max>%d</max>\n' % (' ' * self.indent, iview['persp']['max']))
+        self.indent -= 4
+        self.file.write('%s</persp>\n' % (' ' * self.indent))
+        # height
+        self.file.write('%s<v_height>\n' % (' ' * self.indent))
+        self.indent += 4
+        self.file.write('%s<value>%d</value>\n' % (' ' * self.indent, iview['height']['value']))
+        self.file.write('%s<min>%d</min>\n' % (' ' * self.indent, iview['height']['min']))
+        self.file.write('%s<max>%d</max>\n' % (' ' * self.indent, iview['height']['max']))
+        self.indent -= 4
+        self.file.write('%s</v_height>\n' % (' ' * self.indent))
+        # twist
+        self.file.write('%s<twist>\n' % (' ' * self.indent))
+        self.indent += 4
+        self.file.write('%s<value>%d</value>\n' % (' ' * self.indent, view['twist']['value']))
+        self.file.write('%s<min>%d</min>\n' % (' ' * self.indent, iview['twist']['min']))
+        self.file.write('%s<max>%d</max>\n' % (' ' * self.indent, iview['twist']['max']))
+        self.indent -= 4
+        self.file.write('%s</twist>\n' % (' ' * self.indent))
+        # z-exag
+        self.file.write('%s<z-exag>\n' % (' ' * self.indent))
+        self.indent += 4
+        self.file.write('%s<value>%.2f</value>\n' % (' ' * self.indent, view['z-exag']['value']))
+        self.file.write('%s<min>%d</min>\n' % (' ' * self.indent, view['z-exag']['min']))
+        self.file.write('%s<max>%d</max>\n' % (' ' * self.indent, view['z-exag']['max']))
+        self.file.write('%s<original>%.2f</original>\n' % (' ' * self.indent, iview['z-exag']['original']))
+        self.indent -= 4
+        self.file.write('%s</z-exag>\n' % (' ' * self.indent))
+        # focus (look here)
+        self.file.write('%s<focus>\n' % (' ' * self.indent))
+        self.indent += 4
+        self.file.write('%s<x>%d</x>\n' % (' ' * self.indent, iview['focus']['x']))
+        self.file.write('%s<y>%d</y>\n' % (' ' * self.indent, iview['focus']['y']))
+        self.file.write('%s<z>%d</z>\n' % (' ' * self.indent, iview['focus']['z']))
+        self.indent -= 4
+        self.file.write('%s</focus>\n' % (' ' * self.indent))
+        # background
+        self.__writeTagWithValue('background_color', view['background']['color'][:3], format = 'd:%d:%d')
+        
+        self.indent -= 4
+        self.file.write('%s</view>\n' % (' ' * self.indent))
+        #
+        # light
+        #
+        self.file.write('%s<light>\n' % (' ' * self.indent))
+        self.indent += 4
+        # position
+        self.file.write('%s<l_position>\n' % (' ' * self.indent))
+        self.indent += 4
+        self.file.write('%s<x>%.2f</x>\n' % (' ' * self.indent, light['position']['x']))
+        self.file.write('%s<y>%.2f</y>\n' % (' ' * self.indent, light['position']['y']))
+        self.file.write('%s<z>%d</z>\n' % (' ' * self.indent, light['position']['z']))
+        self.indent -= 4
+        self.file.write('%s</l_position>\n' % (' ' * self.indent))
+        # bright
+        self.__writeTagWithValue('bright', light['bright'])
+        # ambient
+        self.__writeTagWithValue('ambient', light['ambient'])
+        # color
+        self.__writeTagWithValue('color', light['color'][:3], format = 'd:%d:%d')
+        
+        self.indent -= 4
+        self.file.write('%s</light>\n' % (' ' * self.indent))
+        #
+        # constant planes
+        #
+        if constants:
+            self.file.write('%s<constant_planes>\n' % (' ' * self.indent))
+            self.indent += 4
+            for idx, plane in enumerate(constants):
+                self.file.write('%s<plane>\n' % (' ' * self.indent))
+                self.indent += 4
+                self.__writeTagWithValue('height', constants[idx]['constant']['value'])
+                self.__writeTagWithValue('fine_resolution', constants[idx]['constant']['resolution'])
+                self.__writeTagWithValue('color', constants[idx]['constant']['color'], format = 's')
+                self.indent -= 4
+                self.file.write('%s</plane>\n' % (' ' * self.indent))
+            self.indent -= 4
+            self.file.write('%s</constant_planes>\n' % (' ' * self.indent))
+        self.indent -= 4
+        
+        self.file.write('%s</nviz_state>\n' % (' ' * self.indent))
+        self.indent -= 4
+    
+    def __writeTagWithValue(self, tag, data, format = 'd'):
+        """!Helper function for writing pair tag
+        
+        @param tag written tag
+        @param data written data
+        @param format conversion type
+        """
+        self.file.write('%s<%s>\n' % (' ' * self.indent, tag))
+        self.indent += 4
+        self.file.write('%s' % (' ' * self.indent))
+        self.file.write(('<value>%' + format + '</value>\n') % data)
+        self.indent -= 4
+        self.file.write('%s</%s>\n' % (' ' * self.indent, tag))
+        
+class ProcessGrcFile(object):
+    def __init__(self, filename):
+        """!Process GRC file"""
+        self.filename = filename
+
+        # elements
+        self.inGroup = False
+        self.inRaster = False
+        self.inVector = False
+
+        # list of layers
+        self.layers = []
+
+        # error message
+        self.error = ''
+        self.num_error = 0
+
+    def read(self, parent):
+        """!Read GRC file
+
+        @param parent parent window
+
+        @return list of map layers
+        """
+        try:
+            file = open(self.filename, "r")
+        except IOError:
+            wx.MessageBox(parent=parent,
+                          message=_("Unable to open file <%s> for reading.") % self.filename,
+                          caption=_("Error"), style=wx.OK | wx.ICON_ERROR)
+            return []
+
+        line_id = 1
+        for line in file.readlines():
+            self.process_line(line.rstrip('\n'), line_id)
+            line_id +=1
+
+        file.close()
+
+        if self.num_error > 0:
+            wx.MessageBox(parent=parent,
+                          message=_("Some lines were skipped when reading settings "
+                                    "from file <%(file)s>.\nSee 'Command output' window for details.\n\n"
+                                    "Number of skipped lines: %(line)d") % \
+                                        { 'file' : self.filename, 'line' : self.num_error },
+                          caption=_("Warning"), style=wx.OK | wx.ICON_EXCLAMATION)
+            parent.goutput.WriteLog('Map layers loaded from GRC file <%s>' % self.filename)
+            parent.goutput.WriteLog('Skipped lines:\n%s' % self.error)
+
+        return self.layers
+
+    def process_line(self, line, line_id):
+        """!Process line definition"""
+        element = self._get_element(line)
+        if element == 'Group':
+            self.groupName = self._get_value(line)
+            self.layers.append({
+                    "type"    : 'group',
+                    "name"    : self.groupName,
+                    "checked" : None,
+                    "opacity" : None,
+                    "cmd"     : None,
+                    "group"   : self.inGroup,
+                    "display" : 0 })
+            self.inGroup = True
+
+        elif element == '_check':
+            if int(self._get_value(line)) ==  1:
+                self.layers[-1]['checked'] = True
+            else:
+                self.layers[-1]['checked'] = False
+            
+        elif element == 'End':
+            if self.inRaster:
+                self.inRaster = False
+            elif self.inVector:
+                self.inVector = False
+            elif self.inGroup:
+                self.inGroup = False
+            elif self.inGridline:
+                self.inGridline = False
+        
+        elif element == 'opacity':
+            self.layers[-1]['opacity'] = float(self._get_value(line))
+
+        # raster
+        elif element == 'Raster':
+            self.inRaster = True
+            self.layers.append({
+                    "type"    : 'raster',
+                    "name"    : self._get_value(line),
+                    "checked" : None,
+                    "opacity" : None,
+                    "cmd"     : ['d.rast'],
+                    "group"   : self.inGroup,
+                    "display" : 0})
+
+        elif element == 'map' and self.inRaster:
+            self.layers[-1]['cmd'].append('map=%s' % self._get_value(line))
+            
+        elif element == 'overlay' and self.inRaster:
+            if int(self._get_value(line)) == 1:
+                self.layers[-1]['cmd'].append('-o')
+            
+        elif element == 'rastquery' and self.inRaster:
+            value = self._get_value(line)
+            if value != '':
+                self.layers[-1]['cmd'].append('catlist=%s' % value)
+            
+        elif element == 'bkcolor' and self.inRaster:
+            value = self._get_value(line)
+            if value != '':
+                self.layers[-1]['cmd'].append('bg=%s' % value)
+
+        # vector
+        elif element == 'Vector':
+            self.inVector = True
+            self.layers.append({
+                    "type"    : 'vector',
+                    "name"    : self._get_value(line),
+                    "checked" : None,
+                    "opacity" : None,
+                    "cmd"     : ['d.vect'],
+                    "group"   : self.inGroup,
+                    "display" : 0})
+
+        elif element == 'vect' and self.inVector:
+            self.layers[-1]['cmd'].append('map=%s' % self._get_value(line))
+                
+        elif element in ('display_shape',
+                         'display_cat',
+                         'display_topo',
+                         'display_dir',
+                         'display_attr',
+                         'type_point',
+                         'type_line',
+                         'type_boundary',
+                         'type_centroid',
+                         'type_area',
+                         'type_face') and self.inVector:
+            
+            if int(self._get_value(line)) == 1:
+                name = element.split('_')[0]
+                type = element.split('_')[1]
+                paramId = self._get_cmd_param_index(self.layers[-1]['cmd'], name)
+                if paramId == -1:
+                    self.layers[-1]['cmd'].append('%s=%s' % (name, type))
+                else:
+                    self.layers[-1]['cmd'][paramId] += ',%s' % type
+
+        elif element in ('color',
+                         'fcolor',
+                         'lcolor') and self.inVector:
+            value = self._get_value(line)
+            if value != '':
+                self.layers[-1]['cmd'].append('%s=%s' % (element,
+                                                         self._color_name_to_rgb(value)))
+
+        elif element == 'rdmcolor' and self.inVector:
+            if int(self._get_value(line)) == 1:
+                self.layers[-1]['cmd'].append('-c')
+
+        elif element == 'sqlcolor' and self.inVector:
+            if int(self._get_value(line)) == 1:
+                self.layers[-1]['cmd'].append('-a')
+
+        elif element in ('icon',
+                         'size',
+                         'layer',
+                         'xref',
+                         'yref',
+                         'lsize',
+                         'where',
+                         'minreg',
+                         'maxreg') and self.inVector:
+            value = self._get_value(line)
+            if value != '':
+                self.layers[-1]['cmd'].append('%s=%s' % (element,
+                                                         value))
+        
+        elif element == 'lwidth':
+            value = self._get_value(line)
+            if value != '':
+                self.layers[-1]['cmd'].append('width=%s' % value)
+
+        elif element == 'lfield':
+            value = self._get_value(line)
+            if value != '':
+                self.layers[-1]['cmd'].append('llayer=%s' % value)
+                                        
+        elif element == 'attribute':
+            value = self._get_value(line)
+            if value != '':
+                self.layers[-1]['cmd'].append('attrcol=%s' % value)
+
+        elif element == 'cat':
+            value = self._get_value(line)
+            if value != '':
+                self.layers[-1]['cmd'].append('cats=%s' % value)
+
+        # gridline
+        elif element == 'gridline':
+            self.inGridline = True
+            self.layers.append({
+                    "type"    : 'grid',
+                    "name"    : self._get_value(line),
+                    "checked" : None,
+                    "opacity" : None,
+                    "cmd"     : ['d.grid'],
+                    "group"   : self.inGroup,
+                    "display" : 0})
+
+        elif element == 'gridcolor':
+            value = self._get_value(line)
+            if value != '':
+                self.layers[-1]['cmd'].append('color=%s' % self._color_name_to_rgb(value))
+
+        elif element == 'gridborder':
+            value = self._get_value(line)
+            if value != '':
+                self.layers[-1]['cmd'].append('bordercolor=%s' % self._color_name_to_rgb(value))
+
+        elif element == 'textcolor':
+            value = self._get_value(line)
+            if value != '':
+                self.layers[-1]['cmd'].append('textcolor=%s' % self._color_name_to_rgb(value))
+
+        elif element in ('gridsize',
+                         'gridorigin'):
+            value = self._get_value(line)
+            if value != '':
+                self.layers[-1]['cmd'].append('%s=%s' % (element[4:], value))
+
+        elif element in 'fontsize':
+            value = self._get_value(line)
+            if value != '':
+                self.layers[-1]['cmd'].append('%s=%s' % (element, value))
+        
+        elif element == 'griddraw':
+            value = self._get_value(line)
+            if value == '0':
+                self.layers[-1]['cmd'].append('-n')
+                
+        elif element == 'gridgeo':
+            value = self._get_value(line)
+            if value == '1':
+                self.layers[-1]['cmd'].append('-g')
+        
+        elif element == 'borderdraw':
+            value = self._get_value(line)
+            if value == '0':
+                self.layers[-1]['cmd'].append('-b')
+
+        elif element == 'textdraw':
+            value = self._get_value(line)
+            if value == '0':
+                self.layers[-1]['cmd'].append('-t')
+        
+        else:
+            self.error += _(' row %d:') % line_id + line + os.linesep
+            self.num_error += 1
+
+    def _get_value(self, line):
+        """!Get value of element"""
+        try:
+            return line.strip(' ').split(' ')[1].strip(' ')
+        except:
+            return ''
+
+    def _get_element(self, line):
+        """!Get element tag"""
+        return line.strip(' ').split(' ')[0].strip(' ')
+
+    def _get_cmd_param_index(self, cmd, name):
+        """!Get index of parameter in cmd list
+
+        @param cmd cmd list
+        @param name parameter name
+
+        @return index
+        @return -1 if not found
+        """
+        i = 0
+        for param in cmd:
+            if '=' not in param:
+                i += 1
+                continue
+            if param.split('=')[0] == name:
+                return i
+
+            i += 1
+
+        return -1
+
+    def _color_name_to_rgb(self, value):
+        """!Convert color name (#) to rgb values"""
+        col = wx.NamedColour(value)
+        return str(col.Red()) + ':' + \
+            str(col.Green()) + ':' + \
+            str(col.Blue())


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/create__init__.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/create__init__.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/create__init__.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+import os
+import sys
+import glob
+
+def main(path):
+    if not os.path.exists(path) or not os.path.isdir(path):
+        print >> sys.stderr, "'%s' is not a directory" % path
+        return 1
+    
+    modules = []
+    for f in glob.glob(os.path.join(os.path.basename(path), '*.py')):
+        if f[-5:-3] == '__':
+            continue
+        modules.append(os.path.splitext(os.path.basename(f))[0])
+        
+    fd = open(os.path.join(path, '__init__.py'), 'w')
+    try:
+        fd.write('all = [%s' % os.linesep)
+        for m in modules:
+            fd.write("    '%s',%s" % (m, os.linesep))
+        fd.write('    ]%s' % os.linesep)
+    finally:
+        fd.close()
+    
+    return 0
+
+if __name__ == "__main__":
+    if len(sys.argv) < 2:
+        sys.exit("usage: %s path/to/gui_modules" % sys.argv[0])
+    
+    sys.exit(main(sys.argv[1]))


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/dialogs.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/dialogs.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/dialogs.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,698 @@
+"""!
+ at package dbmgr.dialogs
+
+ at brief DBM-related dialogs
+
+List of classes:
+ - dialogs::DisplayAttributesDialog
+ - dialogs::ModifyTableRecord
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import types
+
+from core import globalvar
+import wx
+import wx.lib.scrolledpanel as scrolled
+
+from core.gcmd        import RunCommand, GError
+from core.debug       import Debug
+from core.settings    import UserSettings
+from dbmgr.vinfo      import VectorDBInfo
+from gui_core.widgets import IntegerValidator, FloatValidator
+
+class DisplayAttributesDialog(wx.Dialog):
+    def __init__(self, parent, map,
+                 query = None, cats = None, line = None,
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+                 pos = wx.DefaultPosition,
+                 action = "add", ignoreError = False):
+        """!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)
+        @param ignoreError True to ignore errors
+        """
+        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:
+            if not ignoreError:
+                dlg = wx.MessageDialog(parent = self.parent,
+                                       message = _("No attribute table found.\n\n"
+                                                   "Do you want to create a new attribute table "
+                                                   "and defined a link to vector map <%s>?") % self.map,
+                                       caption = _("Create table?"),
+                                       style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
+                if dlg.ShowModal() == wx.ID_YES:
+                    lmgr = self.parent.lmgr
+                    lmgr.OnShowAttributeTable(event = None, selection = 'layers')
+                
+                dlg.Destroy()
+            
+            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)
+        if self.action == 'display':
+            self.closeDialog.Enable(False)
+        
+        # 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(_("Define 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"))
+        if self.action == 'display':
+            btnSubmit.Enable(False)
+        
+        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()
+        w += 50
+        if h < 200:
+            self.SetMinSize((w, 200))
+        else:
+            self.SetMinSize((w, h))
+        
+        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 IsFound(self):
+        """!Check for status
+
+        @return True on attributes found
+        @return False attributes not found
+        """
+        return bool(self.notebook.GetPageCount())
+    
+    def GetSQLString(self, updateValues = False):
+        """!Create SQL statement string based on self.sqlStatement
+
+        Show error message when invalid values are entered.
+        
+        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
+                    ctype  = columns[name]['ctype']
+                    value = columns[name]['values'][idx]
+                    id    = columns[name]['ids'][idx]
+                    try:
+                        newvalue = self.FindWindowById(id).GetValue()
+                    except:
+                        newvalue = self.FindWindowById(id).GetLabel()
+                  
+                    if newvalue:
+                        try:
+                            if ctype == int:
+                                newvalue = int(newvalue)
+                            elif ctype == float:
+                                newvalue = float(newvalue)
+                        except ValueError:
+                            GError(parent = self,
+                                   message = _("Column <%(col)s>: Value '%(value)s' needs to be entered as %(type)s.") % \
+                                       {'col' : name,
+                                        'value' : str(newvalue),
+                                        'type' : columns[name]['type'].lower()},
+                                   showTraceback = False)
+                            sqlCommands.append(None)
+                            continue
+                    else:
+                        if self.action == 'add':
+                            continue
+                    
+                    if newvalue != value:
+                        updatedColumns.append(name)
+                        if not newvalue:
+                            updatedValues.append('NULL')
+                        else:
+                            if ctype != str:
+                                updatedValues.append(str(newvalue))
+                            else:
+                                updatedValues.append("'" + str(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 hasattr(self, "digit"):
+            self.parent.digit.GetDisplay().SetSelected([])
+            self.parent.UpdateMap(render = False)
+        else:
+            self.parent.parent.OnRender(None)
+        
+        self.Close()
+
+    def OnSubmit(self, event):
+        """!Submit records"""
+        close = True
+        enc = UserSettings.Get(group = 'atm', key = 'encoding', subkey = 'value')
+        if not enc and 'GRASS_DB_ENCODING' in os.environ:
+            enc = os.environ['GRASS_DB_ENCODING']
+        
+        for sql in self.GetSQLString(updateValues = True):
+            if not sql:
+                close = False
+                continue
+            if enc:
+                sql = sql.encode(enc)
+            
+            RunCommand('db.execute',
+                       parent = self,
+                       quiet = True,
+                       stdin = sql)
+        
+        if close and 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,
+                     action = None):
+        """!Update dialog
+        
+        @param map name of vector map
+        @param query
+        @param cats
+        @param fid feature id
+        @param action add, update, display or None
+        
+        @return True if updated
+        @return False
+        """
+        if action:
+            self.action = action
+            if action == 'display':
+                enabled = False
+            else:
+                enabled = True
+            self.closeDialog.Enable(enabled)
+            self.FindWindowById(wx.ID_OK).Enable(enabled)
+        
+        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 'Layer' in data:
+                idx = 0
+                for layer in data['Layer']:
+                    layer = int(layer)
+                    if 'Id' in data:
+                        tfid = int(data['Id'][idx])
+                    else:
+                        tfid = 0 # Area / Volume
+                    if not tfid in self.cats:
+                        self.cats[tfid] = {}
+                    if not layer in self.cats[tfid]:
+                        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 layer in self.cats[self.fid]:
+                    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 layer in self.cats[self.fid]:
+                        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 = 3, hgap = 3, vgap = 3)
+                flexSizer.AddGrowableCol(2)
+                # 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'].lower()
+                    ctype  = columns[name]['ctype']
+                    
+                    if columns[name]['values'][idx] is not None:
+                        if columns[name]['ctype'] != types.StringType:
+                            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 = "[%s]:" % vtype)
+                    colValue = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = value)
+                    colValue.SetName(name)
+                    if ctype == int:
+                        colValue.SetValidator(IntegerValidator())
+                    elif ctype == float:
+                        colValue.SetValidator(FloatValidator())
+                    
+                    self.Bind(wx.EVT_TEXT, self.OnSQLStatement, colValue)
+                    if self.action == 'display':
+                        colValue.SetWindowStyle(wx.TE_READONLY)
+                    
+                    flexSizer.Add(colName, proportion = 0,
+                                  flag = wx.ALIGN_CENTER_VERTICAL)
+                    flexSizer.Add(colType, proportion = 0,
+                                  flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+                    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):
+    def __init__(self, parent, title, data, keyEditable = (-1, True),
+                 id = wx.ID_ANY, style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+        """!Dialog for inserting/updating table record
+        
+        @param data a list: [(column, value)]
+        @param KeyEditable (id, editable?) indicates if textarea for key column
+        is editable(True) or not
+        """
+        # parent -> VDigitWindow
+        wx.Dialog.__init__(self, parent, id, title, style = style)
+        
+        self.CenterOnParent()
+        
+        self.keyId = keyEditable[0]
+        
+        box = wx.StaticBox(parent = self, id = wx.ID_ANY)
+        box.Hide()
+        self.dataPanel = scrolled.ScrolledPanel(parent = self, id = wx.ID_ANY,
+                                                style = wx.TAB_TRAVERSAL)
+        self.dataPanel.SetupScrolling(scroll_x = False)
+        
+        # buttons
+        self.btnCancel = wx.Button(self, wx.ID_CANCEL)
+        self.btnSubmit = wx.Button(self, wx.ID_OK, _("&Submit"))
+        self.btnSubmit.SetDefault()
+        
+        # data area
+        self.widgets = []
+        cId = 0
+        self.usebox = False
+        self.cat = None
+        winFocus = False
+        
+        for column, ctype, ctypeStr, value in data:
+            if self.keyId == cId:
+                self.cat = int(value)
+                if not keyEditable[1]:
+                    self.usebox = True
+                    box.SetLabel(" %s %d " % (_("Category"), self.cat))
+                    box.Show()
+                    self.boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+                    cId += 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))
+                if ctype == int:
+                    valueWin.SetValidator(IntegerValidator())
+                elif ctype == float:
+                    valueWin.SetValidator(FloatValidator())
+                if not winFocus:
+                    wx.CallAfter(valueWin.SetFocus)
+                    winFocus = True
+            
+            label = wx.StaticText(parent = self.dataPanel, id = wx.ID_ANY,
+                                  label = column)
+            ctype = wx.StaticText(parent = self.dataPanel, id = wx.ID_ANY,
+                                  label = "[%s]:" % ctypeStr.lower())
+            self.widgets.append((label.GetId(), ctype.GetId(), valueWin.GetId()))
+            
+            cId += 1
+        
+        self._layout()
+        
+    def _layout(self):
+        """!Do layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        # data area
+        dataSizer = wx.FlexGridSizer(cols = 3, hgap = 3, vgap = 3)
+        dataSizer.AddGrowableCol(2)
+        
+        for labelId, ctypeId, valueId in self.widgets:
+            label = self.FindWindowById(labelId)
+            ctype = self.FindWindowById(ctypeId)
+            value = self.FindWindowById(valueId)
+            
+            dataSizer.Add(label, proportion = 0,
+                          flag = wx.ALIGN_CENTER_VERTICAL)
+            dataSizer.Add(ctype, proportion = 0,
+                          flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+            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.GetBestSize()[0] + 25
+        self.SetMinSize((framewidth, 250))
+
+        self.SetAutoLayout(True)
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+        
+        self.Layout()
+        
+    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, ctypeId, 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/dbmgr/dialogs.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Added: grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/manager.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/manager.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/manager.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,3093 @@
+"""!
+ at package dbmgr.manager
+
+ at brief GRASS Attribute Table Manager
+
+This program is based on FileHunter, published in 'The wxPython Linux
+Tutorial' on wxPython WIKI pages.
+
+It also uses some functions at
+http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/426407
+
+ at code
+python dbm.py vector at mapset
+ at endcode
+
+List of classes:
+ - manager::Log
+ - manager::VirtualAttributeList
+ - manager::AttributeManager
+ - manager::TableListCtrl
+ - manager::LayerListCtrl
+ - manager::LayerBook
+
+(C) 2007-2009, 2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Jachym Cepicky <jachym.cepicky gmail.com>
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import sys
+import os
+import locale
+import tempfile
+import copy
+import types
+
+if __name__ == "__main__":
+    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'wxpython'))
+from core import globalvar
+import wx
+import wx.lib.mixins.listctrl as listmix
+import wx.lib.flatnotebook    as FN
+
+import grass.script as grass
+
+from dbmgr.sqlbuilder import SQLFrame
+from core.gcmd        import RunCommand, GException, GError, GMessage, GWarning
+from core.utils       import ListOfCatsToRange
+from gui_core.dialogs import CreateNewVector
+from dbmgr.vinfo      import VectorDBInfo, unicodeValue, createDbInfoDesc
+from core.debug       import Debug
+from dbmgr.dialogs    import ModifyTableRecord
+from core.settings    import UserSettings
+from gui_core.widgets import GNotebook
+
+class Log:
+    """
+    The log output is redirected to the status bar of the containing frame.
+    """
+    def __init__(self, parent):
+        self.parent = parent
+
+    def write(self, text_string):
+        """!Update status bar"""
+        self.parent.SetStatusText(text_string.strip())
+
+
+class VirtualAttributeList(wx.ListCtrl,
+                           listmix.ListCtrlAutoWidthMixin,
+                           listmix.ColumnSorterMixin):
+    """
+    Support virtual list class
+    """
+    def __init__(self, parent, log, mapDBInfo, layer):
+        #
+        # initialize variables
+        #
+        self.parent  = parent
+        self.log     = log
+        self.mapDBInfo = mapDBInfo
+        self.layer   = layer
+        
+        self.columns = {} # <- LoadData()
+
+        wx.ListCtrl.__init__(self, parent = parent, id = wx.ID_ANY,
+                             style = wx.LC_REPORT | wx.LC_HRULES |
+                             wx.LC_VRULES | wx.LC_VIRTUAL | wx.LC_SORT_ASCENDING)
+        
+        try:
+            keyColumn = self.LoadData(layer)
+        except GException, e:
+            GError(parent = self,
+                   message = e.value)
+            return
+        
+        #
+        # add some attributes (colourful background for each item rows)
+        #
+        self.attr1 = wx.ListItemAttr()
+        self.attr1.SetBackgroundColour(wx.Colour(238,238,238))
+        self.attr2 = wx.ListItemAttr()
+        self.attr2.SetBackgroundColour("white")
+        self.il = wx.ImageList(16, 16)
+        self.sm_up = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_UP,   wx.ART_TOOLBAR,
+                                                          (16,16)))
+        self.sm_dn = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_DOWN, wx.ART_TOOLBAR,
+                                                          (16,16)))
+        self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
+        
+        # setup mixins
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+        listmix.ColumnSorterMixin.__init__(self, len(self.columns))
+
+        # sort item by category (id)
+        if keyColumn > -1:
+            self.SortListItems(col = keyColumn, ascending = True) 
+        else:
+            self.SortListItems(col = 0, ascending = True) 
+        
+        # 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.OnColumnSort)     
+        self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.OnColumnMenu)     
+        
+    def Update(self, mapDBInfo):
+        """!Update list according new mapDBInfo description"""
+        self.mapDBInfo = mapDBInfo
+        self.LoadData(self.layer)
+
+    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 (-> 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
+        """
+        self.log.write(_("Loading data..."))
+        
+        tableName    = self.mapDBInfo.layers[layer]['table']
+        keyColumn    = self.mapDBInfo.layers[layer]['key']
+        try:
+            self.columns = self.mapDBInfo.tables[tableName]
+        except KeyError:
+            raise GException(_("Attribute table <%s> not found. "
+                               "For creating the table switch to "
+                               "'Manage layers' tab.") % tableName)
+        
+        if not columns:
+            columns = self.mapDBInfo.GetColumns(tableName)
+        else:
+            all = self.mapDBInfo.GetColumns(tableName)
+            for col in columns:
+                if col not in all:
+                    GError(parent = self,
+                           message = _("Column <%(column)s> not found in "
+                                       "in the table <%(table)s>.") % \
+                               { 'column' : col, 'table' : tableName })
+                    return
+        
+        try:
+            # for maps connected via v.external
+            keyId = columns.index(keyColumn)
+        except:
+            keyId = -1
+        
+        #
+        # read data
+        #
+        # FIXME: Max. number of rows, while the GUI is still usable
+
+        # stdout can be very large, do not use PIPE, redirect to temp file
+        # TODO: more effective way should be implemented...
+        outFile = tempfile.NamedTemporaryFile(mode = 'w+b')
+        
+        if sql:
+            ret = RunCommand('db.select',
+                             quiet = True,
+                             parent = self,
+                             flags = 'c',
+                             sql = sql,
+                             output = outFile.name)
+        else:
+            if columns:
+                ret = RunCommand('v.db.select',
+                                 quiet = True,
+                                 parent = self,
+                                 flags = 'c',
+                                 map = self.mapDBInfo.map,
+                                 layer = layer,
+                                 columns = ','.join(columns),
+                                 where = where,
+                                 stdout = outFile)
+            else:
+                ret = RunCommand('v.db.select',
+                                 quiet = True,
+                                 parent = self,
+                                 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  = {}
+        self.itemIndexMap = []
+        self.itemCatsMap  = {}
+        
+        self.DeleteAllItems()
+        
+        # self.ClearAll()
+        for i in range(self.GetColumnCount()):
+            self.DeleteColumn(0)
+        
+        i = 0
+        info = wx.ListItem()
+        info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT
+        info.m_image = -1
+        info.m_format = 0
+        for column in columns:
+            info.m_text = column
+            self.InsertColumnInfo(i, info)
+            i += 1
+            
+            if i >= 256:
+                self.log.write(_("Can display only 256 columns."))
+        
+        i = 0
+        outFile.seek(0)
+        
+        while True:
+            # os.linesep doesn't work here (MSYS)
+            record = outFile.readline().replace('\n', '')
+            
+            if not record:
+                break
+           
+            self.AddDataRow(i, record, columns, keyId)
+
+            i += 1
+            if i >= 100000:
+                self.log.write(_("Limit 100000 records."))
+                break
+        
+        self.SetItemCount(i)
+        
+        i = 0
+        for col in columns:
+            width = self.columns[col]['length'] * 6 # FIXME
+            if width < 60:
+                width = 60
+            if width > 300:
+                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(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
+                    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..."""
+        #         cat = int(self.GetItemText(event.m_itemIndex))
+        #         if cat not in self.selectedCats:
+        #             self.selectedCats.append(cat)
+        #             self.selectedCats.sort()
+        
+        event.Skip()
+
+    def OnItemDeselected(self, event):
+        """!Item deselected. Remove item from selected cats..."""
+        #         cat = int(self.GetItemText(event.m_itemIndex))
+        #         if cat in self.selectedCats:
+        #             self.selectedCats.remove(cat)
+        #             self.selectedCats.sort()
+
+        event.Skip()
+
+    def GetSelectedItems(self):
+        """!Return list of selected items (category numbers)"""
+        cats = []
+        item = self.GetFirstSelected()
+        while item != -1:
+            cats.append(self.GetItemText(item))
+            item = self.GetNextSelected(item)
+
+        return cats
+
+    def GetColumnText(self, index, col):
+        """!Return column text"""
+        item = self.GetItem(index, col)
+        return item.GetText()
+
+    def GetListCtrl(self):
+        """!Returt list"""
+        return self
+
+    def OnGetItemText(self, item, col):
+        """!Get item text"""
+        index = self.itemIndexMap[item]
+        s = self.itemDataMap[index][col]
+        return s
+
+    def OnGetItemAttr(self, item):
+        """!Get item attributes"""
+        if ( item % 2) == 0:
+            return self.attr2
+        else:
+            return self.attr1
+
+    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
+        
+        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()
+        info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE
+        info.m_image = -1
+        for column in range(self.GetColumnCount()):
+            info.m_text = self.GetColumn(column).GetText()
+            self.SetColumn(column, info)
+        
+    def SortItems(self, sorter = cmp):
+        """!Sort items"""
+        items = list(self.itemDataMap.keys())
+        items.sort(self.Sorter)
+        self.itemIndexMap = items
+
+        # redraw the list
+        self.Refresh()
+        
+    def Sorter(self, key1, key2):
+        colName = self.GetColumn(self._col).GetText()
+        ascending = self._colSortFlag[self._col]
+        try:
+            item1 = self.columns[colName]["ctype"](self.itemDataMap[key1][self._col])
+            item2 = self.columns[colName]["ctype"](self.itemDataMap[key2][self._col])
+        except ValueError:
+            item1 = self.itemDataMap[key1][self._col]
+            item2 = self.itemDataMap[key2][self._col]
+
+        if type(item1) == types.StringType or type(item2) == types.StringTypes:
+            cmpVal = locale.strcoll(str(item1), str(item2))
+        else:
+            cmpVal = cmp(item1, item2)
+
+
+        # 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"""
+        return (self.sm_dn, self.sm_up)
+
+    def IsEmpty(self):
+        """!Check if list if empty"""
+        if self.columns:
+            return False
+        
+        return True
+    
+class AttributeManager(wx.Frame):
+    def __init__(self, parent, id = wx.ID_ANY,
+                 title = None, vectorName = None, item = None, log = None,
+                 selection = None, **kwargs):
+        """!GRASS Attribute Table Manager window
+
+        @param parent parent window
+        @parem id window id
+        @param title window title or None for default title
+        @param vetorName name of vector map
+        @param item item from Layer Tree
+        @param log log window
+        @param selection name of page to be selected
+        @param kwagrs other wx.Frame's arguments
+        """
+        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.vectorName, element = 'vector')['mapset'] == grass.gisenv()['MAPSET']:
+            self.editable = True
+        else:
+            self.editable = False
+        
+        self.cmdLog = log    # self.parent.goutput
+        
+        wx.Frame.__init__(self, parent, id, *kwargs)
+
+        # 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))
+
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+        try:
+            self.map        = self.parent.curr_page.maptree.Map
+            self.mapdisplay = self.parent.curr_page.maptree.mapdisplay
+        except:
+            self.map = self.mapdisplay = None
+        
+        # status bar log class
+        self.log = Log(self) # -> statusbar
+
+        # query map layer (if parent (GMFrame) is given)
+        self.qlayer = None
+
+        # -> layers / tables description
+        self.mapDBInfo = VectorDBInfo(self.vectorName)
+
+        # sqlbuilder
+        self.builder = None
+        
+        if len(self.mapDBInfo.layers.keys()) == 0:
+            GMessage(parent = self.parent,
+                     message = _("Database connection for vector map <%s> "
+                                 "is not defined in DB file. "
+                                 "You can define new connection in "
+                                 "'Manage layers' tab.") % self.vectorName)
+        
+        #
+        # list of command/SQL statements to be performed
+        #
+        self.listOfCommands      = []
+        self.listOfSQLStatements = []
+
+        self.CreateStatusBar(number = 1)
+
+        # set up virtual lists (each layer)
+        ### {layer: list, widgets...}
+        self.layerPage = {}
+
+        self.notebook = GNotebook(self.panel, style = globalvar.FNPageDStyle)
+        
+        if globalvar.hasAgw:
+            dbmStyle = { 'agwStyle' : globalvar.FNPageStyle }
+        else:
+            dbmStyle = { 'style' : globalvar.FNPageStyle }
+        
+        self.browsePage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
+                                          **dbmStyle)
+        self.notebook.AddPage(page = self.browsePage, text = _("Browse data"),
+                              name = 'browse')
+        self.browsePage.SetTabAreaColour(globalvar.FNPageColor)
+
+        self.manageTablePage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
+                                               **dbmStyle)
+        self.notebook.AddPage(page = self.manageTablePage, text = _("Manage tables"),
+                              name = 'table')
+        if not self.editable:
+            self.notebook.GetPage(self.notebook.GetPageCount()-1).Enable(False)
+        self.manageTablePage.SetTabAreaColour(globalvar.FNPageColor)
+
+        self.manageLayerPage = FN.FlatNotebook(self.panel, id = wx.ID_ANY,
+                                               **dbmStyle)
+        self.notebook.AddPage(page = self.manageLayerPage, text = _("Manage layers"),
+                              name = 'layers')
+        self.manageLayerPage.SetTabAreaColour(globalvar.FNPageColor)
+        if not self.editable:
+            self.notebook.GetPage(self.notebook.GetPageCount()-1).Enable(False)
+        
+        self._createBrowsePage()
+        self._createManageTablePage()
+        self._createManageLayerPage()
+
+        if selection:
+            wx.CallAfter(self.notebook.SetSelectionByName, selection)
+        else:
+            wx.CallAfter(self.notebook.SetSelection, 0) # select browse tab
+        
+        # buttons
+        self.btnQuit   = wx.Button(parent = self.panel, id = wx.ID_EXIT)
+        self.btnQuit.SetToolTipString(_("Close Attribute Table Manager"))
+        self.btnReload = wx.Button(parent = self.panel, id = wx.ID_REFRESH)
+        self.btnReload.SetToolTipString(_("Reload attribute data (selected layer only)"))
+
+        # events
+        self.btnQuit.Bind(wx.EVT_BUTTON,   self.OnCloseWindow)
+        self.btnReload.Bind(wx.EVT_BUTTON, self.OnDataReload)
+        self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
+        self.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnLayerPageChanged, self.browsePage)
+        self.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnLayerPageChanged, self.manageTablePage)
+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+
+        # do layout
+        self._layout()
+
+        # self.SetMinSize(self.GetBestSize())
+        self.SetSize((700, 550)) # FIXME hard-coded size
+        self.SetMinSize(self.GetSize())
+
+    def _createBrowsePage(self, onlyLayer = -1):
+        """!Create browse tab page"""
+        for layer in self.mapDBInfo.layers.keys():
+            if onlyLayer > 0 and layer != onlyLayer:
+                continue
+
+            panel = wx.Panel(parent = self.browsePage, id = wx.ID_ANY)
+            
+            #IMPORTANT NOTE: wx.StaticBox MUST be defined BEFORE any of the 
+            #   controls that are placed IN the wx.StaticBox, or it will freeze
+            #   on the Mac
+            
+            listBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                                   label = " %s " % _("Attribute data - right-click to edit/manage records"))
+            listSizer = wx.StaticBoxSizer(listBox, wx.VERTICAL)
+            
+            win = VirtualAttributeList(panel, self.log,
+                                       self.mapDBInfo, layer)
+            if win.IsEmpty():
+                del panel
+                continue
+            
+            win.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnDataItemActivated)
+
+            self.layerPage[layer] = {'browsePage': panel.GetId()}
+            
+            label = _("Table")
+            if not self.editable:
+                label += _(" (readonly)")
+            self.browsePage.AddPage(page = panel, text = " %d / %s %s" % \
+                                        (layer, label, self.mapDBInfo.layers[layer]['table']))
+            
+            pageSizer = wx.BoxSizer(wx.VERTICAL)
+
+            # attribute data            
+            sqlBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                                  label = " %s " % _("SQL Query"))
+
+            sqlSizer = wx.StaticBoxSizer(sqlBox, wx.VERTICAL)
+
+            win.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnDataRightUp) #wxMSW
+            win.Bind(wx.EVT_RIGHT_UP,            self.OnDataRightUp) #wxGTK
+            if UserSettings.Get(group = 'atm', key = 'leftDbClick', subkey = 'selection') == 0:
+                win.Bind(wx.EVT_LEFT_DCLICK, self.OnDataItemEdit)
+                win.Bind(wx.EVT_COMMAND_LEFT_DCLICK, self.OnDataItemEdit)
+            else:
+                win.Bind(wx.EVT_LEFT_DCLICK, self.OnDataDrawSelected)
+                win.Bind(wx.EVT_COMMAND_LEFT_DCLICK, self.OnDataDrawSelected)
+            
+            listSizer.Add(item = win, proportion = 1,
+                          flag = wx.EXPAND | wx.ALL,
+                          border = 3)
+
+            # sql statement box
+            btnApply = wx.Button(parent = panel, id = wx.ID_APPLY)
+            btnApply.SetToolTipString(_("Apply SELECT statement and reload data records"))
+            btnApply.Bind(wx.EVT_BUTTON, self.OnApplySqlStatement)
+            btnSqlBuilder = wx.Button(parent = panel, id = wx.ID_ANY, label = _("SQL Builder"))
+            btnSqlBuilder.Bind(wx.EVT_BUTTON, self.OnBuilder)
+
+            sqlSimple = wx.RadioButton(parent = panel, id = wx.ID_ANY,
+                                       label = _("Simple"))
+            sqlSimple.SetValue(True)
+            sqlAdvanced = wx.RadioButton(parent = panel, id = wx.ID_ANY,
+                                         label = _("Advanced"))
+            sqlSimple.Bind(wx.EVT_RADIOBUTTON,   self.OnChangeSql)
+            sqlAdvanced.Bind(wx.EVT_RADIOBUTTON, self.OnChangeSql)
+
+            sqlWhereColumn = wx.ComboBox(parent = panel, id = wx.ID_ANY,
+                                         size = (100,-1),
+                                         style = wx.CB_SIMPLE | wx.CB_READONLY,
+                                         choices = self.mapDBInfo.GetColumns(self.mapDBInfo.layers[layer]['table']))
+            sqlWhereColumn.SetSelection(0)
+            sqlWhereCond = wx.Choice(parent = panel, id = wx.ID_ANY,
+                                     size = (55,-1),
+                                     choices = ['=', '!=', '<', '<=', '>', '>='])
+            sqlWhereValue = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = "",
+                                        style = wx.TE_PROCESS_ENTER)
+            sqlWhereValue.SetToolTipString(_("Example: %s") % "MULTILANE = 'no' AND OBJECTID < 10")
+
+            sqlStatement = wx.TextCtrl(parent = panel, id = wx.ID_ANY,
+                                       value = "SELECT * FROM %s" % \
+                                           self.mapDBInfo.layers[layer]['table'],
+                                       style = wx.TE_PROCESS_ENTER)
+            sqlStatement.SetToolTipString(_("Example: %s") % "SELECT * FROM roadsmajor WHERE MULTILANE = 'no' AND OBJECTID < 10")
+            sqlWhereValue.Bind(wx.EVT_TEXT_ENTER, self.OnApplySqlStatement)
+            sqlStatement.Bind(wx.EVT_TEXT_ENTER, self.OnApplySqlStatement)
+
+            sqlLabel = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                     label = "SELECT * FROM %s WHERE " % \
+                                         self.mapDBInfo.layers[layer]['table'])
+            label_query = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                        label = "")
+
+            sqlFlexSizer = wx.FlexGridSizer (cols = 3, hgap = 5, vgap = 5)
+            sqlFlexSizer.AddGrowableCol(1)
+
+            sqlFlexSizer.Add(item = sqlSimple,
+                             flag = wx.ALIGN_CENTER_VERTICAL)
+            sqlSimpleSizer = wx.BoxSizer(wx.HORIZONTAL)
+            sqlSimpleSizer.Add(item = sqlLabel,
+                               flag = wx.ALIGN_CENTER_VERTICAL)
+            sqlSimpleSizer.Add(item = sqlWhereColumn,
+                               flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+            sqlSimpleSizer.Add(item = sqlWhereCond,
+                               flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+                               border = 3)
+            sqlSimpleSizer.Add(item = sqlWhereValue, proportion = 1,
+                               flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL)
+            sqlFlexSizer.Add(item = sqlSimpleSizer,
+                             flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
+            sqlFlexSizer.Add(item = btnApply,
+                             flag = wx.ALIGN_RIGHT)
+            sqlFlexSizer.Add(item = sqlAdvanced,
+                             flag = wx.ALIGN_CENTER_VERTICAL)
+            sqlFlexSizer.Add(item = sqlStatement,
+                             flag = wx.EXPAND)
+            sqlFlexSizer.Add(item = btnSqlBuilder,
+                             flag = wx.ALIGN_RIGHT)
+
+            sqlSizer.Add(item = sqlFlexSizer,
+                         flag = wx.ALL | wx.EXPAND,
+                         border = 3)
+
+            pageSizer.Add(item = listSizer,
+                          proportion = 1,
+                          flag = wx.ALL | wx.EXPAND,
+                          border = 5)
+
+            pageSizer.Add(item = sqlSizer,
+                          proportion = 0,
+                          flag = wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.EXPAND,
+                          border = 5)
+
+            panel.SetSizer(pageSizer)
+
+            self.layerPage[layer]['data']      = win.GetId()
+            self.layerPage[layer]['simple']    = sqlSimple.GetId()
+            self.layerPage[layer]['advanced']  = sqlAdvanced.GetId()
+            self.layerPage[layer]['whereColumn'] = sqlWhereColumn.GetId()
+            self.layerPage[layer]['whereOperator'] = sqlWhereCond.GetId()
+            self.layerPage[layer]['where']     = sqlWhereValue.GetId()
+            self.layerPage[layer]['builder']   = btnSqlBuilder.GetId()
+            self.layerPage[layer]['statement'] = sqlStatement.GetId()
+
+
+        self.browsePage.SetSelection(0) # select first layer
+        try:
+            self.layer = self.mapDBInfo.layers.keys()[0]
+            self.OnChangeSql(None)
+            self.log.write(_("Number of loaded records: %d") % \
+                           self.FindWindowById(self.layerPage[self.layer]['data']).GetItemCount())
+        except (IndexError, KeyError):
+            self.layer = None
+        
+    def _createManageTablePage(self, onlyLayer = -1):
+        """!Create manage page (create/link and alter tables)"""
+        for layer in self.mapDBInfo.layers.keys():
+            if onlyLayer > 0 and layer != onlyLayer:
+                continue
+            
+            if not layer in self.layerPage:
+                continue
+            
+            panel = wx.Panel(parent = self.manageTablePage, id = wx.ID_ANY)
+            self.layerPage[layer]['tablePage'] = panel.GetId()
+            label = _("Table")
+            if not self.editable:
+                label += _(" (readonly)")
+            self.manageTablePage.AddPage(page = panel,
+                                         text = " %d / %s %s" % (layer, label,
+                                                               self.mapDBInfo.layers[layer]['table']))
+            
+            pageSizer = wx.BoxSizer(wx.VERTICAL)
+            
+            #
+            # dbInfo
+            #
+            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),
+                        proportion = 1,
+                        flag = wx.EXPAND | wx.ALL,
+                        border = 3)
+            
+            #
+            # table description
+            #
+            table = self.mapDBInfo.layers[layer]['table']
+            tableBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                                    label = " %s " % _("Table <%s> - right-click to delete column(s)") % table)
+            
+            tableSizer = wx.StaticBoxSizer(tableBox, wx.VERTICAL)
+            
+            tlist = self._createTableDesc(panel, table)
+            tlist.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnTableRightUp) #wxMSW
+            tlist.Bind(wx.EVT_RIGHT_UP,            self.OnTableRightUp) #wxGTK
+            self.layerPage[layer]['tableData'] = tlist.GetId()
+            
+            # manage columns (add)
+            addBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                                  label = " %s " % _("Add column"))
+            addSizer = wx.StaticBoxSizer(addBox, wx.HORIZONTAL)
+            
+            column = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = '',
+                                 size = (150, -1), style = wx.TE_PROCESS_ENTER)
+            column.Bind(wx.EVT_TEXT,       self.OnTableAddColumnName)
+            column.Bind(wx.EVT_TEXT_ENTER, self.OnTableItemAdd)
+            self.layerPage[layer]['addColName'] = column.GetId()
+            addSizer.Add(item =  wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Column")),
+                         flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+                         border = 5)
+            addSizer.Add(item = column, proportion = 1,
+                         flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+                         border = 5)
+            
+            ctype = wx.Choice (parent = panel, id = wx.ID_ANY,
+                               choices = ["integer",
+                                          "double",
+                                          "varchar",
+                                          "date"]) # FIXME
+            ctype.SetSelection(0)
+            ctype.Bind(wx.EVT_CHOICE, self.OnTableChangeType)
+            self.layerPage[layer]['addColType'] = ctype.GetId()
+            addSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Type")), 
+                         flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+                         border = 5)
+            addSizer.Add(item = ctype,
+                         flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+                         border = 5)
+            
+            length = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                                 initial = 250,
+                                 min = 1, max = 1e6)
+            length.Enable(False)
+            self.layerPage[layer]['addColLength'] = length.GetId()
+            addSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Length")),
+                         flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+                         border = 5)
+            addSizer.Add(item = length,
+                         flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+                         border = 5)
+            
+            btnAddCol = wx.Button(parent = panel, id = wx.ID_ADD)
+            btnAddCol.Bind(wx.EVT_BUTTON, self.OnTableItemAdd)
+            btnAddCol.Enable(False)
+            self.layerPage[layer]['addColButton'] = btnAddCol.GetId()
+            addSizer.Add(item = btnAddCol, flag = wx.ALL | wx.ALIGN_RIGHT | wx.EXPAND,
+                         border = 3)
+            
+            # manage columns (rename)
+            renameBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                                     label = " %s " % _("Rename column"))
+            renameSizer = wx.StaticBoxSizer(renameBox, wx.HORIZONTAL)
+            
+            column = wx.ComboBox(parent = panel, id = wx.ID_ANY, size = (150, -1),
+                                 style = wx.CB_SIMPLE | wx.CB_READONLY,
+                                 choices = self.mapDBInfo.GetColumns(table))
+            column.SetSelection(0)
+            self.layerPage[layer]['renameCol'] = column.GetId()
+            renameSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Column")),
+                            flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+                            border = 5)
+            renameSizer.Add(item = column, proportion = 1,
+                            flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+                            border = 5)
+            
+            columnTo = wx.TextCtrl(parent = panel, id = wx.ID_ANY, value = '',
+                                   size = (150, -1), style = wx.TE_PROCESS_ENTER)
+            columnTo.Bind(wx.EVT_TEXT,       self.OnTableRenameColumnName)
+            columnTo.Bind(wx.EVT_TEXT_ENTER, self.OnTableItemChange)
+            self.layerPage[layer]['renameColTo'] = columnTo.GetId()
+            renameSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("To")),
+                            flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+                            border = 5)
+            renameSizer.Add(item = columnTo, proportion = 1,
+                            flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+                            border = 5)
+            
+            btnRenameCol = wx.Button(parent = panel, id = wx.ID_ANY, label = _("&Rename"))
+            btnRenameCol.Bind(wx.EVT_BUTTON, self.OnTableItemChange)
+            btnRenameCol.Enable(False)
+            self.layerPage[layer]['renameColButton'] = btnRenameCol.GetId()
+            renameSizer.Add(item = btnRenameCol, flag = wx.ALL | wx.ALIGN_RIGHT | wx.EXPAND,
+                            border = 3)
+            
+            tableSizer.Add(item = tlist,
+                           flag = wx.ALL | wx.EXPAND,
+                           proportion = 1,
+                           border = 3)
+            
+            pageSizer.Add(item=dbSizer,
+                          flag = wx.ALL | wx.EXPAND,
+                          proportion = 0,
+                          border = 3)
+            
+            pageSizer.Add(item = tableSizer,
+                          flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
+                          proportion = 1,
+                          border = 3)
+            
+            pageSizer.Add(item = addSizer,
+                          flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
+                          proportion = 0,
+                          border = 3)
+            pageSizer.Add(item = renameSizer,
+                          flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
+                          proportion = 0,
+                          border = 3)
+            
+            panel.SetSizer(pageSizer)
+        
+        self.manageTablePage.SetSelection(0) # select first layer
+        try:
+            self.layer = self.mapDBInfo.layers.keys()[0]
+        except IndexError:
+            self.layer = None
+        
+    def _createTableDesc(self, parent, table):
+        """!Create list with table description"""
+        tlist = TableListCtrl(parent = parent, id = wx.ID_ANY,
+                             table = self.mapDBInfo.tables[table],
+                             columns = self.mapDBInfo.GetColumns(table))
+        tlist.Populate()
+        # sorter
+        # itemDataMap = list.Populate()
+        # listmix.ColumnSorterMixin.__init__(self, 2)
+
+        return tlist
+
+    def _createManageLayerPage(self):
+        """!Create manage page"""
+        splitterWin = wx.SplitterWindow(parent = self.manageLayerPage, id = wx.ID_ANY)
+        splitterWin.SetMinimumPaneSize(100)
+        
+        label = _("Layers of vector map")
+        if not self.editable:
+            label += _(" (readonly)")
+        self.manageLayerPage.AddPage(page = splitterWin,
+                                     text = label) # dummy page
+        
+        #
+        # list of layers
+        #
+        panelList = wx.Panel(parent = splitterWin, id = wx.ID_ANY)
+
+        panelListSizer  = wx.BoxSizer(wx.VERTICAL)
+        layerBox = wx.StaticBox(parent = panelList, id = wx.ID_ANY,
+                                label = " %s " % _("List of layers"))
+        layerSizer = wx.StaticBoxSizer(layerBox, wx.VERTICAL)
+
+        self.layerList = self._createLayerDesc(panelList)
+        self.layerList.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnLayerRightUp) #wxMSW
+        self.layerList.Bind(wx.EVT_RIGHT_UP,            self.OnLayerRightUp) #wxGTK
+        
+        layerSizer.Add(item = self.layerList,
+                       flag = wx.ALL | wx.EXPAND,
+                       proportion = 1,
+                       border = 3)
+
+        panelListSizer.Add(item = layerSizer,
+                           flag = wx.ALL | wx.EXPAND,
+                           proportion = 1,
+                           border = 3)
+
+        panelList.SetSizer(panelListSizer)
+
+        #
+        # manage part
+        #
+        panelManage = wx.Panel(parent = splitterWin, id = wx.ID_ANY)
+         
+        manageSizer = wx.BoxSizer(wx.VERTICAL)
+
+        self.manageLayerBook = LayerBook(parent = panelManage, id = wx.ID_ANY,
+                                         parentDialog = self)
+
+        manageSizer.Add(item = self.manageLayerBook,
+                        proportion = 1,
+                        flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
+                        border = 5)
+
+        panelManage.SetSizer(manageSizer)
+        splitterWin.SplitHorizontally(panelList, panelManage, 100) 
+        splitterWin.Fit()
+
+    def _createLayerDesc(self, parent):
+        """!Create list of linked layers"""
+        tlist = LayerListCtrl(parent = parent, id = wx.ID_ANY,
+                             layers = self.mapDBInfo.layers)
+        
+        tlist.Populate()
+        # sorter
+        # itemDataMap = list.Populate()
+        # listmix.ColumnSorterMixin.__init__(self, 2)
+
+        return tlist
+
+    def _layout(self):
+        """!Do layout"""
+        # frame body
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+
+        # buttons
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(item = self.btnReload, proportion = 1,
+                     flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
+        btnSizer.Add(item = self.btnQuit, proportion = 1,
+                     flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
+
+        mainSizer.Add(item = self.notebook, proportion = 1, flag = wx.EXPAND)
+        mainSizer.Add(item = btnSizer, flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+
+        self.panel.SetAutoLayout(True)
+        self.panel.SetSizer(mainSizer)
+        mainSizer.Fit(self.panel)
+        self.Layout()
+        
+    def OnDataRightUp(self, event):
+        """!Table description area, context menu"""
+        if not hasattr(self, "popupDataID1"):
+            self.popupDataID1 = wx.NewId()
+            self.popupDataID2 = wx.NewId()
+            self.popupDataID3 = wx.NewId()
+            self.popupDataID4 = wx.NewId()
+            self.popupDataID5 = wx.NewId()
+            self.popupDataID6 = wx.NewId()
+            self.popupDataID7 = wx.NewId()
+            self.popupDataID8 = wx.NewId()
+            self.popupDataID9 = wx.NewId()
+            self.popupDataID10 = wx.NewId()
+            self.popupDataID11 = wx.NewId()
+
+            self.Bind(wx.EVT_MENU, self.OnDataItemEdit,       id = self.popupDataID1)
+            self.Bind(wx.EVT_MENU, self.OnDataItemAdd,        id = self.popupDataID2)
+            self.Bind(wx.EVT_MENU, self.OnDataItemDelete,     id = self.popupDataID3)
+            self.Bind(wx.EVT_MENU, self.OnDataItemDeleteAll,  id = self.popupDataID4)
+            self.Bind(wx.EVT_MENU, self.OnDataSelectAll,      id = self.popupDataID5)
+            self.Bind(wx.EVT_MENU, self.OnDataSelectNone,     id = self.popupDataID6)
+            self.Bind(wx.EVT_MENU, self.OnDataDrawSelected,   id = self.popupDataID7)
+            self.Bind(wx.EVT_MENU, self.OnDataDrawSelectedZoom, id = self.popupDataID8)
+            self.Bind(wx.EVT_MENU, self.OnExtractSelected,    id = self.popupDataID9)
+            self.Bind(wx.EVT_MENU, self.OnDeleteSelected,     id = self.popupDataID11)
+            self.Bind(wx.EVT_MENU, self.OnDataReload,         id = self.popupDataID10)
+
+        tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
+        # generate popup-menu
+        menu = wx.Menu()
+        menu.Append(self.popupDataID1, _("Edit selected record"))
+        selected = tlist.GetFirstSelected()
+        if not self.editable or selected == -1 or tlist.GetNextSelected(selected) != -1:
+            menu.Enable(self.popupDataID1, False)
+        menu.Append(self.popupDataID2, _("Insert new record"))
+        menu.Append(self.popupDataID3, _("Delete selected record(s)"))
+        menu.Append(self.popupDataID4, _("Delete all records"))
+        if not self.editable:
+            menu.Enable(self.popupDataID2, False)
+            menu.Enable(self.popupDataID3, False)
+            menu.Enable(self.popupDataID4, False)
+        menu.AppendSeparator()
+        menu.Append(self.popupDataID5, _("Select all"))
+        menu.Append(self.popupDataID6, _("Deselect all"))
+        menu.AppendSeparator()
+        menu.Append(self.popupDataID7, _("Highlight selected features"))
+        menu.Append(self.popupDataID8, _("Highlight selected features and zoom"))
+        if not self.map or len(tlist.GetSelectedItems()) == 0:
+            menu.Enable(self.popupDataID7, False)
+            menu.Enable(self.popupDataID8, False)
+        menu.Append(self.popupDataID9, _("Extract selected features"))
+        menu.Append(self.popupDataID11, _("Delete selected features"))
+        if not self.editable:
+            menu.Enable(self.popupDataID11, False)
+        if tlist.GetFirstSelected() == -1:
+            menu.Enable(self.popupDataID3, False)
+            menu.Enable(self.popupDataID9, False)
+            menu.Enable(self.popupDataID11, False)
+        menu.AppendSeparator()
+        menu.Append(self.popupDataID10, _("Reload"))
+
+        self.PopupMenu(menu)
+        menu.Destroy()
+
+        # update statusbar
+        self.log.write(_("Number of loaded records: %d") % \
+                           tlist.GetItemCount())
+
+    def OnDataItemDelete(self, event):
+        """!Delete selected item(s) from the tlist (layer/category pair)"""
+        dlist = self.FindWindowById(self.layerPage[self.layer]['data'])
+        item = dlist.GetFirstSelected()
+        
+        table    = self.mapDBInfo.layers[self.layer]["table"]
+        key      = self.mapDBInfo.layers[self.layer]["key"]
+        
+        indeces = []
+        # collect SQL statements
+        while item != -1:
+            index = dlist.itemIndexMap[item]
+            indeces.append(index)
+            
+            cat = dlist.itemCatsMap[index]
+            
+            self.listOfSQLStatements.append('DELETE FROM %s WHERE %s=%d' % \
+                                                (table, key, cat))
+            
+            item = dlist.GetNextSelected(item)
+        
+        if UserSettings.Get(group = 'atm', key = 'askOnDeleteRec', subkey = 'enabled'):
+            deleteDialog = wx.MessageBox(parent = self,
+                                         message = _("Selected data records (%d) will be 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:
+                self.listOfSQLStatements = []
+                return False
+        
+        # restore maps
+        i = 0
+        indexTemp = copy.copy(dlist.itemIndexMap)
+        dlist.itemIndexMap = []
+        dataTemp = copy.deepcopy(dlist.itemDataMap)
+        dlist.itemDataMap = {}
+        catsTemp = copy.deepcopy(dlist.itemCatsMap)
+        dlist.itemCatsMap = {}
+        
+        i = 0
+        for index in indexTemp:
+            if index in indeces:
+                continue
+            dlist.itemIndexMap.append(i)
+            dlist.itemDataMap[i] = dataTemp[index]
+            dlist.itemCatsMap[i] = catsTemp[index]
+            
+            i += 1
+            
+        dlist.SetItemCount(len(dlist.itemIndexMap))
+        
+        # deselect items
+        item = dlist.GetFirstSelected()
+        while item != -1:
+            dlist.SetItemState(item, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED)
+            item = dlist.GetNextSelected(item)
+        
+        # submit SQL statements
+        self.ApplyCommands()
+        
+        return True
+
+    def OnDataItemDeleteAll(self, event):
+        """!Delete all items from the list"""
+        dlist = self.FindWindowById(self.layerPage[self.layer]['data'])
+        if UserSettings.Get(group = 'atm', key = 'askOnDeleteRec', subkey = 'enabled'):
+            deleteDialog = wx.MessageBox(parent = self,
+                                         message = _("All data records (%d) will be permanently deleted "
+                                                   "from table. Do you want to delete them?") % \
+                                             (len(dlist.itemIndexMap)),
+                                         caption = _("Delete records"),
+                                         style = wx.YES_NO | wx.CENTRE)
+            if deleteDialog != wx.YES:
+                return
+
+        dlist.DeleteAllItems()
+        dlist.itemDataMap  = {}
+        dlist.itemIndexMap = []
+        dlist.SetItemCount(0)
+
+        table = self.mapDBInfo.layers[self.layer]["table"]
+        self.listOfSQLStatements.append('DELETE FROM %s' % table)
+
+        self.ApplyCommands()
+        
+        event.Skip()
+
+    def _drawSelected(self, zoom):
+        """!Highlight selected features"""
+        if not self.map or not self.mapdisplay:
+            return
+        
+        tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
+        cats = map(int, tlist.GetSelectedItems())
+
+        digitToolbar = self.mapdisplay.toolbars['vdigit']
+        if digitToolbar and digitToolbar.GetLayer() and \
+                digitToolbar.GetLayer().GetName() == self.vectorName:
+            display = self.mapdisplay.GetMapWindow().GetDisplay()
+            display.SetSelected(cats, layer = self.layer)
+            if zoom:
+                n, s, w, e = display.GetRegionSelected()
+                self.mapdisplay.Map.GetRegion(n = n, s = s, w = w, e = e,
+                                              update = True)
+        else:
+            # add map layer with higlighted vector features
+            self.AddQueryMapLayer() # -> self.qlayer
+
+            # set opacity based on queried layer
+            if self.parent and self.parent.GetName() == "LayerManager" and \
+                    self.treeItem:
+                maptree = self.parent.curr_page.maptree
+                opacity = maptree.GetPyData(self.treeItem)[0]['maplayer'].GetOpacity(float = True)
+                self.qlayer.SetOpacity(opacity)
+            if zoom:
+                keyColumn = self.mapDBInfo.layers[self.layer]['key']
+                where = ''
+                for range in ListOfCatsToRange(cats).split(','):
+                    if '-' in range:
+                        min, max = range.split('-')
+                        where += '%s >= %d and %s <= %d or ' % \
+                            (keyColumn, int(min),
+                             keyColumn, int(max))
+                    else:
+                        where += '%s = %d or ' % (keyColumn, int(range))
+                where = where.rstrip('or ')
+                
+                select = 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 select.splitlines():
+                    key, value = line.split('=')
+                    region[key.strip()] = float(value.strip())
+                
+                self.mapdisplay.Map.GetRegion(n = region['n'], s = region['s'],
+                                              w = region['w'], e = region['e'],
+                                              update = True)
+        
+        if zoom:
+            self.mapdisplay.Map.AdjustRegion()           # adjust resolution
+            self.mapdisplay.Map.AlignExtentFromDisplay() # adjust extent
+            self.mapdisplay.MapWindow.UpdateMap(render = True,  renderVector = True)
+        else:
+            self.mapdisplay.MapWindow.UpdateMap(render = False, renderVector = True)
+        
+    def OnDataDrawSelected(self, event):
+        """!Reload table description"""
+        self._drawSelected(zoom = False)
+        event.Skip()
+
+    def OnDataDrawSelectedZoom(self, event):
+        self._drawSelected(zoom = True)
+        event.Skip()
+        
+    def OnDataItemAdd(self, event):
+        """!Add new record to the attribute table"""
+        tlist      = 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 = []
+
+        # collect names of all visible columns
+        columnName = []
+        for i in range(tlist.GetColumnCount()): 
+            columnName.append(tlist.GetColumn(i).GetText())
+
+        # maximal category number
+        if len(tlist.itemCatsMap.values()) > 0:
+            maxCat = max(tlist.itemCatsMap.values())
+        else:
+            maxCat = 0 # starting category '1'
+        
+        # key column must be always presented
+        if keyColumn not in columnName:
+            columnName.insert(0, keyColumn) # insert key column on first position
+            data.append((keyColumn, str(maxCat + 1)))
+            missingKey = True
+        else:
+            missingKey = False
+            
+        # add other visible columns
+        colIdx = 0
+        keyId = -1
+        for col in columnName:
+            ctype = self.mapDBInfo.tables[table][col]['ctype']
+            ctypeStr = self.mapDBInfo.tables[table][col]['type']
+            if col == keyColumn: # key 
+                if missingKey is False: 
+                    data.append((col, ctype, ctypeStr, str(maxCat + 1)))
+                    keyId = colIdx
+            else:
+                data.append((col, ctype, ctypeStr, ''))
+            
+            colIdx += 1
+                
+        dlg = ModifyTableRecord(parent = self,
+                                title = _("Insert new record"),
+                                data = data, keyEditable = (keyId, True))
+
+        if dlg.ShowModal() == wx.ID_OK:
+            try: # get category number
+                cat = int(dlg.GetValues(columns = [keyColumn])[0])
+            except:
+                cat = -1
+
+            try:
+                if cat in tlist.itemCatsMap.values():
+                    raise ValueError(_("Record with category number %d "
+                                       "already exists in the table.") % cat)
+
+                values = dlg.GetValues() # values (need to be casted)
+                columnsString = ''
+                valuesString   = ''
+                
+                for i in range(len(values)):
+                    if len(values[i]) == 0: # NULL
+                        if columnName[i] == keyColumn:
+                            raise ValueError(_("Category number (column %s)"
+                                               " is missing.") % keyColumn)
+                        else:
+                            continue
+
+                    try:
+                        if tlist.columns[columnName[i]]['ctype'] == int:
+                            # values[i] is stored as text. 
+                            value = float(values[i])
+                        else:
+                            value = values[i]
+                        values[i] = tlist.columns[columnName[i]]['ctype'] (value)
+
+                    except:
+                        raise ValueError(_("Value '%(value)s' needs to be entered as %(type)s.") % 
+                                         {'value' : str(values[i]),
+                                          'type' : tlist.columns[columnName[i]]['type']})
+                    columnsString += '%s,' % columnName[i]
+                    if tlist.columns[columnName[i]]['ctype'] == str:
+                        valuesString += "'%s'," % values[i]
+                    else:
+                        valuesString += "%s," % values[i]
+
+            except ValueError, err:
+                GError(parent = self,
+                       message = _("Unable to insert new record.\n%s") % err,
+                       showTraceback = False)
+                self.OnDataItemAdd(event)
+                return
+            
+            # remove category if need 
+            if missingKey is True:
+                del values[0]
+                
+            # add new item to the tlist
+            if len(tlist.itemIndexMap) > 0:
+                index = max(tlist.itemIndexMap) + 1
+            else:
+                index = 0
+            
+            tlist.itemIndexMap.append(index)
+            tlist.itemDataMap[index] = values
+            tlist.itemCatsMap[index] = cat
+            tlist.SetItemCount(tlist.GetItemCount() + 1)
+
+            self.listOfSQLStatements.append('INSERT INTO %s (%s) VALUES(%s)' % \
+                                                (table,
+                                                 columnsString.strip(','),
+                                                 valuesString.strip(',')))
+            self.ApplyCommands()
+            
+    def OnDataItemEdit(self, event):
+        """!Edit selected record of the attribute table"""
+        tlist      = self.FindWindowById(self.layerPage[self.layer]['data'])
+        item      = tlist.GetFirstSelected()
+        if item == -1:
+            return
+
+        table     = self.mapDBInfo.layers[self.layer]['table']
+        keyColumn = self.mapDBInfo.layers[self.layer]['key']
+        cat       = tlist.itemCatsMap[tlist.itemIndexMap[item]]
+
+        # (column name, value)
+        data = []
+
+        # collect names of all visible columns
+        columnName = []
+        for i in range(tlist.GetColumnCount()): 
+            columnName.append(tlist.GetColumn(i).GetText())
+
+
+        # key column must be always presented
+        if keyColumn not in columnName:
+            columnName.insert(0, keyColumn) # insert key column on first position
+            data.append((keyColumn, str(cat)))
+            keyId = 0
+            missingKey = True
+        else:
+            missingKey = False
+            
+        # add other visible columns
+        for i in range(len(columnName)):
+            ctype = self.mapDBInfo.tables[table][columnName[i]]['ctype']
+            ctypeStr = self.mapDBInfo.tables[table][columnName[i]]['type']
+            if columnName[i] == keyColumn: # key 
+                if missingKey is False: 
+                    data.append((columnName[i], ctype, ctypeStr, str(cat)))
+                    keyId = i
+            else:
+                if missingKey is True:
+                    value = tlist.GetItem(item, i-1).GetText()
+                else:
+                    value = tlist.GetItem(item, i).GetText()
+                data.append((columnName[i], ctype, ctypeStr, value))
+
+        dlg = ModifyTableRecord(parent = self, 
+                                title = _("Update existing record"),
+                                data = data, keyEditable = (keyId, False))
+
+        if dlg.ShowModal() == wx.ID_OK:
+            values = dlg.GetValues() # string
+            updateString = ''
+            try:
+                for i in range(len(values)): 
+                    if i == keyId: # skip key column
+                        continue
+                    if tlist.GetItem(item, i).GetText() != values[i]:
+                        if len(values[i]) > 0:
+                            try:
+                                if missingKey is True:
+                                    idx = i - 1
+                                else:
+                                    idx = i
+                                if tlist.columns[columnName[i]]['ctype'] != types.StringType:
+                                    if tlist.columns[columnName[i]]['ctype'] == int:
+                                        value = float(values[i])
+                                    else:
+                                        value = values[i]
+                                    tlist.itemDataMap[item][idx] = \
+                                        tlist.columns[columnName[i]]['ctype'] (value)
+                                else:
+                                    tlist.itemDataMap[item][idx] = values[i]
+                            except:
+                                raise ValueError(_("Value '%(value)s' needs to be entered as %(type)s.") % \
+                                                     {'value' : str(values[i]),
+                                                      'type' : tlist.columns[columnName[i]]['type']})
+
+                            if tlist.columns[columnName[i]]['ctype'] == str:
+                                updateString += "%s='%s'," % (columnName[i], values[i])
+                            else:
+                                updateString += "%s=%s," % (columnName[i], values[i])
+                        else: # NULL
+                            updateString += "%s=NULL," % (columnName[i])
+                            
+            except ValueError, err:
+                GError(parent = self,
+                       message = _("Unable to update existing record.\n%s") % err,
+                       showTraceback = False)
+                self.OnDataItemEdit(event)
+                return
+            
+            if len(updateString) > 0:
+                self.listOfSQLStatements.append('UPDATE %s SET %s WHERE %s=%d' % \
+                                                    (table, updateString.strip(','),
+                                                     keyColumn, cat))
+                self.ApplyCommands()
+
+            tlist.Update(self.mapDBInfo)
+                        
+    def OnDataReload(self, event):
+        """!Reload tlist of records"""
+        self.OnApplySqlStatement(None)
+        self.listOfSQLStatements = []
+
+    def OnDataSelectAll(self, event):
+        """!Select all items"""
+        tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
+        item = -1
+
+        while True:
+            item = tlist.GetNextItem(item)
+            if item == -1:
+                break
+            tlist.SetItemState(item, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
+
+        event.Skip()
+
+    def OnDataSelectNone(self, event):
+        """!Deselect items"""
+        tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
+        item = -1
+
+        while True:
+            item = tlist.GetNextItem(item, wx.LIST_STATE_SELECTED)
+            if item == -1:
+                break
+            tlist.SetItemState(item, 0, wx.LIST_STATE_SELECTED | wx.LIST_STATE_FOCUSED)
+
+        event.Skip()
+
+
+    def OnTableChangeType(self, event):
+        """!Data type for new column changed. Enable or disable
+        data length widget"""
+        win = self.FindWindowById(self.layerPage[self.layer]['addColLength'])
+        if event.GetString() == "varchar":
+            win.Enable(True)
+        else:
+            win.Enable(False)
+
+    def OnTableRenameColumnName(self, event):
+        """!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'])
+        if len(col.GetValue()) > 0 and len(colTo.GetValue()) > 0:
+            btn.Enable(True)
+        else:
+            btn.Enable(False)
+
+        event.Skip()
+
+    def OnTableAddColumnName(self, event):
+        """!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)
+        else:
+            btn.Enable(False)
+
+        event.Skip()
+
+    def OnTableItemChange(self, event):
+        """!Rename column in the table"""
+        tlist   = 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()
+
+        table = self.mapDBInfo.layers[self.layer]["table"]
+
+        if not name or not nameTo:
+            GError(parent = self,
+                   message = _("Unable to rename column. "
+                               "No column name defined."))
+            return
+        else:
+            item = tlist.FindItem(start = -1, str = name)
+            if item > -1:
+                if tlist.FindItem(start = -1, str = nameTo) > -1:
+                    GError(parent = self,
+                           message = _("Unable to rename column <%(column)s> to "
+                                       "<%(columnTo)s>. Column already exists "
+                                       "in the table <%(table)s>.") % \
+                               {'column' : name, 'columnTo' : nameTo,
+                                'table' : table})
+                    return
+                else:
+                    tlist.SetItemText(item, nameTo)
+
+                    self.listOfCommands.append(('v.db.renamecol',
+                                                { 'map'    : self.vectorName,
+                                                  'layer'  : self.layer,
+                                                  'column' : '%s,%s' % (name, nameTo) }
+                                                ))
+            else:
+                GError(parent = self,
+                       message = _("Unable to rename column. "
+                                   "Column <%(column)s> doesn't exist in the table <%(table)s>.") % 
+                       {'column' : name, 'table' : table})
+                return
+            
+        # apply changes
+        self.ApplyCommands()
+
+        # update widgets
+        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
+        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
+        self.FindWindowById(self.layerPage[self.layer]['renameColTo']).SetValue('')
+
+        event.Skip()
+
+    def OnTableRightUp(self, event):
+        """!Table description area, context menu"""
+        if not hasattr(self, "popupTableID"):
+            self.popupTableID1 = wx.NewId()
+            self.popupTableID2 = wx.NewId()
+            self.popupTableID3 = wx.NewId()
+            self.Bind(wx.EVT_MENU, self.OnTableItemDelete,    id = self.popupTableID1)
+            self.Bind(wx.EVT_MENU, self.OnTableItemDeleteAll, id = self.popupTableID2)
+            self.Bind(wx.EVT_MENU, self.OnTableReload,        id = self.popupTableID3)
+
+        # generate popup-menu
+        menu = wx.Menu()
+        menu.Append(self.popupTableID1, _("Drop selected column"))
+        if self.FindWindowById(self.layerPage[self.layer]['tableData']).GetFirstSelected() == -1:
+            menu.Enable(self.popupTableID1, False)
+        menu.Append(self.popupTableID2, _("Drop all columns"))
+        menu.AppendSeparator()
+        menu.Append(self.popupTableID3, _("Reload"))
+
+        self.PopupMenu(menu)
+        menu.Destroy()
+
+    def OnTableItemDelete(self, event):
+        """!Delete selected item(s) from the list"""
+        tlist = self.FindWindowById(self.layerPage[self.layer]['tableData'])
+        
+        item = tlist.GetFirstSelected()
+        
+        if UserSettings.Get(group = 'atm', key = 'askOnDeleteRec', subkey = 'enabled'):
+            deleteDialog = wx.MessageBox(parent = self,
+                                         message = _("Selected column '%s' will PERMANENTLY removed "
+                                                   "from table. Do you want to drop the column?") % \
+                                             (tlist.GetItemText(item)),
+                                         caption = _("Drop column(s)"),
+                                         style = wx.YES_NO | wx.CENTRE)
+            if deleteDialog != wx.YES:
+                return False
+        
+        while item != -1:
+            self.listOfCommands.append(('v.db.dropcol',
+                                        { 'map' : self.vectorName,
+                                          'layer' : self.layer,
+                                          'column' : tlist.GetItemText(item) }
+                                        ))
+            tlist.DeleteItem(item)
+            item = tlist.GetFirstSelected()
+        
+        # apply changes
+        self.ApplyCommands()
+        
+        # update widgets
+        table = self.mapDBInfo.layers[self.layer]['table']
+        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
+        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
+        
+        event.Skip()
+
+    def OnTableItemDeleteAll(self, event):
+        """!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']
+        if keyColumn in cols:
+            cols.remove(keyColumn)
+        
+        if UserSettings.Get(group = 'atm', key = 'askOnDeleteRec', subkey = 'enabled'):
+            deleteDialog = wx.MessageBox(parent = self,
+                                         message = _("Selected columns\n%s\nwill PERMANENTLY removed "
+                                                   "from table. Do you want to drop the columns?") % \
+                                             ('\n'.join(cols)),
+                                         caption = _("Drop column(s)"),
+                                         style = wx.YES_NO | wx.CENTRE)
+            if deleteDialog != wx.YES:
+                return False
+        
+        for col in cols:
+            self.listOfCommands.append(('v.db.dropcol',
+                                        { 'map' : self.vectorName,
+                                          'layer' : self.layer,
+                                          'column' : col }
+                                        ))
+        self.FindWindowById(self.layerPage[self.layer]['tableData']).DeleteAllItems()
+
+        # apply changes
+        self.ApplyCommands()
+
+        # update widgets
+        table = self.mapDBInfo.layers[self.layer]['table']
+        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
+        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
+
+        event.Skip()
+
+    def OnTableReload(self, event = None):
+        """!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"""
+	table = self.mapDBInfo.layers[self.layer]['table']
+        name = self.FindWindowById(self.layerPage[self.layer]['addColName']).GetValue()
+        
+        if not name:
+            GError(parent = self,
+                   message = _("Unable to add column to the table. "
+                               "No column name defined."))
+            return
+        
+        ctype = self.FindWindowById(self.layerPage[self.layer]['addColType']). \
+            GetStringSelection()
+        
+        # cast type if needed
+        if ctype == 'double':
+            ctype = 'double precision'
+        if ctype == 'varchar':
+            length = int(self.FindWindowById(self.layerPage[self.layer]['addColLength']). \
+                             GetValue())
+        else:
+            length = '' # FIXME
+        
+        # add item to the list of table columns
+        tlist = self.FindWindowById(self.layerPage[self.layer]['tableData'])
+        # check for duplicate items
+        if tlist.FindItem(start = -1, str = name) > -1:
+            GError(parent = self,
+                   message = _("Column <%(column)s> already exists in table <%(table)s>.") % \
+                       {'column' : name, 'table' : self.mapDBInfo.layers[self.layer]["table"]})
+            return
+        index = tlist.InsertStringItem(sys.maxint, str(name))
+        tlist.SetStringItem(index, 0, str(name))
+        tlist.SetStringItem(index, 1, str(ctype))
+        tlist.SetStringItem(index, 2, str(length))
+        
+        # add v.db.addcol command to the list
+        if ctype == 'varchar':
+            ctype += ' (%d)' % length
+        self.listOfCommands.append(('v.db.addcol',
+                                    { 'map'     : self.vectorName,
+                                      'layer'   : self.layer,
+                                      'columns' : '%s %s' % (name, ctype) }
+                                    ))
+        # apply changes
+        self.ApplyCommands()
+        
+        # update widgets
+        self.FindWindowById(self.layerPage[self.layer]['addColName']).SetValue('')
+	self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetItems(self.mapDBInfo.GetColumns(table))
+        self.FindWindowById(self.layerPage[self.layer]['renameCol']).SetSelection(0)
+        
+        event.Skip()
+        
+    def OnLayerPageChanged(self, event):
+        """!Layer tab changed"""
+        pageNum = event.GetSelection()
+        self.layer = self.mapDBInfo.layers.keys()[pageNum]
+        
+        try:
+            idCol = self.layerPage[self.layer]['whereColumn']
+        except KeyError:
+            idCol = None
+        
+        try:
+            self.OnChangeSql(None)
+            # update statusbar
+            self.log.write(_("Number of loaded records: %d") % \
+                               self.FindWindowById(self.layerPage[self.layer]['data']).\
+                               GetItemCount())
+        except:
+            pass
+        
+        if idCol:
+            winCol = self.FindWindowById(idCol)
+            table = self.mapDBInfo.layers[self.layer]["table"]
+            self.mapDBInfo.GetColumns(table)
+        
+        event.Skip()
+        
+    def OnPageChanged(self, event):
+        try:
+            id = self.layerPage[self.layer]['data']
+        except KeyError:
+            id = None
+        
+        if event.GetSelection() == 0 and id:
+            win = self.FindWindowById(id)
+            if win:
+                self.log.write(_("Number of loaded records: %d") % win.GetItemCount())
+            else:
+                self.log.write("")
+            self.btnReload.Enable()
+        else:
+            self.log.write("")
+            self.btnReload.Enable(False)
+        
+        event.Skip()
+        
+    def OnLayerRightUp(self, event):
+        """!Layer description area, context menu"""
+        pass
+
+    def OnChangeSql(self, event):
+        """!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)
+            self.FindWindowById(self.layerPage[self.layer]['builder']).Enable(False)
+        else:
+            self.FindWindowById(self.layerPage[self.layer]['where']).Enable(False)
+            self.FindWindowById(self.layerPage[self.layer]['statement']).Enable(True)
+            self.FindWindowById(self.layerPage[self.layer]['builder']).Enable(True)
+
+    def ApplyCommands(self):
+        """!Apply changes"""
+        # perform GRASS commands (e.g. v.db.addcol)
+        wx.BeginBusyCursor()
+        
+        if len(self.listOfCommands) > 0:
+            for cmd in self.listOfCommands:
+                RunCommand(prog = cmd[0],
+                           quiet = True,
+                           parent = self,
+                           **cmd[1])
+            
+            self.mapDBInfo = VectorDBInfo(self.vectorName)
+            table = self.mapDBInfo.layers[self.layer]['table']
+
+            # update table description
+            tlist = self.FindWindowById(self.layerPage[self.layer]['tableData'])
+            tlist.Update(table = self.mapDBInfo.tables[table],
+                        columns = self.mapDBInfo.GetColumns(table))
+            self.OnTableReload(None)
+
+            # update data tlist
+            tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
+            tlist.Update(self.mapDBInfo)
+
+            # reset list of commands
+            self.listOfCommands = []
+        
+        # perform SQL non-select statements (e.g. 'delete from table where cat=1')
+        if len(self.listOfSQLStatements) > 0:
+            sqlFile = tempfile.NamedTemporaryFile(mode = "wt")
+            for sql in self.listOfSQLStatements:
+                enc = UserSettings.Get(group = 'atm', key = 'encoding', subkey = 'value')
+                if not enc and 'GRASS_DB_ENCODING' in os.environ:
+                    enc = os.environ['GRASS_DB_ENCODING']
+                if enc:
+                    sqlFile.file.write(sql.encode(enc) + ';')
+                else:
+                    sqlFile.file.write(sql + ';')
+                sqlFile.file.write(os.linesep)
+                sqlFile.file.flush()
+
+            driver   = self.mapDBInfo.layers[self.layer]["driver"]
+            database = self.mapDBInfo.layers[self.layer]["database"]
+            
+            Debug.msg(3, 'AttributeManger.ApplyCommands(): %s' %
+                      ';'.join(["%s" % s for s in self.listOfSQLStatements]))
+            
+            RunCommand('db.execute',
+                       parent = self,
+                       input = sqlFile.name,
+                       driver = driver,
+                       database = database)
+            
+            # reset list of statements
+            self.listOfSQLStatements = []
+            
+        wx.EndBusyCursor()
+        
+    def OnApplySqlStatement(self, event):
+        """!Apply simple/advanced sql statement"""
+        keyColumn = -1 # index of key column
+        listWin = self.FindWindowById(self.layerPage[self.layer]['data'])
+        sql = None
+        win = self.FindWindowById(self.layerPage[self.layer]['simple'])
+        if not win:
+            return
+        
+        wx.BeginBusyCursor()
+        if win.GetValue():
+            # simple sql statement
+            whereCol = self.FindWindowById(self.layerPage[self.layer]['whereColumn']).GetStringSelection()
+            whereOpe = self.FindWindowById(self.layerPage[self.layer]['whereOperator']).GetStringSelection()
+            whereVal = self.FindWindowById(self.layerPage[self.layer]['where']).GetValue().strip()
+            try:
+                if len(whereVal) > 0:
+                    keyColumn = listWin.LoadData(self.layer, where = whereCol + whereOpe + whereVal)
+                else:
+                    keyColumn = listWin.LoadData(self.layer)
+            except GException, e:
+                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:
+                GError(parent = self,
+                       message = _("Loading attribute data failed.\n"
+                                   "Invalid SQL select statement.\n\n%s") % win.GetValue())
+                win.SetValue("SELECT * FROM %s" % self.mapDBInfo.layers[self.layer]['table'])
+                cols = None
+                where = None
+            
+            if cols or where or sql:
+                try:
+                    keyColumn = listWin.LoadData(self.layer, columns = cols,
+                                                 where = where, sql = sql)
+                except GException, e:
+                    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 sql and 'order by' in sql.lower():
+            pass # don't order by key column
+        else:
+            if keyColumn > -1:
+                listWin.SortListItems(col = keyColumn, ascending = True)
+            else:
+                listWin.SortListItems(col = 0, ascending = True) 
+        
+        wx.EndBusyCursor()
+        
+        # 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
+
+        @return (columns, where)
+        @return None on error
+        """
+        if statement[0:7].lower() != 'select ':
+            return None
+        
+        cols = ''
+        index = 7
+        for c in statement[index:]:
+            if c == ' ':
+                break
+            cols += c
+            index += 1
+        if cols == '*':
+            cols = None
+        else:
+            cols = cols.split(',')
+        
+        tablelen = len(self.mapDBInfo.layers[self.layer]['table'])
+        
+        if statement[index+1:index+6].lower() != 'from ' or \
+                statement[index+6:index+6+tablelen] != '%s' % \
+                (self.mapDBInfo.layers[self.layer]['table']):
+            return None
+        
+        if len(statement[index+7+tablelen:]) > 0:
+            index = statement.lower().find('where ')
+            if index > -1:
+                where = statement[index+6:]
+            else:
+                where = None
+        else:
+            where = None
+        
+        return (cols, where)
+    
+    def OnCloseWindow(self, event):
+        """!Cancel button pressed"""
+        if self.parent and self.parent.GetName() == 'LayerManager':
+            # deregister ATM
+            self.parent.dialogs['atm'].remove(self)
+                    
+        if not isinstance(event, wx.CloseEvent):
+            self.Destroy()
+        
+        event.Skip()
+
+    def OnBuilder(self,event):
+        """!SQL Builder button pressed -> show the SQLBuilder dialog"""
+        if not self.builder:
+            self.builder = SQLFrame(parent = self, id = wx.ID_ANY,
+                                    title = _("SQL Builder"),
+                                    vectmap = self.vectorName,
+                                    evtheader = self.OnBuilderEvt)
+            self.builder.Show()
+        else:
+            self.builder.Raise()
+        
+    def OnBuilderEvt(self, event):
+        if event == 'apply':
+            sqlstr = self.builder.GetSQLStatement()
+            self.FindWindowById(self.layerPage[self.layer]['statement']).SetValue(sqlstr)
+            if self.builder.CloseOnApply():
+                self.builder = None
+        elif event == 'close':
+            self.builder = None
+        
+    def OnTextEnter(self, event):
+        pass
+    
+    def OnDataItemActivated(self, event):
+        """!Item activated, highlight selected item"""
+        self.OnDataDrawSelected(event)
+
+        event.Skip()
+
+    def OnExtractSelected(self, event):
+        """!Extract vector objects selected in attribute browse window
+        to new vector map
+        """
+        tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
+        # cats = tlist.selectedCats[:]
+        cats = tlist.GetSelectedItems()
+        if len(cats) == 0:
+            GMessage(parent = self,
+                     message = _('Nothing to extract.'))
+            return
+        else:
+            # dialog to get file name
+            dlg = CreateNewVector(parent = self, title = _('Extract selected features'),
+                                  log = self.cmdLog,
+                                  cmd = (('v.extract',
+                                          { 'input' : self.vectorName,
+                                            'list' : ListOfCatsToRange(cats) },
+                                          'output')),
+                                  disableTable = True)
+            if not dlg:
+                return
+            
+            name = dlg.GetName(full = True)
+            if name and dlg.IsChecked('add'):
+                # add layer to map layer tree
+                self.parent.curr_page.maptree.AddLayer(ltype = 'vector',
+                                                       lname = name,
+                                                       lcmd = ['d.vect', 'map=%s' % name])
+            dlg.Destroy()
+            
+    def OnDeleteSelected(self, event):
+        """!Delete vector objects selected in attribute browse window
+        (attribures and geometry)
+        """
+        tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
+        cats = tlist.GetSelectedItems()
+        if len(cats) == 0:
+            GMessage(parent = self,
+                     message = _('Nothing to delete.'))
+        
+        display = None
+        if 'vdigit' in self.mapdisplay.toolbars:
+            digitToolbar = self.mapdisplay.toolbars['vdigit']
+            if digitToolbar and digitToolbar.GetLayer() and \
+                    digitToolbar.GetLayer().GetName() == self.vectorName:
+                display = self.mapdisplay.GetMapWindow().GetDisplay()
+                display.SetSelected(map(int, cats), layer = self.layer)
+                self.mapdisplay.MapWindow.UpdateMap(render = True, renderVector = True)
+        
+        if self.OnDataItemDelete(None):
+            if display:
+                self.mapdisplay.GetMapWindow().digit.DeleteSelectedLines()
+            else:
+                RunCommand('v.edit',
+                           parent = self,
+                           quiet = True,
+                           map = self.vectorName,
+                           tool = 'delete',
+                           cats = ListOfCatsToRange(cats))
+            
+            self.mapdisplay.MapWindow.UpdateMap(render = True, renderVector = True)
+        
+    def AddQueryMapLayer(self):
+        """!Redraw a map
+
+        Return True if map has been redrawn, False if no map is given
+        """
+        tlist = self.FindWindowById(self.layerPage[self.layer]['data'])
+        cats = { 
+            self.layer : tlist.GetSelectedItems()
+            }
+        
+        if self.mapdisplay.Map.GetLayerIndex(self.qlayer) < 0:
+            self.qlayer = None
+            
+        if self.qlayer:
+            self.qlayer.SetCmd(self.mapdisplay.AddTmpVectorMapLayer(self.vectorName, cats, addLayer = False))
+        else:
+            self.qlayer = self.mapdisplay.AddTmpVectorMapLayer(self.vectorName, cats)
+
+        return self.qlayer
+    
+    def UpdateDialog(self, layer):
+        """!Updates dialog layout for given layer"""
+        # delete page
+        if layer in self.mapDBInfo.layers.keys():
+            # delete page
+            # draging pages disallowed
+            # if self.browsePage.GetPageText(page).replace('Layer ', '').strip() == str(layer):
+            # self.browsePage.DeletePage(page)
+            # break
+            self.browsePage.DeletePage(self.mapDBInfo.layers.keys().index(layer))
+            self.manageTablePage.DeletePage(self.mapDBInfo.layers.keys().index(layer))
+            # set current page selection
+            self.notebook.SetSelectionByName('layers')
+            
+        # fetch fresh db info
+        self.mapDBInfo = VectorDBInfo(self.vectorName)    
+
+        #
+        # add new page
+        #
+        if layer in self.mapDBInfo.layers.keys():
+            # 'browse data' page
+            self._createBrowsePage(layer)
+            # 'manage tables' page
+            self._createManageTablePage(layer)
+            # set current page selection
+            self.notebook.SetSelectionByName('layers')
+            
+        #
+        # 'manage layers' page
+        #
+        # update list of layers
+        self.layerList.Update(self.mapDBInfo.layers)
+        self.layerList.Populate(update = True)
+        # update selected widgets
+        listOfLayers = map(str, self.mapDBInfo.layers.keys())
+        ### delete layer page
+        self.manageLayerBook.deleteLayer.SetItems(listOfLayers)
+        if len(listOfLayers) > 0:
+            self.manageLayerBook.deleteLayer.SetStringSelection(listOfLayers[0])
+            tableName = self.mapDBInfo.layers[int(listOfLayers[0])]['table']
+            maxLayer = max(self.mapDBInfo.layers.keys())
+        else:
+            tableName = ''
+            maxLayer = 0
+        self.manageLayerBook.deleteTable.SetLabel( \
+            _('Drop also linked attribute table (%s)') % \
+                tableName)
+        ### add layer page
+        self.manageLayerBook.addLayerWidgets['layer'][1].SetValue(\
+            maxLayer+1)
+        ### 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"""
+
+    def __init__(self, parent, id, table, columns, pos = wx.DefaultPosition,
+                 size = wx.DefaultSize):
+
+        self.parent  = parent
+        self.table   = table
+        self.columns = columns
+        wx.ListCtrl.__init__(self, parent, id, pos, size,
+                             style = wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES |
+                             wx.BORDER_NONE)
+
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+        # listmix.TextEditMixin.__init__(self)
+
+    def Update(self, table, columns):
+        """!Update column description"""
+        self.table   = table
+        self.columns = columns
+
+    def Populate(self, update = False):
+        """!Populate the list"""
+        itemData = {} # requested by sorter
+
+        if not update:
+            headings = [_("Column name"), _("Data type"), _("Data length")]
+            i = 0
+            for h in headings:
+                self.InsertColumn(col = i, heading = h)
+                self.SetColumnWidth(col = i, width = 150)
+                i += 1
+        else:
+            self.DeleteAllItems()
+
+        i = 0
+        for column in self.columns:
+            index = self.InsertStringItem(sys.maxint, str(column))
+            self.SetStringItem(index, 0, str(column))
+            self.SetStringItem(index, 1, str(self.table[column]['type']))
+            self.SetStringItem(index, 2, str(self.table[column]['length']))
+            self.SetItemData(index, i)
+            itemData[i] = (str(column),
+                           str(self.table[column]['type']),
+                           int(self.table[column]['length']))
+            i = i + 1
+
+        self.SendSizeEvent()
+        
+        return itemData
+
+class LayerListCtrl(wx.ListCtrl,
+                    listmix.ListCtrlAutoWidthMixin):
+                    # listmix.ColumnSorterMixin):
+                    # listmix.TextEditMixin):
+    """!Layer description list"""
+
+    def __init__(self, parent, id, layers,
+                 pos = wx.DefaultPosition,
+                 size = wx.DefaultSize):
+
+        self.parent = parent
+        self.layers = layers
+        wx.ListCtrl.__init__(self, parent, id, pos, size,
+                             style = wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES |
+                             wx.BORDER_NONE)
+
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+        # listmix.TextEditMixin.__init__(self)
+
+    def Update(self, layers):
+        """!Update description"""
+        self.layers = layers
+
+    def Populate(self, update = False):
+        """!Populate the list"""
+        itemData = {} # requested by sorter
+
+        if not update:
+            headings = [_("Layer"),  _("Driver"), _("Database"), _("Table"), _("Key")]
+            i = 0
+            for h in headings:
+                self.InsertColumn(col = i, heading = h)
+                i += 1
+        else:
+            self.DeleteAllItems()
+
+        i = 0
+        for layer in self.layers.keys():
+            index = self.InsertStringItem(sys.maxint, str(layer))
+            self.SetStringItem(index, 0, str(layer))
+            database = str(self.layers[layer]['database'])
+            driver   = str(self.layers[layer]['driver'])
+            table    = str(self.layers[layer]['table'])
+            key      = str(self.layers[layer]['key'])
+            self.SetStringItem(index, 1, driver)
+            self.SetStringItem(index, 2, database)
+            self.SetStringItem(index, 3, table)
+            self.SetStringItem(index, 4, key)
+            self.SetItemData(index, i)
+            itemData[i] = (str(layer),
+                           driver,
+                           database,
+                           table,
+                           key)
+            i += 1
+
+        for i in range(self.GetColumnCount()):
+            self.SetColumnWidth(col = i, width = wx.LIST_AUTOSIZE)
+            if self.GetColumnWidth(col = i) < 60:
+                self.SetColumnWidth(col = i, width = 60)
+
+        self.SendSizeEvent()
+        
+        return itemData
+
+class LayerBook(wx.Notebook):
+    """!Manage layers (add, delete, modify)"""
+    def __init__(self, parent, id,
+                 parentDialog,
+                 style = wx.BK_DEFAULT):
+        wx.Notebook.__init__(self, parent, id, style = style)
+
+        self.parent       = parent
+        self.parentDialog = parentDialog
+        self.mapDBInfo    = self.parentDialog.mapDBInfo
+
+        #
+        # drivers
+        #
+        drivers = RunCommand('db.drivers',
+                             quiet = True,
+                             read = True,
+                             flags = 'p')
+        
+        self.listOfDrivers = []
+        for drv in drivers.splitlines():
+            self.listOfDrivers.append(drv.strip())
+        
+        #
+        # get default values
+        #
+        self.defaultConnect = {}
+        connect = RunCommand('db.connect',
+                             flags = 'p',
+                             read = True,
+                             quiet = True)
+        
+        for line in connect.splitlines():
+            item, value = line.split(':', 1)
+            self.defaultConnect[item.strip()] = value.strip()
+        
+        if len(self.defaultConnect['driver']) == 0 or \
+               len(self.defaultConnect['database']) == 0:
+            GWarning(parent = self.parent,
+                     message = _("Unknown default DB connection. "
+                                 "Please define DB connection using db.connect module."))
+        
+        self.defaultTables = self._getTables(self.defaultConnect['driver'],
+                                             self.defaultConnect['database'])
+        try:
+            self.defaultColumns = self._getColumns(self.defaultConnect['driver'],
+                                                    self.defaultConnect['database'],
+                                                    self.defaultTables[0])
+        except IndexError:
+            self.defaultColumns = []
+
+        self._createAddPage()
+        self._createDeletePage()
+        self._createModifyPage()
+
+    def _createAddPage(self):
+        """!Add new layer"""
+        self.addPanel = wx.Panel(parent = self, id = wx.ID_ANY)
+        self.AddPage(page = self.addPanel, text = _("Add layer"))
+        
+        try:
+            maxLayer = max(self.mapDBInfo.layers.keys())
+        except ValueError:
+            maxLayer = 0
+
+        # layer description
+        
+        layerBox = wx.StaticBox (parent = self.addPanel, id = wx.ID_ANY,
+                                 label = " %s " % (_("Layer description")))
+        layerSizer = wx.StaticBoxSizer(layerBox, wx.VERTICAL)
+        
+        #
+        # list of layer widgets (label, value)
+        #
+        self.addLayerWidgets = {'layer':
+                                    (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
+                                                   label = '%s:' % _("Layer")),
+                                     wx.SpinCtrl(parent = self.addPanel, id = wx.ID_ANY, size = (65, -1),
+                                                 initial = maxLayer+1,
+                                                 min = 1, max = 1e6)),
+                                'driver':
+                                    (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
+                                                   label = '%s:' % _("Driver")),
+                                     wx.Choice(parent = self.addPanel, id = wx.ID_ANY, size = (200, -1),
+                                               choices = self.listOfDrivers)),
+                                'database':
+                                    (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
+                                                   label = '%s:' % _("Database")),
+                                     wx.TextCtrl(parent = self.addPanel, id = wx.ID_ANY,
+                                                 value = '',
+                                                 style = wx.TE_PROCESS_ENTER)),
+                                'table':
+                                    (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
+                                                   label = '%s:' % _("Table")),
+                                     wx.Choice(parent = self.addPanel, id = wx.ID_ANY, size = (200, -1),
+                                               choices = self.defaultTables)),
+                                'key':
+                                    (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
+                                                   label = '%s:' % _("Key column")),
+                                     wx.Choice(parent = self.addPanel, id = wx.ID_ANY, size = (200, -1),
+                                               choices = self.defaultColumns)),
+                                'addCat':
+                                    (wx.CheckBox(parent = self.addPanel, id = wx.ID_ANY,
+                                                 label = _("Insert record for each category into table")),
+                                     None),
+                                }
+        
+        # set default values for widgets
+        self.addLayerWidgets['driver'][1].SetStringSelection(self.defaultConnect['driver'])
+        self.addLayerWidgets['database'][1].SetValue(self.defaultConnect['database'])
+        self.addLayerWidgets['table'][1].SetSelection(0)
+        self.addLayerWidgets['key'][1].SetSelection(0)
+        # events
+        self.addLayerWidgets['driver'][1].Bind(wx.EVT_CHOICE, self.OnDriverChanged)
+        self.addLayerWidgets['database'][1].Bind(wx.EVT_TEXT_ENTER, self.OnDatabaseChanged)
+        self.addLayerWidgets['table'][1].Bind(wx.EVT_CHOICE, self.OnTableChanged)
+        
+        # tooltips
+        self.addLayerWidgets['addCat'][0].SetToolTipString(_("You need to add categories "
+                                                             "by v.category module."))
+        #
+        # list of table widgets
+        #
+        keyCol = UserSettings.Get(group = 'atm', key = 'keycolumn', subkey = 'value')
+        self.tableWidgets = {'table': (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
+                                                     label = '%s:' % _("Table name")),
+                                       wx.TextCtrl(parent = self.addPanel, id = wx.ID_ANY,
+                                                   value = '',
+                                                   style = wx.TE_PROCESS_ENTER)),
+                             'key': (wx.StaticText(parent = self.addPanel, id = wx.ID_ANY,
+                                                   label = '%s:' % _("Key column")),
+                                     wx.TextCtrl(parent = self.addPanel, id = wx.ID_ANY,
+                                                 value = keyCol,
+                                                 style = wx.TE_PROCESS_ENTER))}
+        # events
+        self.tableWidgets['table'][1].Bind(wx.EVT_TEXT_ENTER, self.OnCreateTable)
+        self.tableWidgets['key'][1].Bind(wx.EVT_TEXT_ENTER, self.OnCreateTable)
+        
+        btnTable   = wx.Button(self.addPanel, wx.ID_ANY, _("&Create table"),
+                             size = (125,-1))
+        btnTable.Bind(wx.EVT_BUTTON, self.OnCreateTable)
+        
+        btnLayer   = wx.Button(self.addPanel, wx.ID_ANY, _("&Add layer"),
+                             size = (125,-1))
+        btnLayer.Bind(wx.EVT_BUTTON, self.OnAddLayer)
+        
+        btnDefault = wx.Button(self.addPanel, wx.ID_ANY, _("&Set default"),
+                               size = (125,-1))
+        btnDefault.Bind(wx.EVT_BUTTON, self.OnSetDefault)
+        
+        # do layout
+        
+        pageSizer = wx.BoxSizer(wx.HORIZONTAL)
+                
+        # data area
+        dataSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        dataSizer.AddGrowableCol(1)
+        row = 0
+        for key in ('layer', 'driver', 'database', 'table', 'key', 'addCat'):
+            label, value = self.addLayerWidgets[key]
+            if not value:
+                span = (1, 2)
+            else:
+                span = (1, 1)
+            dataSizer.Add(item = label,
+                          flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0),
+                          span = span)
+            
+            if not value:
+                row += 1
+                continue
+
+            if label.GetLabel() == "Layer:":
+                style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT
+            else:
+                style = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND
+            
+            dataSizer.Add(item = value,
+                          flag = style, pos = (row, 1))
+            
+            row += 1
+        
+        layerSizer.Add(item = dataSizer,
+                       proportion = 1,
+                       flag = wx.ALL | wx.EXPAND,
+                       border = 5)
+        
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(item = btnDefault,
+                     proportion = 0,
+                     flag = wx.ALL | wx.ALIGN_LEFT,
+                     border = 5)
+        
+        btnSizer.Add(item = (5, 5),
+                     proportion = 1,
+                     flag = wx.ALL | wx.EXPAND,
+                     border = 5)
+        
+        btnSizer.Add(item = btnLayer,
+                     proportion = 0,
+                     flag = wx.ALL | wx.ALIGN_RIGHT,
+                     border = 5)
+        
+        layerSizer.Add(item = btnSizer,
+                       proportion = 0,
+                       flag = wx.ALL | wx.EXPAND,
+                       border = 0)
+                
+        # table description
+        tableBox = wx.StaticBox (parent = self.addPanel, id = wx.ID_ANY,
+                                 label = " %s " % (_("Table description")))
+        tableSizer = wx.StaticBoxSizer(tableBox, wx.VERTICAL)
+        
+        # data area
+        dataSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
+        dataSizer.AddGrowableCol(1)
+        for key in ['table', 'key']:
+            label, value = self.tableWidgets[key]
+            dataSizer.Add(item = label,
+                          flag = wx.ALIGN_CENTER_VERTICAL)
+            dataSizer.Add(item = value,
+                          flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
+
+        tableSizer.Add(item = dataSizer,
+                       proportion = 1,
+                       flag = wx.ALL | wx.EXPAND,
+                       border = 5)
+
+        tableSizer.Add(item = btnTable,
+                       proportion = 0,
+                       flag = wx.ALL | wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT,
+                       border = 5)
+
+        pageSizer.Add(item = layerSizer,
+                      proportion = 3,
+                      flag = wx.ALL | wx.EXPAND,
+                      border = 3)
+        
+        pageSizer.Add(item = tableSizer,
+                      proportion = 2,
+                      flag = wx.TOP | wx.BOTTOM | wx.RIGHT | wx.EXPAND,
+                      border = 3)
+        
+        layerSizer.SetVirtualSizeHints(self.addPanel)
+        self.addPanel.SetAutoLayout(True)
+        self.addPanel.SetSizer(pageSizer)
+        pageSizer.Fit(self.addPanel)
+        
+    def _createDeletePage(self):
+        """!Delete layer"""
+        self.deletePanel = wx.Panel(parent = self, id = wx.ID_ANY)
+        self.AddPage(page = self.deletePanel, text = _("Remove layer"))
+
+        label = wx.StaticText(parent = self.deletePanel, id = wx.ID_ANY,
+                              label = '%s:' % _("Layer to remove"))
+
+        self.deleteLayer = wx.ComboBox(parent = self.deletePanel, id = wx.ID_ANY, size = (100, -1),
+                                       style = wx.CB_SIMPLE | wx.CB_READONLY,
+                                       choices = map(str, self.mapDBInfo.layers.keys()))
+        self.deleteLayer.SetSelection(0)           
+        self.deleteLayer.Bind(wx.EVT_COMBOBOX, self.OnChangeLayer)
+
+        try:
+            tableName = self.mapDBInfo.layers[int(self.deleteLayer.GetStringSelection())]['table']
+        except ValueError:
+            tableName = ''
+            
+        self.deleteTable = wx.CheckBox(parent = self.deletePanel, id = wx.ID_ANY,
+                                       label = _('Drop also linked attribute table (%s)') % \
+                                       tableName)
+
+        if tableName == '':
+            self.deleteLayer.Enable(False)
+            self.deleteTable.Enable(False)
+            
+        btnDelete   = wx.Button(self.deletePanel, wx.ID_DELETE, _("&Remove layer"),
+                                size = (125,-1))
+        btnDelete.Bind(wx.EVT_BUTTON, self.OnDeleteLayer)
+
+        #
+        # do layout
+        #
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+
+        dataSizer = wx.BoxSizer(wx.VERTICAL)
+
+        flexSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(2)
+
+        flexSizer.Add(item = label,
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(item = self.deleteLayer,
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+
+        dataSizer.Add(item = flexSizer,
+                      proportion = 0,
+                      flag = wx.ALL | wx.EXPAND,
+                      border = 1)
+
+        dataSizer.Add(item = self.deleteTable,
+                      proportion = 0,
+                      flag = wx.ALL | wx.EXPAND,
+                      border = 1)
+
+        pageSizer.Add(item = dataSizer,
+                      proportion = 1,
+                      flag = wx.ALL | wx.EXPAND,
+                      border = 5)
+
+        pageSizer.Add(item = btnDelete,
+                      proportion = 0,
+                      flag = wx.ALL | wx.ALIGN_RIGHT,
+                      border = 5)
+
+        self.deletePanel.SetSizer(pageSizer)
+
+    def _createModifyPage(self):
+        """!Modify layer"""
+        self.modifyPanel = wx.Panel(parent = self, id = wx.ID_ANY)
+        self.AddPage(page = self.modifyPanel, text = _("Modify layer"))
+
+        #
+        # list of layer widgets (label, value)
+        #
+        self.modifyLayerWidgets = {'layer':
+                                       (wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
+                                                      label = '%s:' % _("Layer")),
+                                        wx.ComboBox(parent = self.modifyPanel, id = wx.ID_ANY,
+                                                    size = (100, -1),
+                                                    style = wx.CB_SIMPLE | wx.CB_READONLY,
+                                                    choices = map(str, 
+                                                                self.mapDBInfo.layers.keys()))),
+                                   'driver':
+                                       (wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
+                                                      label = '%s:' % _("Driver")),
+                                        wx.Choice(parent = self.modifyPanel, id = wx.ID_ANY,
+                                                  size = (200, -1),
+                                                  choices = self.listOfDrivers)),
+                                   'database':
+                                       (wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
+                                                      label = '%s:' % _("Database")),
+                                        wx.TextCtrl(parent = self.modifyPanel, id = wx.ID_ANY,
+                                                    value = '', size = (350, -1),
+                                                    style = wx.TE_PROCESS_ENTER)),
+                                   'table':
+                                       (wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
+                                                      label = '%s:' % _("Table")),
+                                        wx.Choice(parent = self.modifyPanel, id = wx.ID_ANY,
+                                                  size = (200, -1),
+                                                  choices = self.defaultTables)),
+                                   'key':
+                                       (wx.StaticText(parent = self.modifyPanel, id = wx.ID_ANY,
+                                                      label = '%s:' % _("Key column")),
+                                        wx.Choice(parent = self.modifyPanel, id = wx.ID_ANY,
+                                                  size = (200, -1),
+                                                  choices = self.defaultColumns))}
+        
+        # set default values for widgets
+        self.modifyLayerWidgets['layer'][1].SetSelection(0)
+        try:
+            layer = int(self.modifyLayerWidgets['layer'][1].GetStringSelection())
+        except ValueError:
+            layer = None
+            for label in self.modifyLayerWidgets.keys():
+                self.modifyLayerWidgets[label][1].Enable(False)
+
+        if layer:
+            driver   = self.mapDBInfo.layers[layer]['driver']
+            database = self.mapDBInfo.layers[layer]['database']
+            table    = self.mapDBInfo.layers[layer]['table']
+
+            listOfColumns = self._getColumns(driver, database, table)
+            self.modifyLayerWidgets['driver'][1].SetStringSelection(driver)
+            self.modifyLayerWidgets['database'][1].SetValue(database)
+            if table in self.modifyLayerWidgets['table'][1].GetItems():
+                self.modifyLayerWidgets['table'][1].SetStringSelection(table)
+            else:
+                if self.defaultConnect['schema'] != '':
+                    table = self.defaultConnect['schema'] + table # try with default schema
+                else:
+                    table = 'public.' + table # try with 'public' schema
+                self.modifyLayerWidgets['table'][1].SetStringSelection(table)
+            self.modifyLayerWidgets['key'][1].SetItems(listOfColumns)
+            self.modifyLayerWidgets['key'][1].SetSelection(0)
+
+        # events
+        self.modifyLayerWidgets['layer'][1].Bind(wx.EVT_COMBOBOX, self.OnChangeLayer)
+        # self.modifyLayerWidgets['driver'][1].Bind(wx.EVT_CHOICE, self.OnDriverChanged)
+        # self.modifyLayerWidgets['database'][1].Bind(wx.EVT_TEXT_ENTER, self.OnDatabaseChanged)
+        # self.modifyLayerWidgets['table'][1].Bind(wx.EVT_CHOICE, self.OnTableChanged)
+
+        btnModify = wx.Button(self.modifyPanel, wx.ID_DELETE, _("&Modify layer"),
+                              size = (125,-1))
+        btnModify.Bind(wx.EVT_BUTTON, self.OnModifyLayer)
+
+        #
+        # do layout
+        #
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+
+        # data area
+        dataSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
+        dataSizer.AddGrowableCol(1)
+        for key in ('layer', 'driver', 'database', 'table', 'key'):
+            label, value = self.modifyLayerWidgets[key]
+            dataSizer.Add(item = label,
+                          flag = wx.ALIGN_CENTER_VERTICAL)
+            if label.GetLabel() == "Layer:":
+                dataSizer.Add(item = value,
+                              flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+            else:
+                dataSizer.Add(item = value,
+                              flag = wx.ALIGN_CENTER_VERTICAL)
+
+        pageSizer.Add(item = dataSizer,
+                      proportion = 1,
+                      flag = wx.ALL | wx.EXPAND,
+                      border = 5)
+
+        pageSizer.Add(item = btnModify,
+                      proportion = 0,
+                      flag = wx.ALL | wx.ALIGN_RIGHT,
+                      border = 5)
+
+        self.modifyPanel.SetSizer(pageSizer)
+
+    def _getTables(self, driver, database):
+        """!Get list of tables for given driver and database"""
+        tables = []
+
+        ret = RunCommand('db.tables',
+                         parent = self,
+                         read = True,
+                         flags = 'p',
+                         driver = driver,
+                         database = database)
+        
+        if ret is None:
+            GError(parent = self,
+                   message = _("Unable to get list of tables.\n"
+                               "Please use db.connect to set database parameters."))
+            
+            return tables
+        
+        for table in ret.splitlines():
+            tables.append(table)
+        
+        return tables
+
+    def _getColumns(self, driver, database, table):
+        """!Get list of column of given table"""
+        columns = []
+
+        ret = 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 = event.GetString()
+        database = self.addLayerWidgets['database'][1].GetValue()
+
+        winTable = self.addLayerWidgets['table'][1]
+        winKey   = self.addLayerWidgets['key'][1]
+        tables   = self._getTables(driver, database)
+
+        winTable.SetItems(tables)
+        winTable.SetSelection(0)
+
+        if len(tables) == 0:
+            winKey.SetItems([])
+
+        event.Skip()
+
+    def OnDatabaseChanged(self, event):
+        """!Database selection changed, update list of tables"""
+        event.Skip()
+
+    def OnTableChanged(self, event):
+        """!Table name changed, update list of columns"""
+        driver   = self.addLayerWidgets['driver'][1].GetStringSelection()
+        database = self.addLayerWidgets['database'][1].GetValue()
+        table    = event.GetString()
+
+        win  = self.addLayerWidgets['key'][1]
+        cols = self._getColumns(driver, database, table)
+        win.SetItems(cols)
+        win.SetSelection(0)
+
+        event.Skip()
+
+    def OnSetDefault(self, event):
+        """!Set default values"""
+        driver   = self.addLayerWidgets['driver'][1]
+        database = self.addLayerWidgets['database'][1]
+        table    = self.addLayerWidgets['table'][1]
+        key      = self.addLayerWidgets['key'][1]
+
+        driver.SetStringSelection(self.defaultConnect['driver'])
+        database.SetValue(self.defaultConnect['database'])
+        tables = self._getTables(self.defaultConnect['driver'],
+                                  self.defaultConnect['database'])
+        table.SetItems(tables)
+        table.SetSelection(0)
+        if len(tables) == 0:
+            key.SetItems([])
+        else:
+            cols = self._getColumns(self.defaultConnect['driver'],
+                                     self.defaultConnect['database'],
+                                     tables[0])
+            key.SetItems(cols)
+            key.SetSelection(0)
+
+        event.Skip()
+
+    def OnCreateTable(self, event):
+        """!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()
+        key      = self.tableWidgets['key'][1].GetValue()
+        
+        if not table or not key:
+            GError(parent = self,
+                   message = _("Unable to create new table. "
+                               "Table name or key column name is missing."))
+            return
+
+        if table in self.addLayerWidgets['table'][1].GetItems():
+            GError(parent = self,
+                   message = _("Unable to create new table. "
+                               "Table <%s> already exists in the database.") % table)
+            return
+        
+        # create table
+        sql = 'CREATE TABLE %s (%s INTEGER)' % (table, key)
+
+        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))
+        tableList.SetStringSelection(table)
+
+        # update key column selection
+        keyList = self.addLayerWidgets['key'][1]
+        keyList.SetItems(self._getColumns(driver, database, table))
+        keyList.SetStringSelection(key)
+        
+        event.Skip()
+
+    def OnAddLayer(self, event):
+        """!Add new layer to vector map"""
+        layer    = int(self.addLayerWidgets['layer'][1].GetValue())
+        layerWin = self.addLayerWidgets['layer'][1]
+        driver   = self.addLayerWidgets['driver'][1].GetStringSelection()
+        database = self.addLayerWidgets['database'][1].GetValue()
+        table    = self.addLayerWidgets['table'][1].GetStringSelection()
+        key      = self.addLayerWidgets['key'][1].GetStringSelection()
+        
+        if layer in self.mapDBInfo.layers.keys():
+            GError(parent = self,
+                   message = _("Unable to add new layer to vector map <%(vector)s>. "
+                               "Layer %(layer)d already exists.") % \
+                       {'vector' : self.mapDBInfo.map, 'layer' : layer})
+            return
+
+        # add new layer
+        ret = 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():
+            RunCommand('v.to.db',
+                       parent = self,
+                       quiet = True,
+                       map = self.mapDBInfo.map,
+                       layer = layer,
+                       qlayer = layer,
+                       option = 'cat',
+                       columns = key)
+
+        if ret == 0:
+            # update dialog (only for new layer)
+            self.parentDialog.UpdateDialog(layer = layer) 
+            # update db info
+            self.mapDBInfo = self.parentDialog.mapDBInfo
+            # increase layer number
+            layerWin.SetValue(layer+1)
+
+        if len(self.mapDBInfo.layers.keys()) == 1:
+            # first layer add --- enable previously disabled widgets
+            self.deleteLayer.Enable()
+            self.deleteTable.Enable()
+            for label in self.modifyLayerWidgets.keys():
+                self.modifyLayerWidgets[label][1].Enable()
+            
+    def OnDeleteLayer(self, event):
+        """!Delete layer"""
+        try:
+            layer = int(self.deleteLayer.GetValue())
+        except:
+            return
+
+        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():
+            driver   = self.addLayerWidgets['driver'][1].GetStringSelection()
+            database = self.addLayerWidgets['database'][1].GetValue()
+            table    = self.mapDBInfo.layers[layer]['table']
+            sql      = 'DROP TABLE %s' % (table)
+
+            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))
+            tableList.SetStringSelection(table)
+        
+        # update dialog
+        self.parentDialog.UpdateDialog(layer = layer) 
+        # update db info
+        self.mapDBInfo = self.parentDialog.mapDBInfo
+
+        if len(self.mapDBInfo.layers.keys()) == 0:
+            # disable selected widgets
+            self.deleteLayer.Enable(False)
+            self.deleteTable.Enable(False)
+            for label in self.modifyLayerWidgets.keys():
+                self.modifyLayerWidgets[label][1].Enable(False)
+            
+        event.Skip()
+
+    def OnChangeLayer(self, event):
+        """!Layer number of layer to be deleted is changed"""
+        try:
+            layer = int(event.GetString())
+        except:
+            try:
+                layer = self.mapDBInfo.layers.keys()[0]
+            except:
+                return
+
+        if self.GetCurrentPage() == self.modifyPanel:
+            driver   = self.mapDBInfo.layers[layer]['driver']
+            database = self.mapDBInfo.layers[layer]['database']
+            table    = self.mapDBInfo.layers[layer]['table']
+            listOfColumns = self._getColumns(driver, database, table)
+            self.modifyLayerWidgets['driver'][1].SetStringSelection(driver)
+            self.modifyLayerWidgets['database'][1].SetValue(database)
+            self.modifyLayerWidgets['table'][1].SetStringSelection(table)
+            self.modifyLayerWidgets['key'][1].SetItems(listOfColumns)
+            self.modifyLayerWidgets['key'][1].SetSelection(0)
+        else:
+            self.deleteTable.SetLabel(_('Drop also linked attribute table (%s)') % \
+                                          self.mapDBInfo.layers[layer]['table'])
+        if event:
+            event.Skip()
+
+    def OnModifyLayer(self, event):
+        """!Modify layer connection settings"""
+
+        layer = int(self.modifyLayerWidgets['layer'][1].GetStringSelection())
+
+        modify = False
+        if self.modifyLayerWidgets['driver'][1].GetStringSelection() != \
+                self.mapDBInfo.layers[layer]['driver'] or \
+                self.modifyLayerWidgets['database'][1].GetStringSelection() != \
+                self.mapDBInfo.layers[layer]['database'] or \
+                self.modifyLayerWidgets['table'][1].GetStringSelection() != \
+                self.mapDBInfo.layers[layer]['table'] or \
+                self.modifyLayerWidgets['key'][1].GetStringSelection() != \
+                self.mapDBInfo.layers[layer]['key']:
+            modify = True
+
+        if modify:
+            # delete layer
+            RunCommand('v.db.connect',
+                       parent = self,
+                       quiet = True,
+                       flag = 'd',
+                       map = self.mapDBInfo.map,
+                       layer = layer)
+            
+            # add modified layer
+            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
+            self.mapDBInfo = self.parentDialog.mapDBInfo
+
+        event.Skip()
+
+def main(argv = None):
+    import gettext
+    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+    
+    if argv is None:
+        argv = sys.argv
+    
+    if len(argv) != 2:
+        print >> sys.stderr, __doc__
+        sys.exit()
+    
+    #some applications might require image handlers
+    wx.InitAllImageHandlers()
+    
+    app = wx.PySimpleApp()
+    f = AttributeManager(parent = None, id = wx.ID_ANY,
+                         title = "%s - <%s>" % (_("GRASS GIS Attribute Table Manager"),
+                                              argv[1]),
+                         size = (900,600), vectorName = argv[1])
+    f.Show()
+    
+    app.MainLoop()
+    
+if __name__ == '__main__':
+    main()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/sqlbuilder.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/sqlbuilder.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/sqlbuilder.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,461 @@
+"""!
+ at package dbmgr.sqlbuilder
+
+ at brief GRASS SQL Builder
+
+Classes:
+ - sqlbuilder::SQLFrame
+
+Usage:
+ at code
+python sqlbuilder.py vector_map
+ at endcode
+
+(C) 2007-2009, 2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Jachym Cepicky <jachym.cepicky gmail.com> (original author)
+ at author Martin Landa <landa.martin gmail.com>
+ at author Hamish Bowman <hamish_b yahoo com>
+"""
+
+import os
+import sys
+
+if __name__ == "__main__":
+    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython'))
+from core import globalvar
+import wx
+
+from core.gcmd   import RunCommand, GError
+from dbmgr.vinfo import createDbInfoDesc, VectorDBInfo
+
+import grass.script as grass
+
+class SQLFrame(wx.Frame):
+    """!SQL Frame class"""
+    def __init__(self, parent, title, vectmap, id = wx.ID_ANY,
+                 layer = 1, qtype = "select", evtheader = None):
+        
+        wx.Frame.__init__(self, parent, id, title)
+        
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_sql.ico'),
+                             wx.BITMAP_TYPE_ICO))
+        
+        self.parent = parent
+        self.evtHeader = evtheader
+
+        #
+        # variables
+        #
+        self.vectmap = vectmap # fullname
+        if not "@" in self.vectmap:
+            self.vectmap = grass.find_file(self.vectmap, element = 'vector')['fullname']
+        self.mapname, self.mapset = self.vectmap.split("@", 1)
+        
+        # db info
+        self.layer = layer
+        self.dbInfo = VectorDBInfo(self.vectmap)
+        self.tablename = self.dbInfo.GetTable(self.layer)
+        self.driver, self.database = self.dbInfo.GetDbSettings(self.layer)
+        
+        self.qtype = qtype      # type of query: SELECT, UPDATE, DELETE, ...
+        self.colvalues = []     # array with unique values in selected column
+
+        # set dialog title
+        self.SetTitle(_("GRASS SQL Builder (%(type)s): vector map <%(map)s>") % \
+                          { 'type' : self.qtype.upper(), 'map' : self.vectmap })
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+        # statusbar
+        self.statusbar = self.CreateStatusBar(number=1)
+        self.statusbar.SetStatusText(_("SQL statement not verified"), 0)
+       
+        self._doLayout()
+
+    def _doLayout(self):
+        """!Do dialog layout"""
+      
+        pagesizer = wx.BoxSizer(wx.VERTICAL)
+
+        
+        # dbInfo
+        databasebox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                   label = " %s " % _("Database connection"))
+        databaseboxsizer = wx.StaticBoxSizer(databasebox, wx.VERTICAL)
+        databaseboxsizer.Add(item=createDbInfoDesc(self.panel, self.dbInfo, layer = self.layer),
+                             proportion=1,
+                             flag=wx.EXPAND | wx.ALL,
+                             border=3)
+
+        #
+        # text areas
+        #
+        # sql box
+        sqlbox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                              label = " %s " % _("Query"))
+        sqlboxsizer = wx.StaticBoxSizer(sqlbox, wx.VERTICAL)
+
+        self.text_sql = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
+                                    value = '', size = (-1, 50),
+                                    style=wx.TE_MULTILINE)
+        if self.qtype.lower() == "select":
+            self.text_sql.SetValue("SELECT * FROM %s" % self.tablename)
+        self.text_sql.SetInsertionPointEnd()
+        self.text_sql.SetToolTipString(_("Example: %s") % "SELECT * FROM roadsmajor WHERE MULTILANE = 'no' OR OBJECTID < 10")
+        wx.CallAfter(self.text_sql.SetFocus)
+
+        sqlboxsizer.Add(item = self.text_sql, flag = wx.EXPAND)
+        
+        #
+        # buttons
+        #
+        self.btn_clear  = wx.Button(parent = self.panel, id = wx.ID_CLEAR)
+        self.btn_clear.SetToolTipString(_("Set SQL statement to default"))
+        self.btn_verify = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                    label = _("Verify"))
+        self.btn_verify.SetToolTipString(_("Verify SQL statement"))
+        self.btn_apply  = wx.Button(parent = self.panel, id = wx.ID_APPLY)
+        self.btn_apply.SetToolTipString(_("Apply SQL statement and close the dialog"))
+        self.btn_close  = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
+        self.btn_close.SetToolTipString(_("Close the dialog"))
+        
+        self.btn_lv = { 'is'    : ['=', ],
+                        'isnot' : ['!=', ],
+                        'like'  : ['LIKE', ],
+                        'gt'    : ['>', ],
+                        'ge'    : ['>=', ],
+                        'lt'    : ['<', ],
+                        'le'    : ['<=', ],
+                        'or'    : ['OR', ],
+                        'not'   : ['NOT', ],
+                        'and'   : ['AND', ],
+                        'brac'  : ['()', ],
+                        'prc'   : ['%', ] }
+        
+        for key, value in self.btn_lv.iteritems():
+            btn = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                            label = value[0])
+            self.btn_lv[key].append(btn.GetId())
+        
+        buttonsizer = wx.FlexGridSizer(cols = 4, hgap = 5, vgap = 5)
+        buttonsizer.Add(item = self.btn_clear)
+        buttonsizer.Add(item = self.btn_verify)
+        buttonsizer.Add(item = self.btn_apply)
+        buttonsizer.Add(item = self.btn_close)
+        
+        buttonsizer2 = wx.GridBagSizer(5, 5)
+        buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['is'][1]), pos = (0,0))
+        buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['isnot'][1]), pos = (1,0))
+        buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['like'][1]), pos = (2, 0))
+
+        buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['gt'][1]), pos = (0, 1))
+        buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['ge'][1]), pos = (1, 1))
+        buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['or'][1]), pos = (2, 1))
+
+        buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['lt'][1]), pos = (0, 2))
+        buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['le'][1]), pos = (1, 2))
+        buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['not'][1]), pos = (2, 2))
+
+        buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['brac'][1]), pos = (0, 3))
+        buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['prc'][1]), pos = (1, 3))
+        buttonsizer2.Add(item = self.FindWindowById(self.btn_lv['and'][1]), pos = (2, 3))
+        
+        #
+        # list boxes (columns, values)
+        #
+        hsizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        columnsbox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                  label = " %s " % _("Columns"))
+        columnsizer = wx.StaticBoxSizer(columnsbox, wx.VERTICAL)
+        self.list_columns = wx.ListBox(parent = self.panel, id = wx.ID_ANY,
+                                       choices = self.dbInfo.GetColumns(self.tablename),
+                                       style = wx.LB_MULTIPLE)
+        columnsizer.Add(item = self.list_columns, proportion = 1,
+                        flag = wx.EXPAND)
+
+        radiosizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.radio_cv = wx.RadioBox(parent = self.panel, id = wx.ID_ANY,
+                                    label = " %s " % _("Add on double-click"),
+                                    choices = [_("columns"), _("values")])
+        self.radio_cv.SetSelection(1) # default 'values'
+        radiosizer.Add(item = self.radio_cv, proportion = 1,
+                       flag = wx.ALIGN_CENTER_HORIZONTAL | wx.EXPAND, border = 5)
+
+        columnsizer.Add(item = radiosizer, proportion = 0,
+                        flag = wx.TOP | wx.EXPAND, border = 5)
+        # self.list_columns.SetMinSize((-1,130))
+        # self.list_values.SetMinSize((-1,100))
+
+        valuesbox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                 label = " %s " % _("Values"))
+        valuesizer = wx.StaticBoxSizer(valuesbox, wx.VERTICAL)
+        self.list_values = wx.ListBox(parent = self.panel, id = wx.ID_ANY,
+                                      choices = self.colvalues,
+                                      style = wx.LB_MULTIPLE)
+        valuesizer.Add(item = self.list_values, proportion = 1,
+                       flag = wx.EXPAND)
+        
+        self.btn_unique = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                    label = _("Get all values"))
+        self.btn_unique.Enable(False)
+        self.btn_uniquesample = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                          label = _("Get sample"))
+        self.btn_uniquesample.Enable(False)
+
+        buttonsizer3 = wx.BoxSizer(wx.HORIZONTAL)
+        buttonsizer3.Add(item = self.btn_uniquesample, proportion = 0,
+                         flag = wx.ALIGN_CENTER_HORIZONTAL | wx.RIGHT, border = 5)
+        buttonsizer3.Add(item = self.btn_unique, proportion = 0,
+                         flag = wx.ALIGN_CENTER_HORIZONTAL)
+
+        valuesizer.Add(item = buttonsizer3, proportion = 0,
+                       flag = wx.TOP, border = 5)
+        
+        # hsizer1.Add(wx.StaticText(self.panel,-1, "Unique values: "), border=0, proportion=1)
+ 
+        hsizer.Add(item = columnsizer, proportion = 1,
+                   flag = wx.EXPAND)
+        hsizer.Add(item = valuesizer, proportion = 1,
+                   flag = wx.EXPAND)
+        
+        self.close_onapply = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
+                                         label = _("Close dialog on apply"))
+        self.close_onapply.SetValue(True)
+ 
+        pagesizer.Add(item = databaseboxsizer,
+                      flag = wx.ALL | wx.EXPAND, border = 5)
+        pagesizer.Add(item = hsizer, proportion = 1,
+                      flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+        # pagesizer.Add(self.btn_uniqe,0,wx.ALIGN_LEFT|wx.TOP,border=5)
+        # pagesizer.Add(self.btn_uniqesample,0,wx.ALIGN_LEFT|wx.TOP,border=5)
+        pagesizer.Add(item = buttonsizer2, proportion = 0,
+                      flag = wx.ALIGN_CENTER_HORIZONTAL)
+        pagesizer.Add(item = sqlboxsizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5)
+        pagesizer.Add(item = buttonsizer, proportion = 0,
+                      flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+        pagesizer.Add(item = self.close_onapply, proportion = 0,
+                      flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+
+        #
+        # bindings
+        #
+        self.btn_unique.Bind(wx.EVT_BUTTON,       self.OnUniqueValues)
+        self.btn_uniquesample.Bind(wx.EVT_BUTTON, self.OnSampleValues)
+        
+        for key, value in self.btn_lv.iteritems():
+            self.FindWindowById(value[1]).Bind(wx.EVT_BUTTON, self.OnAddMark)
+        
+        self.btn_close.Bind(wx.EVT_BUTTON,       self.OnClose)
+        self.btn_clear.Bind(wx.EVT_BUTTON,       self.OnClear)
+        self.btn_verify.Bind(wx.EVT_BUTTON,      self.OnVerify)
+        self.btn_apply.Bind(wx.EVT_BUTTON,       self.OnApply)
+
+        self.list_columns.Bind(wx.EVT_LISTBOX,   self.OnAddColumn)
+        self.list_values.Bind(wx.EVT_LISTBOX,    self.OnAddValue)
+        
+        self.text_sql.Bind(wx.EVT_TEXT,          self.OnText)
+        
+        self.panel.SetAutoLayout(True)
+        self.panel.SetSizer(pagesizer)
+        pagesizer.Fit(self.panel)
+        
+        self.Layout()
+        self.SetMinSize((660, 525))
+        self.SetClientSize(self.panel.GetSize())
+        self.CenterOnParent()
+        
+    def OnUniqueValues(self, event, justsample = False):
+        """!Get unique values"""
+        vals = []
+        try:
+            idx = self.list_columns.GetSelections()[0]
+            column = self.list_columns.GetString(idx)
+        except:
+            self.list_values.Clear()
+            return
+        
+        self.list_values.Clear()
+        
+        querystring = "SELECT %s FROM %s" % (column, self.tablename)
+        
+        data = grass.db_select(table = self.tablename,
+                               sql = querystring,
+                               database = self.database,
+                               driver = self.driver)
+        if not data:
+            return
+
+        desc = self.dbInfo.GetTableDesc(self.dbInfo.GetTable(self.layer))[column]
+        
+        i = 0
+        for item in sorted(map(desc['ctype'], data)):
+            if justsample and i < 256 or \
+               not justsample:
+                if desc['type'] != 'character':
+                    item = str(item)
+                self.list_values.Append(item)
+            else:
+                break
+            i += 1
+        
+    def OnSampleValues(self, event):
+        """!Get sample values"""
+        self.OnUniqueValues(None, True)
+
+    def OnAddColumn(self, event):
+        """!Add column name to the query"""
+        idx = self.list_columns.GetSelections()
+        for i in idx:
+            column = self.list_columns.GetString(i)
+            self._add(element = 'column', value = column)
+        
+        if not self.btn_uniquesample.IsEnabled():
+            self.btn_uniquesample.Enable(True)
+            self.btn_unique.Enable(True)
+        
+    def OnAddValue(self, event):
+        """!Add value"""
+        selection = self.list_values.GetSelections()
+        if not selection:
+            event.Skip()
+            return
+
+        idx = selection[0]
+        value = self.list_values.GetString(idx)
+        idx = self.list_columns.GetSelections()[0]
+        column = self.list_columns.GetString(idx)
+        
+        ctype = self.dbInfo.GetTableDesc(self.dbInfo.GetTable(self.layer))[column]['type']
+        
+        if ctype == 'character':
+            value = "'%s'" % value
+        
+        self._add(element = 'value', value = value)
+
+    def OnAddMark(self, event):
+        """!Add mark"""
+        mark = None
+        for key, value in self.btn_lv.iteritems():
+            if event.GetId() == value[1]:
+                mark = value[0]
+                break
+        
+        self._add(element = 'mark', value = mark)
+
+    def _add(self, element, value):
+        """!Add element to the query
+
+        @param element element to add (column, value)
+        """
+        sqlstr = self.text_sql.GetValue()
+        newsqlstr = ''
+        if element == 'column':
+            if self.radio_cv.GetSelection() == 0: # -> column
+                idx1 = len('select')
+                idx2 = sqlstr.lower().find('from')
+                colstr = sqlstr[idx1:idx2].strip()
+                if colstr == '*':
+                    cols = []
+                else:
+                    cols = colstr.split(',')
+                if value in cols:
+                        cols.remove(value)
+                else:
+                    cols.append(value)
+                
+                if len(cols) < 1:
+                    cols = ['*',]
+                
+                newsqlstr = 'SELECT ' + ','.join(cols) + ' ' + sqlstr[idx2:]
+            else: # -> where
+                newsqlstr = sqlstr
+                if sqlstr.lower().find('where') < 0:
+                    newsqlstr += ' WHERE'
+                
+                newsqlstr += ' ' + value
+        
+        elif element == 'value':
+            newsqlstr = sqlstr + ' ' + value
+        elif element == 'mark':
+            newsqlstr = sqlstr + ' ' + value
+        
+        if newsqlstr:
+            self.text_sql.SetValue(newsqlstr)
+
+    def GetSQLStatement(self):
+        """!Return SQL statement"""
+        return self.text_sql.GetValue().strip().replace("\n"," ")
+    
+    def CloseOnApply(self):
+        """!Return True if the dialog will be close on apply"""
+        return self.close_onapply.IsChecked()
+    
+    def OnText(self, event):
+        """Query string changed"""
+        if len(self.text_sql.GetValue()) > 0:
+            self.btn_verify.Enable(True)
+        else:
+            self.btn_verify.Enable(False)
+        
+    def OnApply(self, event):
+        """Apply button pressed"""
+        if self.evtHeader:
+            self.evtHeader(event = 'apply')
+
+        if self.close_onapply.IsChecked():
+            self.Destroy()
+            
+        event.Skip()
+    
+    def OnVerify(self, event):
+        """!Verify button pressed"""
+        ret, msg = RunCommand('db.select',
+                              getErrorMsg = True,
+                              table = self.tablename,
+                              sql = self.text_sql.GetValue(),
+                              flags = 't',
+                              driver = self.driver,
+                              database = self.database)
+        
+        if ret != 0 and msg:
+            self.statusbar.SetStatusText(_("SQL statement is not valid"), 0)
+            GError(parent = self,
+                   message = _("SQL statement is not valid.\n\n%s") % msg)
+        else:
+            self.statusbar.SetStatusText(_("SQL statement is valid"), 0)
+                        
+    def OnClear(self, event):
+        """!Clear button pressed"""
+        if self.qtype.lower() == "select":
+            self.text_sql.SetValue("SELECT * FROM %s" % self.tablename)
+        else:
+            self.text_sql.SetValue("")
+    
+    def OnClose(self, event):
+        """!Close button pressed"""
+        if self.evtHeader:
+            self.evtHeader(event = 'close')
+        
+        self.Destroy()
+        
+        event.Skip()
+        
+if __name__ == "__main__":
+    if len(sys.argv) != 2:
+        print >>sys.stderr, __doc__
+        sys.exit()
+    
+    import gettext
+    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode=True)
+    
+    app = wx.App(0)
+    sqlb = SQLFrame(parent = None, title = _('SQL Builder'), vectmap = sys.argv[1])
+    sqlb.Show()
+    
+    app.MainLoop()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/vinfo.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/vinfo.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/dbmgr/vinfo.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,164 @@
+"""
+ at package dbmgr.vinfo
+
+ at brief Support classes for Database Manager
+
+List of classes:
+ - vinfo::VectorDBInfo
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import types
+
+import wx
+
+from gui_core.gselect import VectorDBInfo as VectorDBInfoBase
+from core.gcmd        import RunCommand
+from core.settings    import UserSettings
+
+import grass.script as grass
+
+def unicodeValue(value):
+    """!Encode value"""
+    if type(value) == types.UnicodeType:
+        return value
+    
+    enc = UserSettings.Get(group = 'atm', key = 'encoding', subkey = 'value')
+    if enc:
+        value = unicode(value, enc)
+    elif 'GRASS_DB_ENCODING' in os.environ:
+        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(VectorDBInfoBase):
+    """!Class providing information about attribute tables
+    linked to the vector map"""
+    def __init__(self, map):
+        VectorDBInfoBase.__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
+
+        data = grass.vector_what(map = self.map,
+                                 coord = (float(queryCoords[0]), float(queryCoords[1])),
+                                 distance = float(qdist))
+
+        if len(data) < 1 or 'Table' not in data[0]:
+            return None
+        
+        # process attributes
+        table = data[0]['Table']
+        for key, value in data[0]['Attributes'].iteritems():
+            if len(value) < 1:
+                value = None
+            else:
+                if self.tables[table][key]['ctype'] != types.StringType:
+                    value = self.tables[table][key]['ctype'] (value)
+                else:
+                    value = unicodeValue(value)
+            self.tables[table][key]['values'].append(value)
+        
+        ret = dict()
+        for key, value in data[0].iteritems():
+            if key == 'Attributes':
+                continue
+            ret[key] = list()
+            ret[key].append(value)
+        
+        return ret
+    
+    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 = 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/dbmgr/vinfo.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

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	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Attribute_Table_Manager.html	2012-02-19 20:31:20 UTC (rev 50882)
@@ -17,21 +17,12 @@
 <h2>SEE ALSO</h2>
 
 <em>
-  <a href="wxGUI.html">wxGUI</a>
+  <a href="wxGUI.html">wxGUI</a><br>
+  <a href="wxGUI.Components.html">wxGUI components</a>
 </em>
 
 <p>
-Other wxGUI components:<br>
 <em>
-<a href="wxGUI.Vector_Digitizer.html">Vector Digitizer</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.PsMap.html">Cartographic Composer</a><br>
-</em>
-
-<p>
-<em>
   <a href="db.columns.html">db.columns</a>,
   <a href="db.connect.html">db.connect</a>,
   <a href="db.describe.html">db.describe</a>,

Added: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Components.html
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Components.html	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Components.html	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,14 @@
+<p>
+List of <em><a href="wxGUI.html">wxGUI</a></em> components:
+
+<ul>
+  <li><a href="wxGUI.Vector_Digitizer.html">Vector Digitizer</a></li>
+  <li><a href="wxGUI.Attribute_Table_Manager.html">Attribute Table Manager</a></li>
+  <li><a href="wxGUI.Nviz.html">3D Viewer</a></li>
+  <li><a href="wxGUI.Modeler.html">Graphical Modeler</a></li>
+  <li><a href="wxGUI.GCP_Manager.html">Ground Control Points Manager</a></li>
+  <li><a href="wxGUI.PsMap.html">Cartographic Composer</a></li>
+</ul>
+
+<p>
+<i>$Date$</i>


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Components.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.GCP_Manager.html
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.GCP_Manager.html	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.GCP_Manager.html	2012-02-19 20:31:20 UTC (rev 50882)
@@ -292,21 +292,12 @@
 <h2>SEE ALSO</h2>
 
 <em>
-  <a href="wxGUI.html">wxGUI</a>
+  <a href="wxGUI.html">wxGUI</a><br>
+  <a href="wxGUI.Components.html">wxGUI components</a>
 </em>
 
 <p>
-Other wxGUI components:<br>
 <em>
-  <a href="wxGUI.Vector_Digitizer.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.PsMap.html">Cartographic Composer</a><br>
-</em>
-
-<p>
-<em>
   <a href="i.rectify.html">i.rectify</a>,
   <a href="g.transform.html">g.transform</a>
 </em>

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Modeler.html
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Modeler.html	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Modeler.html	2012-02-19 20:31:20 UTC (rev 50882)
@@ -34,25 +34,15 @@
 <h2>SEE ALSO</h2>
 
 <em>
-  <a href="wxGUI.html">wxGUI</a>
+  <a href="wxGUI.html">wxGUI</a><br>
+  <a href="wxGUI.Components.html">wxGUI components</a>
 </em>
 
 <p>
-Other wxGUI components:<br>
-<em>
-<a href="wxGUI.Vector_Digitizer.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.GCP_Manager.html">Manage Ground Control Points</a><br>
-<a href="wxGUI.PsMap.html">Cartographic Composer</a><br>
-</em>
-
-<p>
 See also
 user <a href="http://grass.osgeo.org/wiki/WxGUI_Modeler">wiki</a> page
-which contains
-various <a href="http://grass.osgeo.org/wiki/WxGUI_Modeler#Video_Tutorials">video
-tutorials</a>.
+(especially various <a href="http://grass.osgeo.org/wiki/WxGUI_Modeler#Video_tutorials">video
+tutorials</a>).
 
 <h2>AUTHORS</h2>
 

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Nviz.html
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Nviz.html	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Nviz.html	2012-02-19 20:31:20 UTC (rev 50882)
@@ -13,7 +13,8 @@
 
 <p>
 To start the wxGUI 3D view mode, choose '3D view' from the map
-toolbar.
+toolbar. You can switch between 2D and 3D view. The region in 
+3D view is updated according to displayed region in 2D view.
 <p>
 wxNviz is emphasized on the ease and speed of viewer positioning and
 provided flexibility for using a wide range of data. A low resolution
@@ -38,30 +39,9 @@
 </center>
 
 <dl>
-  <dt><img src="icons/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/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/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/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/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/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/script-save.png">&nbsp;
+    <em>Generate command for m.nviz.image</em></dt>
+  <dd>Generate command for m.nviz.image based on current state.</dd>
   <dt><img src="icons/settings.png">&nbsp;
     <em>Show 3D view mode settings</em></dt>
   <dd>Show dialog with settings for wxGUI 3D view mode. The user
@@ -69,10 +49,6 @@
   <dt><img src="icons/help.png">&nbsp;
     <em>Show help</em></dt>
   <dd>Show this help.</dd>
-  <dt><img src="icons/quit.png">&nbsp;
-    <em>Quit</em></dt>
-  <dd>Quit 3D view mode and switch map display to the 2D view
-  mode.</dd>
 </dl>
 
 <h2>3D View Layer Manager Toolbox</h2>
@@ -81,9 +57,11 @@
 has several tabs:
 
 <ul>
-  <li><b>View</b> for view controling,</li>
+  <li><b>View</b> for view controlling,</li>
   <li><b>Data</b> for data properties,</li>
   <li><b>Appearance</b> for appearance settings (lighting, fringes, ...).</li>
+  <li><b>Analysis</b> for various data analyses (only cutting planes so far).</li>
+  <li><b>Animation</b> for creating simple animations.</li>
 </ul>
 
 <h3>View</h3>
@@ -92,15 +70,26 @@
   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
-  at</em> choice control.
+  the current eye position. Another way to change eye position is
+  to press the buttons around the position box representing cardinal
+  and ordinal directions. 
+  
+<p>
+There are four other buttons for view control in the bottom of this panel
+(following label <em>Look:</em>):
+<ul>
+  <li><em>here</em> requires you to click on Map Display Window to determine
+   the point to look at.</li>
+   <li><em>center</em> changes the point you are looking at to the center.</li>
+   <li><em>top</em> moves the current eye position above the map center.</li>
+   <li><em>reset</em> returns all current view settings to their default values.</li>
+</ul>
 
 <center>
   <br><img src="wxGUI_nviz_tools_view.jpg" border="1"><br><br>
 </center>
 
-You can adjust the viewer's height above the scene, angle of view or
+You can adjust the viewer's height above the scene, perspective and
 twist value to rotate the scene about the horizontal axis. An angle of
 0 is flat. The scene rotates between -90 and 90 degrees.
 
@@ -109,43 +98,35 @@
 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.
+View parameters can be controlled by sliders or edited directly in text box.
+It's possible to enter values which are out of slider's range (and it will 
+adjust then).
 
-<h3>Data properties - Surface</h3>
+<h4>Fly-through mode</h4>
+View can be changed in fly-through mode (can be activated in Map Display toolbar),
+which enables to change the view smoothly and therefore it is suitable
+for creating animation (see below). To start flying, press left mouse button
+and hold it down to continue flying. Flight direction is controlled by mouse cursor
+position on screen. Flight speed can be increased/decreased stepwise by keys
+PageUp/PageDown, Home/End or Up/Down arrows.
+Speed is increased multiple times while Shift key is held down. Holding down
+Ctrl key switches flight mode in the way that position of viewpoint is
+changed (not the direction).
 
-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:
+<h3>Data properties</h3> 
+This tab allows to control parameters related to map layers. It consists
+of four collapsible panels - <em>Surface</em>, <em>Constant surface</em>, 
+<em>Vector</em> and <em>Volume</em>.
 
-<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>
+<h4>Surface</h4>
 
-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.
-
-<center>
-  <br><img src="wxGUI_nviz_tools_surface.jpg" border="1"><br><br>
-</center>
-
+Each active raster map layer from the current layer tree is displayed
+as surface in the 3D space. This panel controls how loaded surfaces are drawn.
+To change parameters of a surface, it must be selected in the very top part of the
+panel.
+<p>
+The top half of the panel has drawing style options.
 Surface can be drawn as a wire mesh or using filled polygons (most
 realistic). You can set draw <b>mode</b> to <em>coarse</em> (fast
 display mode), <em>fine</em> (draws surface as filled polygons with
@@ -170,10 +151,43 @@
 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 "Set to all".
 
-<h3>Data properties - Vector</h3>
+<p>
+The bottom half of the panel has options to set, unset or modify attributes
+of the current surface. Separate raster data or constants can be
+used for various attributes of the surface:
+<ul>
+  <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 100 (transparent).</li>
+  <li><b>shininess</b> - raster map or constant value that controls
+    the shininess (reflectivity) of the current surface. Range from 0 to
+    100.</li>
+</ul>
 
+<p>
+In the very bottom part of the panel position of surface can be set.
+To move the surface right (looking from the south) choose <em>X</em> axis
+and set some positive value. To reset the surface position press
+<em>Reset</em> button.
+
+<center>
+  <br><img src="wxGUI_nviz_tools_surface.jpg" border="1"><br><br>
+</center>
+
+<h4>Constant surface</h4>
+It is possible to add constant surface and set its properties like 
+fine resolution, value (height), color and transparency. It behaves 
+similarly to surface but it has less options.
+
+<h4>Vector</h4>
+
 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.
@@ -186,14 +200,12 @@
 You can define the width (in pixels) of the line features, the color
 used for lines or point markers.
 
-<center>
-  <br><img src="wxGUI_nviz_tools_vector.jpg" border="1"><br><br>
-</center>
-
+<p>
 If vector map is 2D you can display vector features as flat at a
 specified elevation or drape it over a surface(s) at a specified
 height. Use the height control to set the flat elevation or the drape
-height above the surface(s).
+height above the surface(s). In case of multiple surfaces it is possible
+to specify which surfaces is the vector map draped over.
 
 <p>
 For display purposes, it is better to set the height slightly above
@@ -201,10 +213,10 @@
 disappear into the surface(s).
 
 <p>
-For 2D/3D vector points you can also set the size of the markers and
-the width (in pixels) of the line used to draw the point markers (only
-applies to wire-frame markers). Currently are implemented these
-markers:
+For 2D/3D vector points you can also set the size of the markers.
+<!-- and the width (in pixels) of the line used to draw the point markers (only
+applies to wire-frame markers). -->
+ Currently are implemented these markers:
 
 <ul>
   <li><b>x</b> sets the current points markers to a 2D "X",</li>
@@ -216,14 +228,40 @@
   <li><b>asterisk</b> - 3D line-star.</li>
 </ul>
  
-<h3>Data properties - Volume</h3>
+<p>
+Thematic mapping can be used to determine marker color and size
+(and line color and width).
 
-Volumes can be displayed either as isosurfaces or slices. Various
-attributes of the isosurface can be defined, similarly to surface
+<center>
+  <br><img src="wxGUI_nviz_tools_vector.jpg" border="1"><br><br>
+</center>
+
+<h4>Volume</h4>
+
+Volumes (3D raster maps) can be displayed either as isosurfaces or slices.
+Similarly to surface panel you can define draw <b>shading</b>
+- <em>gouraud</em> (draws the volumes with a smooth shading to blend
+individual cell colors together) and <em>flat</em> (draws the volumes
+with flat shading with one color for every two cells. The volume
+appears faceted). As mentioned above currently are supported two
+visualization modes:
+
+<ul>
+  <li><b>isosurface</b> - the levels of values for drawing the
+  volume(s) as isosurfaces,</li>
+  <li>and <b>slice</b> -  drawing the volume
+  as cross-sections.</li>
+</ul>
+<p>
+The middle part of the panel has controls to add, delete, move up/down selected 
+isosurface or slice. The bottom part differs for isosurface and slice. 
+When choosing isosurface, this part the of panel has options to set, unset
+or modify attributes of the current isosurface. 
+Various attributes of the isosurface can be defined, similarly to surface
 attributes:
 
 <ul>
-  <li><b>level</b> - reference isosurface level (height in map
+  <li><b>isosurface value</b> - reference isosurface value (height in map
   units).</li>
   <li><b>color</b> - raster map or constant color to drape over the
   current volume.</li>
@@ -231,40 +269,79 @@
     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>
+    opaque. Range from 0 (opaque) to 100 (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>
+    100.</li>
 </ul>
 
+In case of volume slice the bottom part of the panel controls the slice 
+attributes (which axis is slice parallel to, position of slice edges,
+transparency). Press button <em>Reset</em> to reset slice position
+attributes.
 <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
-controls to add, delete, move up/down selected isosurface or
-slices. The bottom part has options to set, unset or modify attributes
-of the current isosurface or slice.
+Volumes can be moved the same way like surfaces do.
 
 <center>
   <br><img src="wxGUI_nviz_tools_volume.jpg" border="1"><br><br>
 </center>
 
-Similarly to surface panel you can define draw <b>shading</b>
-- <em>gouraud</em> (draws the volumes with a smooth shading to blend
-individual cell colors together) and <em>flat</em> (draws the volumes
-with flat shading with one color for every two cells. The volume
-appears faceted). As mentioned above currently are supported two
-visualization modes:
+<h3>Analysis</h3>
+<em>Analysis</em> tab contains <em>Cutting planes</em> panel.
 
+<h4>Cutting planes</h4>
+Cutting planes allow to cut surfaces along a plane. You can switch 
+between six planes; to disable cutting planes switch to <em>None</em>.
+Initially the plane is vertical, you can change it to horizontal by setting
+<em>tilt</em> 90 degrees. The <em>X</em> and <em>Y</em> values specify
+the rotation center of plane. You can see better what <em>X</em> and <em>Y</em>
+do when changing <em>rotation</em>. 
+<em>Height</em> parameter has sense only when changing 
+<em>tilt</em> too. Press button <em>Reset</em> to reset current cutting plane.
+<p>
+In case of multiple surfaces you can visualize the cutting plane by
+<em>Shading</em>. Shading is visible only when more than one surface
+is loaded and these surfaces must have the same fine resolution set.
+
+
+
+<h3>Appearance</h3>
+Appearance tab consists of three collapsible panels:
+
 <ul>
-  <li><b>isosurface</b> - the levels of values for drawing the
-  volume(s) as isosurfaces,</li>
-  <li>and <b>slice</b> - the levels of values for drawing the volume
-  as cross-sections.</li>
+  <li><em>Lighting</em> for adjusting light source</li>
+  <li><em>Fringe</em> for drawing fringes
+  <li><em>Decorations</em> to display north arrow and scale bar</li>
 </ul>
+<p>
+The <em>lighting</em> panel enables to change the position of light
+source, light color, brightness and ambient. Light position is controlled 
+similarly to eye position. If option <em>Show light model</em> is enabled
+light model is displayed to visualize the light settings.
 
+<center>
+  <br><img src="wxGUI_nviz_tools_light.jpg" border="1"><br><br>
+</center>
+<p>
+The <em>Fringe</em> panel allows to draw fringes in different directions
+(North & East, South & East, South & West, North & West). It is possible
+to set fringe color and height of the bottom edge.
+<p>
+The <em>Decorations</em> panel enables to display north arrow and simple
+scale bar. North arrow and scale bar length is determined in map units. 
+You can display more than one scale bar.
+
+<h3>Animation</h3>
+Animation panel enables to create a simple animation as a sequence of images.
+Press 'Record' button and start changing the view. Views are
+recorded in given interval (FPS - Frames Per Second). After recording,
+the animation can be replayed. To save the animation, fill in the
+directory and file prefix, choose image format (PPM or TIF) and then
+press 'Save'. Now wait until the last image is generated.
+
+It is recommended to record animations using fly-through mode to achieve
+smooth motion.
+
 <h2>Settings</h2>
 
 This panel has controls which allows user to set default surface,
@@ -276,15 +353,9 @@
 <h2>To be implement</h2>
 
 <ul>
-  <li>Improve intuitive navigation (mouse, fly mode)</li>
-  <li>Animation capabilities</li>
-  <li>Arbitrary cutting planes</li>
-  <li>Labels, decoration, etc.</li>
-  <li>Scripting capabilities</li>
-  <li>Better workspace support (view settings, lighting)
+  <li>Labels, decoration, etc. (Implemented, but not fully functional)</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>
   <li>...</li>
 </ul>
 
@@ -295,21 +366,15 @@
 <h2>SEE ALSO</h2>
 
 <em>
-  <a href="wxGUI.html">wxGUI</a>
+  <a href="wxGUI.html">wxGUI</a><br>
+  <a href="wxGUI.Components.html">wxGUI components</a>
 </em>
 
 <p>
-Other wxGUI components:<br>
-<em>
-  <a href="wxGUI.Vector_Digitizer.html">Vector Digitizer</a><br>
-  <a href="wxGUI.Attribute_Table_Manager.html">Attribute Table Manager</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.PsMap.html">Cartographic Composer</a><br>
-</em>
+See also <a href="http://grass.osgeo.org/wiki/WxNVIZ">wiki</a> page
+(especially various <a href="http://grass.osgeo.org/wiki/WxNVIZ#Video_tutorials">video
+tutorials</a>).
 
-<p>
-See also <a href="http://grass.osgeo.org/wiki/WxNVIZ">wiki</a> page.
 <br><br>
 
 Command-line module <em><a href="m.nviz.image.html">m.nviz.image</a></em>.

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.PsMap.html
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.PsMap.html	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.PsMap.html	2012-02-19 20:31:20 UTC (rev 50882)
@@ -43,6 +43,9 @@
   <li> text
   <li> scalebar
   <li> mapinfo
+  <li> point
+  <li> line
+  <li> rectangle
 </ul>
 
 
@@ -184,19 +187,11 @@
 <h2>SEE ALSO</h2>
 
 <em>
-  <a href="wxGUI.html">wxGUI</a>
+  <a href="wxGUI.html">wxGUI</a><br>
+  <a href="wxGUI.Components.html">wxGUI components</a>
 </em>
 
 <p>
-Other wxGUI components:<br>
-<em>
-  <a href="wxGUI.Vector_Digitizer.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.GCP_Manager.html">Manage Ground Control Points</a><br>
-</em>
-</p>
-<p>
 See also <a href="http://grass.osgeo.org/wiki/WxGUI_Cartographic_Composer">wiki</a> page.
 </p>
 <h2>AUTHORS</h2>

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Vector_Digitizer.html
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Vector_Digitizer.html	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.Vector_Digitizer.html	2012-02-19 20:31:20 UTC (rev 50882)
@@ -184,6 +184,10 @@
 	vector lines in bounding box. This is useful for labeling contour lines.</li>
     </ul>
   </dd>
+  
+  <dt><img src="icons/undo.png">&nbsp;
+    <em>Undo</em></dt>
+  <dd>Undo previous operations.</dd>
 
   <dt><img src="icons/settings.png">&nbsp;
     <em>Settings</em></dt>
@@ -221,28 +225,15 @@
 If the digitizer crashes for some reason you can repair the vector map
 which was left open with the <em>v.build</em> module.
 
-<p>
-<b>Please note that vector digitizer is under active development and
-distributed as &quot;Experimental Prototype&quot;.</b>
-
 <h2>SEE ALSO</h2>
 
 <em>
-  <a href="wxGUI.html">wxGUI</a>
+  <a href="wxGUI.html">wxGUI</a><br>
+  <a href="wxGUI.Components.html">wxGUI components</a>
 </em>
 
 <p>
-Other wxGUI components:<br>
 <em>
-  <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.PsMap.html">Cartographic Composer</a><br>
-</em>
-
-<p>
-<em>
 <a href="v.edit.html">v.edit</a>,
 <a href="v.category.html">v.category</a>
 </em>

Modified: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.html
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.html	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI.html	2012-02-19 20:31:20 UTC (rev 50882)
@@ -1,41 +1,47 @@
 <h2>DESCRIPTION</h2>
 
-<b>wxGUI</b> is a new generation of the GUI for GRASS GIS. It's a successor
-of the <em><a href="gis.m.html">Tcl/Tk GUI</a></em> from GRASS 6.
+<b>wxGUI</b> is a new generation of the <em>Graphical User
+Interface</em> (GUI) for GRASS GIS written
+in <a href="http://www.python.org">Python</a>
+using <a href="http://www.wxpython.org">wxPython</a> library. It's a
+successor of the <em><a href="gis.m.html">Tcl/Tk GUI</a></em> from
+GRASS 6.
 
 <p>
 If wxGUI is not your default GUI, you can define it as default by
-typing at GRASS command line prompt
+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
+Alternatively it may be defined in GISRC file
+(<tt>$HOME/.grassrc6</tt> on GNU/Linux, <tt>$APPDATA\GRASS6\grassrc6</tt>
+on MS Windows) by <tt>GRASS_GUI</tt> variable
 
 <div class="code"><pre>
-    GRASS_GUI: wxpython
+GRASS_GUI: wxpython
 </pre></div>
 
 The GUI can be quit by selecting the 'File->Exit' menu item. The GUI
 can be restarted from the GRASS command line prompt by typing
 
 <div class="code"><pre>
-    g.gui wxpython
+g.gui wxpython
 </pre></div>
 
-or  to restart with previously saved workspace type:
+To restart with previously saved workspace file:
 
 <div class="code"><pre>
-    g.gui wxpython workspace=file.gxw
+g.gui wxpython workspace=file.gxw
 </pre></div>
 
 <p>
-You can also start GRASS from the shell command line with wxGUI
-defined by the <tt>-wxpython</tt> switch:
+The user can also start GRASS from the shell command line with the wxGUI
+specifying the <tt>-gui</tt> (or <tt>-wxpython</tt>) switch:
 
 <div class="code"><pre>
-    grass64 -wxpython
+grass65 -gui
 </pre></div>
 
 <p>
@@ -584,18 +590,15 @@
 
 <h2>SEE ALSO</h2>
 
-wxGUI components:<br>
 <em>
-  <a href="wxGUI.Vector_Digitizer.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.PsMap.html">Cartographic Composer</a><br>
+  <a href="wxGUI.Components.html">wxGUI components</a>
 </em>
 
 <p>
-See also wxGUI <a href="http://grass.osgeo.org/wiki/WxGUI">wiki</a> page,
+See also wxGUI <a href="http://grass.osgeo.org/wiki/WxGUI">wiki</a>
+page
+(especially various <a href="http://grass.osgeo.org/wiki/WxGUI#Video_tutorials">video
+tutorials</a>),
 and <a href="http://grass.osgeo.org/wiki/Quick_wxGUI_tutorial">Quick
 wxGUI Tutorial</a>.
 

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

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

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


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/docs/wxGUI_nviz_tools_light.jpg
___________________________________________________________________
Added: svn:mime-type
   + image/jpeg

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/docs/wxGUI_vector_digitizer_toolbar.jpg
===================================================================
(Binary files differ)

Added: grass/branches/releasebranch_6_4/gui/wxpython/gcp/manager.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gcp/manager.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gcp/manager.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,2779 @@
+"""!
+ at package gcp.manager
+
+ at brief Georectification module for GRASS GIS. Includes ground control
+point management and interactive point and click GCP creation
+
+Classes:
+ - manager::GCPWizard
+ - manager::LocationPage
+ - manager::GroupPage
+ - manager::DispMapPage
+ - manager::GCP
+ - manager::GCPList
+ - manager::VectGroup
+ - manager::EditGCP
+ - manager::GrSettingsDialog
+
+(C) 2006-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton
+ at author Updated by Martin Landa <landa.martin gmail.com>
+ at author Markus Metz redesign georectfier -> GCP Manager
+"""
+
+import os
+import sys
+import shutil
+
+import wx
+from wx.lib.mixins.listctrl import CheckListCtrlMixin, ColumnSorterMixin, ListCtrlAutoWidthMixin
+import wx.lib.colourselect as csel
+import wx.wizard           as wiz
+
+import grass.script as grass
+
+from core              import globalvar
+from core              import utils
+from core.render       import Map
+from gui_core.gselect  import Select, LocationSelect, MapsetSelect
+from gui_core.dialogs  import GroupDialog
+from core.gcmd         import RunCommand, GMessage, GError, GWarning
+from core.settings     import UserSettings
+from gcp.mapdisplay    import MapFrame
+
+from location_wizard.wizard  import TitledPage
+
+#
+# global variables
+#
+global src_map
+global tgt_map
+global maptype
+
+src_map = ''
+tgt_map = ''
+maptype = 'cell'
+
+def getSmallUpArrowImage():
+    stream = open(os.path.join(globalvar.ETCIMGDIR, 'small_up_arrow.png'), 'rb')
+    try:
+        img = wx.ImageFromStream(stream)
+    finally:
+        stream.close()
+    return img
+
+def getSmallDnArrowImage():
+    stream = open(os.path.join(globalvar.ETCIMGDIR, 'small_down_arrow.png'), 'rb')
+    try:
+        img = wx.ImageFromStream(stream)
+    finally:
+        stream.close()
+    stream.close()
+    return img
+
+class GCPWizard(object):
+    """
+    Start wizard here and finish wizard here
+    """
+
+    def __init__(self, parent):
+        self.parent = parent # GMFrame
+
+        #
+        # get environmental variables
+        #
+        self.grassdatabase = grass.gisenv()['GISDBASE']
+        
+        #
+        # read original environment settings
+        #
+        self.target_gisrc = os.environ['GISRC']
+        self.gisrc_dict = {}
+        try:
+            f = open(self.target_gisrc, 'r')
+            for line in f.readlines():
+                line = line.replace('\n', '').strip()
+                if len(line) < 1:
+                    continue
+                key, value = line.split(':', 1)
+                self.gisrc_dict[key.strip()] = value.strip()
+        finally:
+            f.close()
+            
+        self.currentlocation = self.gisrc_dict['LOCATION_NAME']
+        self.currentmapset = self.gisrc_dict['MAPSET']
+        # location for xy map to georectify
+        self.newlocation = ''
+        # mapset for xy map to georectify
+        self.newmapset = '' 
+
+        global maptype
+        global src_map
+        global tgt_map
+
+        src_map = ''
+        tgt_map = ''
+        maptype = 'cell'
+
+        # GISRC file for source location/mapset of map(s) to georectify
+        self.source_gisrc = ''
+        self.src_maps = []
+
+        #
+        # define wizard pages
+        #
+        self.wizard = wiz.Wizard(parent=parent, id=wx.ID_ANY, title=_("Setup for georectification"))
+        self.startpage = LocationPage(self.wizard, self)
+        self.grouppage = GroupPage(self.wizard, self)
+        self.mappage = DispMapPage(self.wizard, self)
+
+        #
+        # set the initial order of the pages
+        #
+        self.startpage.SetNext(self.grouppage)
+        self.grouppage.SetPrev(self.startpage)
+        self.grouppage.SetNext(self.mappage)
+        self.mappage.SetPrev(self.grouppage)
+
+        #
+        # do pages layout
+        #
+        self.startpage.DoLayout()
+        self.grouppage.DoLayout()
+        self.mappage.DoLayout()
+        self.wizard.FitToPage(self.startpage)
+
+        # self.Bind(wx.EVT_CLOSE,    self.Cleanup)
+        # self.parent.Bind(wx.EVT_ACTIVATE, self.OnGLMFocus)
+
+        success = False
+
+        #
+        # run wizard
+        #
+        if self.wizard.RunWizard(self.startpage):
+            success = self.OnWizFinished()
+            if success == False:
+                GMessage(parent = self.parent,
+                         message = _("Georectifying setup canceled."))
+                self.Cleanup()
+        else:
+            GMessage(parent = self.parent,
+                     message = _("Georectifying setup canceled."))
+            self.Cleanup()
+
+        #
+        # start GCP display
+        #
+        if success != False:
+            # instance of render.Map to be associated with display
+            self.SwitchEnv('source')
+            self.SrcMap = Map(gisrc=self.source_gisrc) 
+            self.SwitchEnv('target')
+            self.TgtMap = Map(gisrc=self.target_gisrc)
+            self.Map = self.SrcMap
+            
+            #
+            # add layer to source map
+            #
+            if maptype == 'cell':
+                rendertype = 'raster'
+                cmdlist = ['d.rast', 'map=%s' % src_map]
+            else: # -> vector layer
+                rendertype = 'vector'
+                cmdlist = ['d.vect', 'map=%s' % src_map]
+            
+            self.SwitchEnv('source')
+            name, found = utils.GetLayerNameFromCmd(cmdlist)
+            self.SrcMap.AddLayer(type=rendertype, command=cmdlist, l_active=True,
+                                 name=name, l_hidden=False, l_opacity=1.0, l_render=False)
+
+            if tgt_map:
+                #
+                # add layer to target map
+                #
+                if maptype == 'cell':
+                    rendertype = 'raster'
+                    cmdlist = ['d.rast', 'map=%s' % tgt_map]
+                else: # -> vector layer
+                    rendertype = 'vector'
+                    cmdlist = ['d.vect', 'map=%s' % tgt_map]
+                
+                self.SwitchEnv('target')
+                name, found = utils.GetLayerNameFromCmd(cmdlist)
+                self.TgtMap.AddLayer(type=rendertype, command=cmdlist, l_active=True,
+                                     name=name, l_hidden=False, l_opacity=1.0, l_render=False)
+            
+            #
+            # start GCP Manager
+            #
+            self.gcpmgr = GCP(self.parent, grwiz=self, size=globalvar.MAP_WINDOW_SIZE,
+                                               toolbars=["gcpdisp"],
+                                               Map=self.SrcMap, lmgr=self.parent)
+
+            # load GCPs
+            self.gcpmgr.InitMapDisplay()
+            self.gcpmgr.CenterOnScreen()
+            self.gcpmgr.Show()
+            # need to update AUI here for wingrass
+            self.gcpmgr._mgr.Update()
+        else:
+            self.Cleanup()
+                            
+    def SetSrcEnv(self, location, mapset):
+        """!Create environment to use for location and mapset
+        that are the source of the file(s) to georectify
+
+        @param location source location
+        @param mapset source mapset
+
+        @return False on error
+        @return True on success
+        """
+        
+        self.newlocation = location
+        self.newmapset = mapset
+        
+        # check to see if we are georectifying map in current working location/mapset
+        if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset:
+            return False
+        
+        self.gisrc_dict['LOCATION_NAME'] = location
+        self.gisrc_dict['MAPSET'] = mapset
+        
+        self.source_gisrc = utils.GetTempfile()
+
+        try:
+            f = open(self.source_gisrc, mode='w')        
+            for line in self.gisrc_dict.items():
+                f.write(line[0] + ": " + line[1] + "\n")
+        finally:
+            f.close()
+
+        return True
+
+    def SwitchEnv(self, grc):
+        """
+        Switches between original working location/mapset and
+        location/mapset that is source of file(s) to georectify
+        """
+        # check to see if we are georectifying map in current working location/mapset
+        if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset:
+            return False
+
+        if grc == 'target':
+            os.environ['GISRC'] = str(self.target_gisrc)
+        elif grc == 'source':
+            os.environ['GISRC'] = str(self.source_gisrc)
+
+        return True
+    
+    def OnWizFinished(self):
+        # self.Cleanup()
+
+        return True
+        
+    def OnGLMFocus(self, event):
+        """!Layer Manager focus"""
+        # self.SwitchEnv('target')
+        
+        event.Skip()
+
+    def Cleanup(self):
+        """!Return to current location and mapset"""
+        self.SwitchEnv('target')
+        self.parent.gcpmanagement = None
+
+        self.wizard.Destroy()
+
+class LocationPage(TitledPage):
+    """
+    Set map type (raster or vector) to georectify and
+    select location/mapset of map(s) to georectify.
+    """
+    def __init__(self, wizard, parent):
+        TitledPage.__init__(self, wizard, _("Select map type and location/mapset"))
+
+        self.parent = parent
+        self.grassdatabase = self.parent.grassdatabase
+        
+        self.xylocation = ''
+        self.xymapset = ''
+        
+        #
+        # layout
+        #
+        self.sizer.AddGrowableCol(2)
+        # map type
+        self.rb_maptype = wx.RadioBox(parent=self, id=wx.ID_ANY,
+                                      label=' %s ' % _("Map type to georectify"),
+                                      choices=[_('raster'), _('vector')],
+                                      majorDimension=wx.RA_SPECIFY_COLS)
+        self.sizer.Add(item=self.rb_maptype,
+                       flag=wx.ALIGN_CENTER | wx.ALL | wx.EXPAND, border=5,
+                       pos=(1, 1), span=(1, 2))
+
+        # location
+        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 = 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))
+
+        # mapset
+        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 = 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))
+
+        #
+        # bindings
+        #
+        self.Bind(wx.EVT_RADIOBOX, self.OnMaptype, self.rb_maptype)
+        self.Bind(wx.EVT_COMBOBOX, self.OnLocation, self.cb_location)
+        self.Bind(wx.EVT_COMBOBOX, self.OnMapset, self.cb_mapset)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
+        # self.Bind(wx.EVT_CLOSE, self.parent.Cleanup)
+
+    def OnMaptype(self,event):
+        """!Change map type"""
+        global maptype
+
+        if event.GetInt() == 0:
+            maptype = 'cell'
+        else:
+            maptype = 'vector'
+        
+    def OnLocation(self, event):
+        """!Sets source location for map(s) to georectify"""
+        self.xylocation = event.GetString()
+        
+        #create a list of valid mapsets
+        tmplist = os.listdir(os.path.join(self.grassdatabase, self.xylocation))
+        self.mapsetList = []
+        for item in tmplist:
+            if os.path.isdir(os.path.join(self.grassdatabase, self.xylocation, item)) and \
+                os.path.exists(os.path.join(self.grassdatabase, self.xylocation, item, 'WIND')):
+                if item != 'PERMANENT':
+                    self.mapsetList.append(item)
+
+        self.xymapset = 'PERMANENT'
+        utils.ListSortLower(self.mapsetList)
+        self.mapsetList.insert(0, 'PERMANENT')
+        self.cb_mapset.SetItems(self.mapsetList)
+        self.cb_mapset.SetStringSelection(self.xymapset)
+        
+        if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled():
+            wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+    def OnMapset(self, event):
+        """!Sets source mapset for map(s) to georectify"""
+        if self.xylocation == '':
+            GMessage(_('You must select a valid location '
+                       'before selecting a mapset'),
+                     parent = self)
+            return
+
+        self.xymapset = event.GetString()
+        
+        if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled():
+            wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+    def OnPageChanging(self, event=None):
+        if event.GetDirection() and \
+               (self.xylocation == '' or self.xymapset == ''):
+            GMessage(_('You must select a valid location '
+                       'and mapset in order to continue'),
+                     parent = self)
+            event.Veto()
+            return
+        
+        self.parent.SetSrcEnv(self.xylocation, self.xymapset)
+        
+    def OnEnterPage(self, event=None):
+        if self.xylocation == '' or self.xymapset == '':
+            wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+        else:
+            wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+class GroupPage(TitledPage):
+    """
+    Set group to georectify. Create group if desired.
+    """
+    def __init__(self, wizard, parent):
+        TitledPage.__init__(self, wizard, _("Select image/map group to georectify"))
+
+        self.parent = parent
+        
+        self.grassdatabase = self.parent.grassdatabase
+        self.groupList = []
+        
+        self.xylocation = ''
+        self.xymapset = ''
+        self.xygroup = ''
+
+        # default extension
+        self.extension = '.georect' + str(os.getpid())
+
+        #
+        # layout
+        #
+        self.sizer.AddGrowableCol(2)
+        # group
+        self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select group:')),
+                       flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+                       pos=(1, 1))
+        self.cb_group = wx.ComboBox(parent=self, id=wx.ID_ANY,
+                                    choices=self.groupList, size=(350, -1),
+                                    style=wx.CB_DROPDOWN | wx.CB_READONLY)
+        self.sizer.Add(item=self.cb_group,
+                       flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+                       pos=(1, 2))
+        
+        # create group               
+        self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Create group if none exists')),
+                       flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+                       pos=(2, 1))
+        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..."))
+        btnSizer.Add(item=self.btn_mkgroup,
+                     flag=wx.RIGHT, border=5)
+
+        btnSizer.Add(item=self.btn_vgroup,
+                     flag=wx.LEFT, border=5)
+        
+        self.sizer.Add(item=btnSizer,
+                       flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+                       pos=(2, 2))
+        
+        # extension
+        self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Extension for output maps:')),
+                       flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+                       pos=(3, 1))
+        self.ext_txt = wx.TextCtrl(parent=self, id=wx.ID_ANY, value="", size=(350,-1))
+        self.ext_txt.SetValue(self.extension)
+        self.sizer.Add(item=self.ext_txt,
+                       flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+                       pos=(3, 2))
+
+        #
+        # bindings
+        #
+        self.Bind(wx.EVT_COMBOBOX, self.OnGroup, self.cb_group)
+        self.Bind(wx.EVT_TEXT, self.OnExtension, self.ext_txt)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
+        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"""
+        dlg = GroupDialog(parent = self, defaultGroup = self.xygroup)
+
+        dlg.ShowModal()
+        gr = dlg.GetSelectedGroup()
+        if gr in dlg.GetExistGroups():
+            self.xygroup = gr
+        else:
+            gr = ''
+        dlg.Destroy()
+        
+        self.OnEnterPage()
+        self.Update()
+        
+    def OnVGroup(self, event):
+        """!Add vector maps to group"""
+        dlg = VectGroup(parent = self,
+                        id = wx.ID_ANY,
+                        grassdb = self.grassdatabase,
+                        location = self.xylocation,
+                        mapset = self.xymapset,
+                        group = self.xygroup)
+
+        if dlg.ShowModal() != wx.ID_OK:
+            return
+
+        dlg.MakeVGroup()
+        self.OnEnterPage()
+        
+    def OnExtension(self, event):
+        self.extension = event.GetString()
+
+    def OnPageChanging(self, event=None):
+        if event.GetDirection() and self.xygroup == '':
+            GMessage(_('You must select a valid image/map '
+                       'group in order to continue'),
+                     parent = self)
+            event.Veto()
+            return
+
+        if event.GetDirection() and self.extension == '':
+            GMessage(_('You must enter an map name '
+                       'extension in order to continue'),
+                     parent = self)
+            event.Veto()
+            return
+
+    def OnEnterPage(self, event=None):
+        global maptype
+        
+        self.groupList = []
+
+        self.xylocation = self.parent.gisrc_dict['LOCATION_NAME']
+        self.xymapset = self.parent.gisrc_dict['MAPSET']
+
+        # create a list of groups in selected mapset
+        if os.path.isdir(os.path.join(self.grassdatabase,
+                                      self.xylocation,
+                                      self.xymapset,
+                                      'group')):
+            tmplist = os.listdir(os.path.join(self.grassdatabase,
+                                              self.xylocation,
+                                              self.xymapset,
+                                              'group'))
+            for item in tmplist:
+                if os.path.isdir(os.path.join(self.grassdatabase,
+                                              self.xylocation,
+                                              self.xymapset,
+                                              'group',
+                                              item)):
+                    self.groupList.append(item)
+        
+        if maptype == 'cell':
+            self.btn_vgroup.Hide()
+            self.Bind(wx.EVT_BUTTON, self.OnMkGroup, self.btn_mkgroup)
+
+        elif maptype == 'vector':
+            self.btn_vgroup.Show()
+            self.Bind(wx.EVT_BUTTON, self.OnMkGroup, self.btn_mkgroup)
+            self.Bind(wx.EVT_BUTTON, self.OnVGroup, self.btn_vgroup)
+        
+        utils.ListSortLower(self.groupList)
+        self.cb_group.SetItems(self.groupList)
+        
+        if len(self.groupList) > 0:
+            if self.xygroup and self.xygroup in self.groupList:
+                self.cb_group.SetStringSelection(self.xygroup)
+            else:
+                self.cb_group.SetSelection(0)
+                self.xygroup = self.groupList[0]
+        
+        if self.xygroup == '' or \
+                self.extension == '':
+            wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+        else:
+            wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+        
+        # switch to source
+        self.parent.SwitchEnv('source')
+    
+class DispMapPage(TitledPage):
+    """
+    Select ungeoreferenced map to display for interactively
+    setting ground control points (GCPs).
+    """
+    def __init__(self, wizard, parent):
+        TitledPage.__init__(self, wizard,
+                            _("Select maps to display for ground control point (GCP) creation"))
+
+        self.parent = parent
+        global maptype
+
+        #
+        # layout
+        #
+        self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select source map to display:')),
+                       flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+                       pos=(1, 1))
+        
+        self.srcselection = Select(self, id=wx.ID_ANY,
+                                   size=globalvar.DIALOG_GSELECT_SIZE, type=maptype, updateOnPopup = False)
+        
+        self.sizer.Add(item=self.srcselection,
+                       flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+                       pos=(1, 2))
+
+        self.sizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY, label=_('Select target map to display:')),
+                       flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+                       pos=(2, 1))
+
+        self.tgtselection = Select(self, id = wx.ID_ANY,
+                                   size = globalvar.DIALOG_GSELECT_SIZE, type=maptype, updateOnPopup = False)
+        
+        self.sizer.Add(item=self.tgtselection,
+                       flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5,
+                       pos=(2, 2))
+
+        #
+        # bindings
+        #
+        self.srcselection.Bind(wx.EVT_TEXT, self.OnSrcSelection)
+        self.tgtselection.Bind(wx.EVT_TEXT, self.OnTgtSelection)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
+        self.Bind(wx.EVT_CLOSE, self.parent.Cleanup)
+
+    def OnSrcSelection(self,event):
+        """!Source map to display selected"""
+        global src_map
+        global maptype
+
+        src_map = event.GetString()
+
+        if src_map == '':
+            wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+        else:
+            wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+        try:
+        # set computational region to match selected map and zoom display to region
+            if maptype == 'cell':
+                p = RunCommand('g.region', rast='src_map')
+            elif maptype == 'vector':
+                p = RunCommand('g.region', vect='src_map')
+            
+            if p.returncode == 0:
+                print 'returncode = ', str(p.returncode)
+                self.parent.Map.region = self.parent.Map.GetRegion()
+        except:
+            pass
+
+    def OnTgtSelection(self,event):
+        """!Source map to display selected"""
+        global tgt_map
+
+        tgt_map = event.GetString()
+
+    def OnPageChanging(self, event=None):
+        global src_map
+        global tgt_map
+
+        if event.GetDirection() and (src_map == ''):
+            GMessage(_('You must select a source map '
+                       'in order to continue'),
+                     parent = self)
+            event.Veto()
+            return
+
+        self.parent.SwitchEnv('target')
+        
+    def OnEnterPage(self, event=None):
+        global maptype
+        global src_map
+        global tgt_map
+
+        self.srcselection.SetElementList(maptype)
+        ret = RunCommand('i.group',
+                         parent = self,
+                         read = True,
+                         group = self.parent.grouppage.xygroup,
+                         flags = 'g')            
+
+        if ret:
+            self.parent.src_maps = ret.splitlines()
+        else:
+            GError(parent = self,
+                   message = _('No maps in selected group <%s>.\n'
+                               'Please edit group or select another group.') %
+                   self.parent.grouppage.xygroup)
+            return
+
+        # filter out all maps not in group
+        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.parent.SwitchEnv('source')
+
+        if src_map == '':
+            wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+        else:
+            wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+class GCP(MapFrame, ColumnSorterMixin):
+    """!
+    Manages ground control points for georectifying. Calculates RMS statics.
+    Calls i.rectify or v.transform to georectify map.
+    """
+    def __init__(self, parent, grwiz = None, id = wx.ID_ANY,
+                 title = _("Manage Ground Control Points"),
+                 size = (700, 300), toolbars = ["gcpdisp"], Map = None, lmgr = None):
+
+        self.grwiz = grwiz # GR Wizard
+
+        if tgt_map == '':
+            self.show_target = False
+        else:
+            self.show_target = True
+        
+        #wx.Frame.__init__(self, parent, id, title, size = size, name = "GCPFrame")
+        MapFrame.__init__(self, parent = parent, title = title, size = size,
+                            Map = Map, toolbars = toolbars, lmgr = lmgr, name = 'GCPMapWindow')
+
+        #
+        # init variables
+        #
+        self.parent = parent # GMFrame
+        self.parent.gcpmanagement = self
+
+        self.grassdatabase = self.grwiz.grassdatabase
+
+        self.currentlocation = self.grwiz.currentlocation
+        self.currentmapset = self.grwiz.currentmapset
+
+        self.newlocation = self.grwiz.newlocation
+        self.newmapset = self.grwiz.newmapset
+
+        self.xylocation = self.grwiz.gisrc_dict['LOCATION_NAME']
+        self.xymapset = self.grwiz.gisrc_dict['MAPSET']
+        self.xygroup = self.grwiz.grouppage.xygroup
+        self.src_maps = self.grwiz.src_maps
+        self.extension = self.grwiz.grouppage.extension
+        self.outname = ''
+        self.VectGRList = []
+
+        self.file = {
+            'points' : os.path.join(self.grassdatabase,
+                                    self.xylocation,
+                                    self.xymapset,
+                                    'group',
+                                    self.xygroup,
+                                    'POINTS'),
+            'points_bak' : os.path.join(self.grassdatabase,
+                                    self.xylocation,
+                                    self.xymapset,
+                                    'group',
+                                    self.xygroup,
+                                    'POINTS_BAK'),
+            'rgrp' : os.path.join(self.grassdatabase,
+                                  self.xylocation,
+                                  self.xymapset,
+                                  'group',
+                                  self.xygroup,
+                                  'REF'),
+            'vgrp' : os.path.join(self.grassdatabase,
+                                  self.xylocation,
+                                  self.xymapset,
+                                  'group',
+                                  self.xygroup,
+                                  'VREF'),
+            'target' : os.path.join(self.grassdatabase,
+                                    self.xylocation,
+                                    self.xymapset,
+                                    'group',
+                                    self.xygroup,
+                                    'TARGET'),
+            }
+
+        # make a backup of the current points file
+        if os.path.exists(self.file['points']):
+            shutil.copy(self.file['points'], self.file['points_bak'])
+
+        # polynomial order transformation for georectification
+        self.gr_order = 1 
+        # interpolation method for georectification
+        self.gr_method = 'nearest'
+        # region clipping for georectified map
+        self.clip_to_region = False
+        # number of GCPs selected to be used for georectification (checked)
+        self.GCPcount = 0
+        # forward RMS error
+        self.fwd_rmserror = 0.0
+        # backward RMS error
+        self.bkw_rmserror = 0.0
+        # list map coords and ID of map display they came from
+        self.mapcoordlist = []
+        self.mapcoordlist.append([ 0,        # GCP number
+                                   0.0,      # source east
+                                   0.0,      # source north
+                                   0.0,      # target east
+                                   0.0,      # target north
+                                   0.0,      # forward error
+                                   0.0 ] )   # backward error
+
+        # init vars to highlight high RMS errors
+        self.highest_only = True
+        self.show_unused =  True
+        self.highest_key = -1
+        self.rmsthresh = 0
+        self.rmsmean = 0
+        self.rmssd = 0
+
+        self.SetTarget(self.xygroup, self.currentlocation, self.currentmapset)
+
+        self.itemDataMap = None
+
+        # images for column sorting
+        # CheckListCtrlMixin must set an ImageList first
+        self.il = self.list.GetImageList(wx.IMAGE_LIST_SMALL)
+
+        SmallUpArrow = wx.BitmapFromImage(getSmallUpArrowImage())            
+        SmallDnArrow = wx.BitmapFromImage(getSmallDnArrowImage())            
+        self.sm_dn = self.il.Add(SmallDnArrow)
+        self.sm_up = self.il.Add(SmallUpArrow)
+
+        # set mouse characteristics
+        self.mapwin = self.SrcMapWindow
+        self.mapwin.mouse['box'] = 'point'
+        self.mapwin.mouse["use"] == "pointer"
+        self.mapwin.zoomtype = 0
+        self.mapwin.pen = wx.Pen(colour='black', width=2, style=wx.SOLID)
+        self.mapwin.SetCursor(self.cursors["cross"])
+
+        self.mapwin = self.TgtMapWindow
+        
+        # set mouse characteristics
+        self.mapwin.mouse['box'] = 'point'
+        self.mapwin.mouse["use"] == "pointer"
+        self.mapwin.zoomtype = 0
+        self.mapwin.pen = wx.Pen(colour='black', width=2, style=wx.SOLID)
+        self.mapwin.SetCursor(self.cursors["cross"])
+
+        #
+        # show new display & draw map
+        #
+        if self.show_target:
+            self.MapWindow = self.TgtMapWindow
+            self.Map = self.TgtMap
+            self.OnZoomToMap(None)
+
+        self.MapWindow = self.SrcMapWindow
+        self.Map = self.SrcMap
+        self.OnZoomToMap(None)
+
+        #
+        # bindings
+        #
+        self.Bind(wx.EVT_ACTIVATE, self.OnFocus)
+        self.Bind(wx.EVT_CLOSE, self.OnQuit)
+
+    def __del__(self):
+        """!Disable GCP manager mode"""
+        self.parent.gcpmanagement = None
+        
+    def CreateGCPList(self):
+        """!Create GCP List Control"""
+
+        return GCPList(parent=self, gcp=self)
+
+    # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
+    def GetListCtrl(self):
+        return self.list
+        
+    def GetMapCoordList(self):
+        return self.mapcoordlist
+
+    # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
+    def GetSortImages(self):
+        return (self.sm_dn, self.sm_up)
+
+    def GetFwdError(self):
+        return self.fwd_rmserror
+        
+    def GetBkwError(self):
+        return self.bkw_rmserror
+                
+    def InitMapDisplay(self):
+        self.list.LoadData()
+        
+        # initialize column sorter
+        self.itemDataMap = self.mapcoordlist
+        ncols = self.list.GetColumnCount()
+        ColumnSorterMixin.__init__(self, ncols)
+        # init to ascending sort on first click
+        self._colSortFlag = [1] * ncols
+
+    def SetTarget(self, tgroup, tlocation, tmapset):
+        """
+        Sets rectification target to current location and mapset
+        """
+        # check to see if we are georectifying map in current working location/mapset
+        if self.newlocation == self.currentlocation and self.newmapset == self.currentmapset:
+            RunCommand('i.target',
+                       parent = self,
+                       flags = 'c',
+                       group = tgroup)
+        else:
+            self.grwiz.SwitchEnv('source')
+            RunCommand('i.target',
+                       parent = self,
+                       group = tgroup,
+                       location = tlocation,
+                       mapset = tmapset)
+            self.grwiz.SwitchEnv('target')
+
+    def AddGCP(self, event):
+        """
+        Appends an item to GCP list
+        """
+        keyval = self.list.AddGCPItem() + 1
+        # source east, source north, target east, target north, forward error, backward error
+        self.mapcoordlist.append([ keyval,             # GCP number
+                                   0.0,                # source east
+                                   0.0,                # source north
+                                   0.0,                # target east
+                                   0.0,                # target north
+                                   0.0,                # forward error
+                                   0.0 ] )             # backward error
+
+        if self.statusbarManager.GetMode() == 8: # go to
+            self.StatusbarUpdate()
+
+    def DeleteGCP(self, event):
+        """
+        Deletes selected item in GCP list
+        """
+        minNumOfItems = self.OnGROrder(None)
+
+        if self.list.GetItemCount() <= minNumOfItems:
+            GMessage(parent = self,
+                     message=_("At least %d GCPs required. Operation canceled.") % minNumOfItems)
+            return
+
+        key = self.list.DeleteGCPItem()
+        del self.mapcoordlist[key]
+
+        # update key and GCP number
+        for newkey in range(key, len(self.mapcoordlist)):
+            index = self.list.FindItemData(-1, newkey + 1)
+            self.mapcoordlist[newkey][0] = newkey
+            self.list.SetStringItem(index, 0, str(newkey))
+            self.list.SetItemData(index, newkey)
+
+        # update selected
+        if self.list.GetItemCount() > 0:
+            if self.list.selected < self.list.GetItemCount():
+                self.list.selectedkey = self.list.GetItemData(self.list.selected)
+            else:
+                self.list.selected = self.list.GetItemCount() - 1
+                self.list.selectedkey = self.list.GetItemData(self.list.selected)
+                
+            self.list.SetItemState(self.list.selected,
+                              wx.LIST_STATE_SELECTED,
+                              wx.LIST_STATE_SELECTED)
+        else:
+            self.list.selected = wx.NOT_FOUND
+            self.list.selectedkey = -1
+
+        self.UpdateColours()
+
+        if self.statusbarManager.GetMode() == 8: # go to
+            self.StatusbarUpdate()
+            if self.list.selectedkey > 0:
+                self.statusbarManager.SetProperty('gotoGCP', self.list.selectedkey)
+
+    def ClearGCP(self, event):
+        """
+        Clears all values in selected item of GCP list and unchecks it
+        """
+        index = self.list.GetSelected()
+
+        for i in range(4):
+            self.list.SetStringItem(index, i, '0.0')
+        self.list.SetStringItem(index, 4, '')
+        self.list.SetStringItem(index, 5, '')
+        self.list.CheckItem(index, False)
+        key = self.list.GetItemData(index)
+
+        # GCP number, source E, source N, target E, target N, fwd error, bkwd error
+        self.mapcoordlist[key] = [key, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+
+    def DrawGCP(self, coordtype):
+        """
+        Updates GCP and map coord maps and redraws
+        active (checked) GCP markers
+        """
+        self.highest_only = UserSettings.Get(group='gcpman', key='rms', subkey='highestonly')
+
+        self.show_unused =  UserSettings.Get(group='gcpman', key='symbol', subkey='unused')
+        col = UserSettings.Get(group='gcpman', key='symbol', subkey='color')
+        wxLowCol = wx.Colour(col[0], col[1], col[2], 255)
+        col = UserSettings.Get(group='gcpman', key='symbol', subkey='hcolor')
+        wxHiCol = wx.Colour(col[0], col[1], col[2], 255)
+        col = UserSettings.Get(group='gcpman', key='symbol', subkey='scolor')
+        wxSelCol = wx.Colour(col[0], col[1], col[2], 255)
+        col = UserSettings.Get(group='gcpman', key='symbol', subkey='ucolor')
+        wxUnCol = wx.Colour(col[0], col[1], col[2], 255)
+        spx = UserSettings.Get(group='gcpman', key='symbol', subkey='size')
+        wpx = UserSettings.Get(group='gcpman', key='symbol', subkey='width')
+        font = self.GetFont()
+        font.SetPointSize(int(spx) + 2)
+
+        penOrig = polypenOrig = None
+
+        mapWin = None
+        
+        if coordtype == 'source':
+            mapWin = self.SrcMapWindow
+            e_idx = 1
+            n_idx = 2
+        elif coordtype == 'target':
+            mapWin = self.TgtMapWindow
+            e_idx = 3
+            n_idx = 4
+
+        if not mapWin:
+            GError(parent = self,
+                   message="%s%s." % (_("mapwin not defined for "),
+                                      str(idx)))
+            return
+
+        #for gcp in self.mapcoordlist:
+        for idx in range(self.list.GetItemCount()):
+
+            key = self.list.GetItemData(idx)
+            gcp = self.mapcoordlist[key]
+
+            if not self.list.IsChecked(idx):
+                if self.show_unused:
+                    wxCol = wxUnCol
+                else:
+                    continue
+            else:
+                if self.highest_only == True:
+                    if key == self.highest_key:
+                        wxCol = wxHiCol
+                    else:
+                        wxCol = wxLowCol
+                elif self.rmsthresh > 0:
+                    if (gcp[5] > self.rmsthresh):
+                        wxCol = wxHiCol
+                    else:
+                        wxCol = wxLowCol
+
+            if idx == self.list.selected:
+                wxCol = wxSelCol
+
+            if not penOrig:
+                penOrig = mapWin.pen
+                polypenOrig = mapWin.polypen
+                mapWin.pen = wx.Pen(colour=wxCol, width=wpx, style=wx.SOLID)
+                mapWin.polypen = wx.Pen(colour=wxCol, width=wpx, style=wx.SOLID) # ?
+
+            mapWin.pen.SetColour(wxCol)
+            mapWin.polypen.SetColour(wxCol)
+
+            coord = mapWin.Cell2Pixel((gcp[e_idx], gcp[n_idx]))
+            mapWin.DrawCross(pdc=mapWin.pdcTmp, coords=coord,
+                             size=spx, text={ 'text' : '%s' % str(gcp[0]),
+                                            'active' : True,
+                                            'font' : font,
+                                            'color': wxCol,
+                                            'coords': [coord[0] + 5,
+                                                       coord[1] + 5,
+                                                       5,
+                                                       5]})
+            
+        if penOrig:
+            mapWin.pen = penOrig
+            mapWin.polypen = polypenOrig
+        
+    def SetGCPData(self, coordtype, coord, mapdisp=None, confirm=False):
+        """
+        Inserts coordinates from file, mouse click on map, or after editing
+        into selected item of GCP list and checks it for use
+        """
+        
+        index = self.list.GetSelected()
+        if index == wx.NOT_FOUND:
+            return
+
+        coord0 = coord[0]
+        coord1 = coord[1]
+
+        key = self.list.GetItemData(index)
+        if confirm:
+            if self.MapWindow == self.SrcMapWindow:
+                currloc = _("source")
+            else:
+                currloc = _("target")
+            ret = wx.MessageBox(parent=self,
+                                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':
+                self.MapWindow.SetFocus()
+            if ret == wx.NO:
+                return
+            
+        if coordtype == 'source':
+            self.list.SetStringItem(index, 1, str(coord0))
+            self.list.SetStringItem(index, 2, str(coord1))
+            self.mapcoordlist[key][1] = coord[0]
+            self.mapcoordlist[key][2] = coord[1]
+        elif coordtype == 'target':
+            self.list.SetStringItem(index, 3, str(coord0))
+            self.list.SetStringItem(index, 4, str(coord1))
+            self.mapcoordlist[key][3] = coord[0]
+            self.mapcoordlist[key][4] = coord[1]
+            
+        self.list.SetStringItem(index, 5, '0')
+        self.list.SetStringItem(index, 6, '0')
+        self.mapcoordlist[key][5] = 0.0
+        self.mapcoordlist[key][6] = 0.0
+
+        # self.list.ResizeColumns()
+
+    def SaveGCPs(self, event):
+        """
+        Make a POINTS file or save GCP coordinates to existing POINTS file
+        """
+
+        self.GCPcount = 0
+        try:
+            f = open(self.file['points'], mode='w')
+            # use os.linesep or '\n' here ???
+            f.write('# Ground Control Points File\n')
+            f.write("# \n")
+            f.write("# target location: " + self.currentlocation + '\n')
+            f.write("# target mapset: " + self.currentmapset + '\n')
+            f.write("#\tsource\t\ttarget\t\tstatus\n")
+            f.write("#\teast\tnorth\teast\tnorth\t(1=ok, 0=ignore)\n")
+            f.write("#-----------------------     -----------------------     ---------------\n")
+
+            for index in range(self.list.GetItemCount()):
+                if self.list.IsChecked(index) == True:
+                    check = "1"
+                    self.GCPcount += 1
+                else:
+                    check = "0"
+                coord0 = self.list.GetItem(index, 1).GetText()
+                coord1 = self.list.GetItem(index, 2).GetText()
+                coord2 = self.list.GetItem(index, 3).GetText()
+                coord3 = self.list.GetItem(index, 4).GetText()
+                f.write(coord0 + ' ' + coord1 + '     ' + coord2 + ' ' + coord3 + '     ' + check + '\n')
+
+        except IOError, err:
+            GError(parent = self,
+                   message="%s <%s>. %s%s" % (_("Writing POINTS file failed"),
+                                              self.file['points'], os.linesep, err))
+            return
+
+        f.close()
+
+        # if event != None save also to backup file
+        if event:
+            shutil.copy(self.file['points'], self.file['points_bak'])
+            self.parent.goutput.WriteLog(_('POINTS file saved for group <%s>') % self.xygroup)
+            #self.SetStatusText(_('POINTS file saved'))
+
+    def ReadGCPs(self):
+        """
+        Reads GCPs and georectified coordinates from POINTS file
+        """
+        
+        self.GCPcount = 0
+
+        sourceMapWin = self.SrcMapWindow
+        targetMapWin = self.TgtMapWindow
+        #targetMapWin = self.parent.curr_page.maptree.mapdisplay.MapWindow
+
+        if not sourceMapWin:
+            GError(parent = self,
+                   message = "%s. %s%s" % (_("source mapwin not defined"),
+                                           os.linesep, err))
+        
+        if not targetMapWin:
+            GError(parent = self,
+                   message="%s. %s%s" % (_("target mapwin not defined"),
+                                         os.linesep, err))
+        
+        try:
+            f = open(self.file['points'], 'r')
+            GCPcnt = 0
+            
+            for line in f.readlines():
+                if line[0] == '#' or line =='':
+                    continue
+                line = line.replace('\n', '').strip()
+                coords = map(float, line.split())
+                if coords[4] == 1:
+                    check = True
+                    self.GCPcount +=1
+                else:
+                    check = False
+
+                self.AddGCP(event=None)
+                self.SetGCPData('source', (coords[0], coords[1]), sourceMapWin)
+                self.SetGCPData('target', (coords[2], coords[3]), targetMapWin)
+                index = self.list.GetSelected()
+                if index != wx.NOT_FOUND:
+                    self.list.CheckItem(index, check)
+                GCPcnt += 1
+
+        except IOError, err:
+            GError(parent = self,
+                   message = "%s <%s>. %s%s" % (_("Reading POINTS file failed"),
+                                                self.file['points'], os.linesep, err))
+            return
+
+        f.close()
+
+        if GCPcnt == 0:
+            # 3 gcp is minimum
+            for i in range(3):
+                self.AddGCP(None)
+
+        if self.CheckGCPcount():
+            # calculate RMS
+            self.RMSError(self.xygroup, self.gr_order)
+
+    def ReloadGCPs(self, event):
+        """!Reload data from file"""
+
+        # use backup
+        shutil.copy(self.file['points_bak'], self.file['points'])
+
+        # delete all items in mapcoordlist
+        self.mapcoordlist = []
+        self.mapcoordlist.append([ 0,        # GCP number
+                                   0.0,      # source east
+                                   0.0,      # source north
+                                   0.0,      # target east
+                                   0.0,      # target north
+                                   0.0,      # forward error
+                                   0.0 ] )   # backward error
+
+        self.list.LoadData()
+        self.itemDataMap = self.mapcoordlist
+
+        if self._col != -1:
+            self.list.ClearColumnImage(self._col)
+        self._colSortFlag = [1] * self.list.GetColumnCount()
+
+        # draw GCPs (source and target)
+        sourceMapWin = self.SrcMapWindow
+        sourceMapWin.UpdateMap(render=False, renderVector=False)
+        if self.show_target:
+            targetMapWin = self.TgtMapWindow
+            targetMapWin.UpdateMap(render=False, renderVector=False)
+    
+    def OnFocus(self, event):
+        # self.grwiz.SwitchEnv('source')
+        pass
+        
+    def OnRMS(self, event):
+        """
+        RMS button handler
+        """
+        self.RMSError(self.xygroup,self.gr_order)
+
+        sourceMapWin = self.SrcMapWindow
+        sourceMapWin.UpdateMap(render=False, renderVector=False)
+        if self.show_target:
+            targetMapWin = self.TgtMapWindow
+            targetMapWin.UpdateMap(render=False, renderVector=False)
+        
+    def CheckGCPcount(self, msg=False):
+        """
+        Checks to make sure that the minimum number of GCPs have been defined and
+        are active for the selected transformation order
+        """
+        if (self.GCPcount < 3 and self.gr_order == 1) or \
+            (self.GCPcount < 6 and self.gr_order == 2) or \
+            (self.GCPcount < 10 and self.gr_order == 3):
+            if msg:
+                GWarning(parent = self,
+                         message=_('Insufficient points defined and active (checked) '
+                                   'for selected rectification method.\n'
+                                   '3+ points needed for 1st order,\n'
+                                   '6+ points for 2nd order, and\n'
+                                   '10+ points for 3rd order.'))
+                return False
+        else:
+            return True
+
+    def OnGeorect(self, event):
+        """
+        Georectifies map(s) in group using i.rectify or v.transform
+        """
+        global maptype
+        self.SaveGCPs(None)
+        
+        if self.CheckGCPcount(msg=True) == False:
+            return
+
+        if maptype == 'cell':
+            self.grwiz.SwitchEnv('source')
+
+            if self.clip_to_region:
+                flags = "ac"
+            else:
+                flags = "a"
+
+            busy = wx.BusyInfo(message=_("Rectifying images, please wait..."),
+                               parent=self)
+            wx.Yield()
+
+            ret, msg = RunCommand('i.rectify',
+                                  parent = self,
+                                  getErrorMsg = True,
+                                  quiet = True,
+                                  group = self.xygroup,
+                                  extension = self.extension,
+                                  order = self.gr_order,
+                                  method=self.gr_method,
+                                  flags = flags)
+
+            busy.Destroy()
+
+            # provide feedback on failure
+            if ret != 0:
+                print >> sys.stderr, msg
+                
+        elif maptype == 'vector':
+            outmsg = ''
+            # loop through all vectors in VREF
+            # and move resulting vector to target location
+            
+            # make sure current mapset has a vector folder
+            if not os.path.isdir(os.path.join(self.grassdatabase,
+                                              self.currentlocation,
+                                              self.currentmapset,
+                                              'vector')):
+                os.mkdir(os.path.join(self.grassdatabase,
+                                      self.currentlocation,
+                                      self.currentmapset,
+                                      'vector'))
+
+            self.grwiz.SwitchEnv('source')
+            
+            # make list of vectors to georectify from VREF
+            f = open(self.file['vgrp'])
+            vectlist = []
+            try:
+                for vect in f.readlines():
+                    vect = vect.strip('\n')
+                    if len(vect) < 1:
+                        continue
+                    vectlist.append(vect)
+            finally:
+                f.close()
+                               
+            # georectify each vector in VREF using v.transform
+            for vect in vectlist:
+                self.outname = vect + '_' + self.extension
+                self.parent.goutput.WriteLog(text = _('Transforming <%s>...') % vect,
+                                             switchPage = True)
+                msg = err = ''
+                
+                ret, out, err = RunCommand('v.transform',
+                                           overwrite = True,
+                                           input = vect,
+                                           output = self.outname,
+                                           pointsfile = self.file['points'],
+                                           getErrorMsg = True, read = True) 
+                
+                if ret == 0:
+                    self.VectGRList.append(self.outname)
+                    # 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. 
+                # Does not copy from xy location to current location
+                # TODO: replace $GISDBASE etc with real paths
+                #                xyLayer = []
+                #                for layer in grass.vector_db(map = vect).itervalues():
+                #                    xyLayer.append((layer['driver'],
+                #                                    layer['database'],
+                #                                    layer['table']))
+                    
+                    
+                    #                dbConnect = grass.db_connection()
+                    #                print 'db connection =', dbConnect
+                    #                for layer in xyLayer:     
+                    #                    self.parent.goutput.RunCmd(['db.copy',
+                    #                                                '--q',
+                    #                                                '--o',
+                    #                                                'from_driver=%s' % layer[0],
+                    #                                                'from_database=%s' % layer[1],
+                    #                                                'from_table=%s' % layer[2],
+                    #                                                'to_driver=%s' % dbConnect['driver'],
+                    #                                                'to_database=%s' % dbConnect['database'],
+                    #                                                'to_table=%s' % layer[2] + '_' + self.extension])
+
+            # copy all georectified vectors from source location to current location
+            for name in self.VectGRList:
+                xyvpath = os.path.join(self.grassdatabase,
+                                       self.xylocation,
+                                       self.xymapset,
+                                       'vector',
+                                       name)
+                vpath = os.path.join(self.grassdatabase,
+                                     self.currentlocation,
+                                     self.currentmapset,
+                                     'vector',
+                                     name)
+                                    
+                if os.path.isdir(vpath):
+                    self.parent.goutput.WriteWarning(_('Vector map <%s> already exists. '
+                                                       'Change extension name and '
+                                                       'georectify again.') % self.outname)
+                    break
+                else:
+                    # use shutil.copytree() because shutil.move() deletes src dir
+                    shutil.copytree(xyvpath, vpath)
+
+                # TODO: connect vectors to copied tables with v.db.connect
+                                                   
+            GMessage(_('For all vector maps georectified successfully,') + '\n' +
+                     _('you will need to copy any attribute tables') + '\n' +
+                     _('and reconnect them to the georectified vectors'),
+                     parent = self)
+        
+        self.grwiz.SwitchEnv('target')
+
+    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):
+        """!GCP Manager settings"""
+        dlg = GrSettingsDialog(parent=self, id=wx.ID_ANY, title=_('GCP Manager settings'))
+        
+        if dlg.ShowModal() == wx.ID_OK:
+            pass
+        
+        dlg.Destroy()
+
+    def UpdateColours(self, srcrender=False, srcrenderVector=False,
+                            tgtrender=False, tgtrenderVector=False):
+        """!update colours"""
+        highest_fwd_err = 0.0
+        self.highest_key = 0
+        highest_idx = 0
+
+        for index in range(self.list.GetItemCount()):
+            if self.list.IsChecked(index):
+                key = self.list.GetItemData(index)
+                fwd_err = self.mapcoordlist[key][5]
+
+                if self.highest_only == True:
+                    self.list.SetItemTextColour(index, wx.BLACK)
+                    if highest_fwd_err < fwd_err:
+                        highest_fwd_err = fwd_err
+                        self.highest_key = key
+                        highest_idx = index
+                elif self.rmsthresh > 0:
+                    if (fwd_err > self.rmsthresh):
+                        self.list.SetItemTextColour(index, wx.RED)
+                    else:
+                        self.list.SetItemTextColour(index, wx.BLACK)
+            else:
+                self.list.SetItemTextColour(index, wx.BLACK)
+        
+        if self.highest_only and highest_fwd_err > 0.0:
+            self.list.SetItemTextColour(highest_idx, wx.RED)
+
+        sourceMapWin = self.SrcMapWindow
+        sourceMapWin.UpdateMap(render=srcrender, renderVector=srcrenderVector)
+        if self.show_target:
+            targetMapWin = self.TgtMapWindow
+            targetMapWin.UpdateMap(render=tgtrender, renderVector=tgtrenderVector)
+
+    def OnQuit(self, event):
+        """!Quit georectifier"""
+        ret = wx.MessageBox(parent=self,
+                      caption=_("Quit GCP Manager"),
+                      message=_('Save ground control points?'),
+                      style=wx.ICON_QUESTION | wx.YES_NO | wx.CANCEL | wx.CENTRE)
+
+        if ret != wx.CANCEL:
+            if ret == wx.YES:
+                self.SaveGCPs(None)
+            elif ret == wx.NO:
+                # restore POINTS file from backup
+                if os.path.exists(self.file['points_bak']):
+                    shutil.copy(self.file['points_bak'], self.file['points'])
+
+            if os.path.exists(self.file['points_bak']):
+                os.unlink(self.file['points_bak'])
+
+            self.SrcMap.Clean()
+            self.TgtMap.Clean()
+
+            self.grwiz.Cleanup()
+
+            self.Destroy()
+
+        #event.Skip()
+
+    def OnGROrder(self, event):
+        """
+        sets transformation order for georectifying
+        """
+        if event:
+            self.gr_order = event.GetInt() + 1
+
+        numOfItems = self.list.GetItemCount()
+        minNumOfItems = numOfItems
+        
+        if self.gr_order == 1:
+            minNumOfItems = 3
+            # self.SetStatusText(_('Insufficient points, 3+ points needed for 1st order'))
+
+        elif self.gr_order == 2:
+            minNumOfItems = 6
+            diff = 6 - numOfItems
+            # self.SetStatusText(_('Insufficient points, 6+ points needed for 2nd order'))
+
+        elif self.gr_order == 3:
+            minNumOfItems = 10
+            # self.SetStatusText(_('Insufficient points, 10+ points needed for 3rd order'))
+
+        for i in range(minNumOfItems - numOfItems):
+            self.AddGCP(None)
+
+        return minNumOfItems
+    
+    def RMSError(self, xygroup, order):
+        """
+        Uses g.transform to calculate forward and backward error for each used GCP
+        in POINTS file and insert error values into GCP list.
+        Calculates total forward and backward RMS error for all used points
+        """
+        # save GCPs to points file to make sure that all checked GCPs are used
+        self.SaveGCPs(None)
+        #self.SetStatusText('')
+        
+        if self.CheckGCPcount(msg=True) == False:
+            return
+        
+        # get list of forward and reverse rms error values for each point
+        self.grwiz.SwitchEnv('source')
+        
+        ret = RunCommand('g.transform',
+                         parent = self,
+                         read = True,
+                         group = xygroup,
+                         order = order)
+        
+        self.grwiz.SwitchEnv('target')
+
+        if ret:
+            errlist = ret.splitlines()
+        else:
+            GError(parent = self,
+                   message=_('Could not calculate RMS Error.\n'
+                             'Possible error with g.transform.'))
+            return
+        
+        # insert error values into GCP list for checked items
+        sdfactor = float(UserSettings.Get(group='gcpman', key='rms', subkey='sdfactor'))
+        GCPcount = 0
+        sumsq_fwd_err = 0.0
+        sumsq_bkw_err = 0.0
+        sum_fwd_err = 0.0
+        highest_fwd_err = 0.0
+        self.highest_key = 0
+        highest_idx = 0
+        
+        for index in range(self.list.GetItemCount()):
+            key = self.list.GetItemData(index)
+            if self.list.IsChecked(index):
+                fwd_err, bkw_err = errlist[GCPcount].split()
+                self.list.SetStringItem(index, 5, fwd_err)
+                self.list.SetStringItem(index, 6, bkw_err)
+                self.mapcoordlist[key][5] = float(fwd_err)
+                self.mapcoordlist[key][6] = float(bkw_err)
+                self.list.SetItemTextColour(index, wx.BLACK)
+                if self.highest_only:
+                    if highest_fwd_err < float(fwd_err):
+                        highest_fwd_err = float(fwd_err)
+                        self.highest_key = key
+                        highest_idx = index
+                        
+                sumsq_fwd_err += float(fwd_err)**2
+                sumsq_bkw_err += float(bkw_err)**2
+                sum_fwd_err += float(fwd_err)
+                GCPcount += 1
+            else:
+                self.list.SetStringItem(index, 5, '')
+                self.list.SetStringItem(index, 6, '')
+                self.mapcoordlist[key][5] = 0.0
+                self.mapcoordlist[key][6] = 0.0
+                self.list.SetItemTextColour(index, wx.BLACK)
+
+        # SD
+        if GCPcount > 0:
+            sum_fwd_err /= GCPcount
+            self.rmsmean = sum_fwd_err /GCPcount
+            self.rmssd = (((sumsq_fwd_err/GCPcount) - self.rmsmean**2)**0.5)
+            self.rmsthresh = self.rmsmean + sdfactor * self.rmssd
+        else:
+            self.rmsthresh = 0
+            self.rmsmean = 0
+            self.rmssd = 0
+
+        if self.highest_only and highest_fwd_err > 0.0:
+            self.list.SetItemTextColour(highest_idx, wx.RED)
+        elif GCPcount > 0 and self.rmsthresh > 0 and not self.highest_only:
+            for index in range(self.list.GetItemCount()):
+                if self.list.IsChecked(index):
+                    key = self.list.GetItemData(index)
+                    if (self.mapcoordlist[key][5] > self.rmsthresh):
+                        self.list.SetItemTextColour(index, wx.RED)
+            
+        # calculate global RMS error (geometric mean)
+        self.fwd_rmserror = round((sumsq_fwd_err/GCPcount)**0.5,4)
+        self.bkw_rmserror = round((sumsq_bkw_err/GCPcount)**0.5,4)
+        self.list.ResizeColumns()
+
+    def GetNewExtent(self, region, map = None):
+
+        coord_file = utils.GetTempfile()
+        newreg = { 'n' : 0.0, 's' : 0.0, 'e' : 0.0, 'w' : 0.0,}
+
+        try:
+            f = open(coord_file, mode='w')
+            # NW corner        
+            f.write(str(region['e']) + " " + str(region['n']) + "\n")
+            # NE corner        
+            f.write(str(region['e']) + " " + str(region['s']) + "\n")
+            # SW corner        
+            f.write(str(region['w']) + " " + str(region['n']) + "\n")
+            # SE corner        
+            f.write(str(region['w']) + " " + str(region['s']) + "\n")
+        finally:
+            f.close()
+
+        # save GCPs to points file to make sure that all checked GCPs are used
+        self.SaveGCPs(None)
+
+        order = self.gr_order
+        self.gr_order = 1
+
+        if self.CheckGCPcount(msg=True) == False:
+            self.gr_order = order
+            return
+        
+        self.gr_order = order
+
+        # get list of forward and reverse rms error values for each point
+        self.grwiz.SwitchEnv('source')
+        
+        if map == 'source':
+            ret = RunCommand('g.transform',
+                             parent = self,
+                             read = True,
+                             group = self.xygroup,
+                             order = 1,
+                             format = 'dst',
+                             coords = coord_file)
+
+        elif map == 'target':
+            ret = RunCommand('g.transform',
+                             parent = self,
+                             read = True,
+                             group = self.xygroup,
+                             order = 1,
+                             flags = 'r',
+                             format = 'src',
+                             coords = coord_file)
+
+        os.unlink(coord_file)
+        
+        self.grwiz.SwitchEnv('target')
+
+        if ret:
+            errlist = ret.splitlines()
+        else:
+            GError(parent = self,
+                   message=_('Could not calculate new extends.\n'
+                             'Possible error with g.transform.'))
+            return
+
+        # fist corner
+        e, n = errlist[0].split()
+        fe = float(e)
+        fn = float(n)
+        newreg['n'] = fn
+        newreg['s'] = fn
+        newreg['e'] = fe
+        newreg['w'] = fe
+        # other three corners
+        for i in range(1, 4):
+            e, n = errlist[i].split()
+            fe = float(e)
+            fn = float(n)
+            if fe < newreg['w']:
+                newreg['w'] = fe
+            if fe > newreg['e']:
+                newreg['e'] = fe
+            if fn < newreg['s']:
+                newreg['s'] = fn
+            if fn > newreg['n']:
+                newreg['n'] = fn
+
+        return newreg
+
+    def OnHelp(self, event):
+        """!Show GCP Manager manual page"""
+        cmdlist = ['g.manual', 'entry=wxGUI.GCP_Manager']
+        self.parent.goutput.RunCmd(cmdlist, compReg=False,
+                                       switchPage=False)
+
+    def OnUpdateActive(self, event):
+
+        if self.activemap.GetSelection() == 0:
+            self.MapWindow = self.SrcMapWindow
+            self.Map = self.SrcMap
+        else:
+            self.MapWindow = self.TgtMapWindow
+            self.Map = self.TgtMap
+
+        self.UpdateActive(self.MapWindow)
+        # for wingrass
+        if os.name == 'nt':
+            self.MapWindow.SetFocus()
+
+    def UpdateActive(self, win):
+
+        # optionally disable tool zoomback tool
+        self.GetMapToolbar().Enable('zoomback', enable = (len(self.MapWindow.zoomhistory) > 1))
+
+        if self.activemap.GetSelection() != (win == self.TgtMapWindow):
+            self.activemap.SetSelection(win == self.TgtMapWindow)
+        self.StatusbarUpdate()
+
+    def AdjustMap(self, newreg):
+        """!Adjust map window to new extents
+        """
+
+        # adjust map window
+        self.Map.region['n'] = newreg['n']
+        self.Map.region['s'] = newreg['s']
+        self.Map.region['e'] = newreg['e']
+        self.Map.region['w'] = newreg['w']
+
+        self.MapWindow.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                 self.Map.region['e'], self.Map.region['w'])
+
+        # LL locations
+        if self.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
+        
+        # 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()
+
+        self.MapWindow.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                 self.Map.region['e'], self.Map.region['w'])
+
+        if self.MapWindow.redrawAll is False:
+            self.MapWindow.redrawAll = True
+
+        self.MapWindow.UpdateMap()
+        self.StatusbarUpdate()
+
+    def OnZoomToSource(self, event):
+        """!Set target map window to match extents of source map window
+        """
+
+        if not self.MapWindow == self.TgtMapWindow:
+            self.MapWindow = self.TgtMapWindow
+            self.Map = self.TgtMap
+            self.UpdateActive(self.TgtMapWindow)
+
+        # get new N, S, E, W for target
+        newreg = self.GetNewExtent(self.SrcMap.region, 'source')
+        if newreg:
+            self.AdjustMap(newreg)
+
+    def OnZoomToTarget(self, event):
+        """!Set source map window to match extents of target map window
+        """
+
+        if not self.MapWindow == self.SrcMapWindow:
+            self.MapWindow = self.SrcMapWindow
+            self.Map = self.SrcMap
+            self.UpdateActive(self.SrcMapWindow)
+
+        # get new N, S, E, W for target
+        newreg = self.GetNewExtent(self.TgtMap.region, 'target')
+        if newreg:
+            self.AdjustMap(newreg)
+
+    def OnZoomMenuGCP(self, event):
+        """!Popup Zoom menu
+        """
+        point = wx.GetMousePosition()
+        zoommenu = wx.Menu()
+        # Add items to the menu
+
+        zoomsource = wx.MenuItem(zoommenu, wx.ID_ANY, _('Adjust source display to target display'))
+        zoommenu.AppendItem(zoomsource)
+        self.Bind(wx.EVT_MENU, self.OnZoomToTarget, zoomsource)
+
+        zoomtarget = wx.MenuItem(zoommenu, wx.ID_ANY, _('Adjust target display to source display'))
+        zoommenu.AppendItem(zoomtarget)
+        self.Bind(wx.EVT_MENU, self.OnZoomToSource, zoomtarget)
+
+        # Popup the menu. If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.PopupMenu(zoommenu)
+        zoommenu.Destroy()
+        
+    def OnDispResize(self, event):
+        """!GCP Map Display resized, adjust Map Windows
+        """
+        if self.GetMapToolbar():
+            srcwidth, srcheight = self.SrcMapWindow.GetSize()
+            tgtwidth, tgtheight = self.TgtMapWindow.GetSize()
+            srcwidth = (srcwidth + tgtwidth) / 2
+            self._mgr.GetPane("target").Hide()
+            self._mgr.Update()
+            self._mgr.GetPane("source").BestSize((srcwidth, srcheight))
+            self._mgr.GetPane("target").BestSize((srcwidth, tgtheight))
+            if self.show_target:
+                self._mgr.GetPane("target").Show()
+            self._mgr.Update()
+        pass
+
+class GCPList(wx.ListCtrl,
+              CheckListCtrlMixin,
+              ListCtrlAutoWidthMixin):
+              
+    def __init__(self, parent, gcp, id=wx.ID_ANY,
+                 pos=wx.DefaultPosition, size=wx.DefaultSize,
+                 style=wx.LC_REPORT | wx.SUNKEN_BORDER | wx.LC_HRULES |
+                 wx.LC_SINGLE_SEL):
+
+        wx.ListCtrl.__init__(self, parent, id, pos, size, style)
+
+        self.gcp = gcp # GCP class
+        self.render = True
+
+        # Mixin settings
+        CheckListCtrlMixin.__init__(self)
+        ListCtrlAutoWidthMixin.__init__(self)
+        # TextEditMixin.__init__(self)
+
+        # tracks whether list items are checked or not
+        self.CheckList = [] 
+
+        self._Create()
+
+        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
+        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
+        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
+
+        self.selected = wx.NOT_FOUND
+        self.selectedkey = -1
+
+    def _Create(self):
+
+        if 0:
+            # normal, simple columns
+            idx_col = 0
+            for col in (_('use'),
+                _('source E'),
+                _('source N'),
+                _('target E'),
+                _('target N'),
+                _('Forward error'),
+                _('Backward error')):
+                self.InsertColumn(idx_col, col)
+                idx_col += 1
+        else:
+            # the hard way: we want images on the column header
+            info = wx.ListItem()
+            info.SetMask(wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE | wx.LIST_MASK_FORMAT)
+            info.SetImage(-1)
+            info.m_format = wx.LIST_FORMAT_LEFT
+
+            idx_col = 0
+            for lbl in (_('use'),
+                _('source E'),
+                _('source N'),
+                _('target E'),
+                _('target N'),
+                _('Forward error'),
+                _('Backward error')):
+                info.SetText(lbl)
+                self.InsertColumnInfo(idx_col, info)
+                idx_col += 1
+
+    def LoadData(self):
+        """!Load data into list"""
+        self.DeleteAllItems()
+
+        self.render = False
+        if os.path.isfile(self.gcp.file['points']):
+            self.gcp.ReadGCPs()
+        else:
+            # 3 gcp is minimum
+            for i in range(3):
+                self.gcp.AddGCP(None)
+
+        # select first point by default
+        self.selected = 0
+        self.selectedkey = self.GetItemData(self.selected)
+        self.SetItemState(self.selected,
+                          wx.LIST_STATE_SELECTED,
+                          wx.LIST_STATE_SELECTED)
+
+        self.ResizeColumns()
+        self.render = True
+
+    def OnCheckItem(self, index, flag):
+        """!Item is checked/unchecked"""
+
+        if self.render:
+            # redraw points
+            sourceMapWin = self.gcp.SrcMapWindow
+            sourceMapWin.UpdateMap(render=False, renderVector=False)
+            if self.gcp.show_target:
+                targetMapWin = self.gcp.TgtMapWindow
+                targetMapWin.UpdateMap(render=False, renderVector=False)
+
+        pass
+    
+    def AddGCPItem(self):
+        """
+        Appends an item to GCP list
+        """
+        self.selectedkey = self.GetItemCount() + 1
+
+        self.Append([str(self.selectedkey),    # GCP number
+                     '0.0',                # source E
+                     '0.0',                # source N
+                     '0.0',                # target E
+                     '0.0',                # target N
+                     '',                   # forward error
+                     ''])                  # backward error
+
+        self.selected = self.GetItemCount() - 1
+        self.SetItemData(self.selected, self.selectedkey)
+
+        self.SetItemState(self.selected,
+                          wx.LIST_STATE_SELECTED,
+                          wx.LIST_STATE_SELECTED)
+
+        self.ResizeColumns()
+
+        return self.selected
+
+    def DeleteGCPItem(self):
+        """
+        Deletes selected item in GCP list
+        """
+        if self.selected == wx.NOT_FOUND:
+            return
+
+        key = self.GetItemData(self.selected)
+        self.DeleteItem(self.selected)
+
+        return key
+        
+    def ResizeColumns(self):
+        """!Resize columns"""
+        minWidth = [90, 120]
+        for i in range(self.GetColumnCount()):
+            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
+            # first column is checkbox, don't set to minWidth
+            if i > 0 and self.GetColumnWidth(i) < minWidth[i > 4]:
+                self.SetColumnWidth(i, minWidth[i > 4])
+
+        self.SendSizeEvent()
+
+    def GetSelected(self):
+        """!Get index of selected item"""
+        return self.selected
+
+    def OnItemSelected(self, event):
+        """
+        Item selected
+        """
+
+        if self.render and self.selected != event.GetIndex():
+            self.selected = event.GetIndex()
+            self.selectedkey = self.GetItemData(self.selected)
+            sourceMapWin = self.gcp.SrcMapWindow
+            sourceMapWin.UpdateMap(render=False, renderVector=False)
+            if self.gcp.show_target:
+                targetMapWin = self.gcp.TgtMapWindow
+                targetMapWin.UpdateMap(render=False, renderVector=False)
+
+        event.Skip()
+
+    def OnItemActivated(self, event):
+        """
+        When item double clicked, open editor to update coordinate values
+        """
+        coords = []
+        index = event.GetIndex()
+        key = self.GetItemData(index)
+        changed = False
+
+        for i in range(1, 5):
+            coords.append(self.GetItem(index, i).GetText())
+
+        dlg = EditGCP(parent=self, id=wx.ID_ANY, data=coords, gcpno=key)
+
+        if dlg.ShowModal() == wx.ID_OK:
+            values = dlg.GetValues() # string
+            
+            if len(values) == 0:
+                GError(parent = self,
+                       message=_("Invalid coordinate value. Operation canceled."))
+            else:
+                for i in range(len(values)):
+                    if values[i] != coords[i]:
+                        self.SetStringItem(index, i + 1, values[i])
+                        changed = True
+
+                if changed:
+                    # reset RMS and update mapcoordlist
+                    self.SetStringItem(index, 5, '')
+                    self.SetStringItem(index, 6, '')
+                    key = self.GetItemData(index)
+                    self.gcp.mapcoordlist[key] = [key,
+                                                  float(values[0]),
+                                                  float(values[1]),
+                                                  float(values[2]),
+                                                  float(values[3]),
+                                                  0.0,
+                                                  0.0]
+                    self.gcp.UpdateColours()
+        
+    def OnColClick(self, event):
+        """!ListCtrl forgets selected item..."""
+        self.selected = self.FindItemData(-1, self.selectedkey)
+        self.SetItemState(self.selected,
+                          wx.LIST_STATE_SELECTED,
+                          wx.LIST_STATE_SELECTED)
+        event.Skip()
+
+class VectGroup(wx.Dialog):
+    """
+    Dialog to create a vector group (VREF file) for georectifying
+
+    @todo Replace by g.group
+    """
+    def __init__(self, parent, id, grassdb, location, mapset, group,
+                 style=wx.DEFAULT_DIALOG_STYLE):
+        
+        wx.Dialog.__init__(self, parent, id, style=style,
+                           title = _("Create vector map group"))
+        
+        self.grassdatabase = grassdb
+        self.xylocation = location
+        self.xymapset = mapset
+        self.xygroup = group
+        
+        #
+        # get list of valid vector directories
+        #
+        vectlist = os.listdir(os.path.join(self.grassdatabase,
+                                           self.xylocation,
+                                           self.xymapset,
+                                           'vector'))
+        for dir in vectlist:
+            if not os.path.isfile(os.path.join(self.grassdatabase,
+                                           self.xylocation,
+                                           self.xymapset,
+                                           'vector',
+                                           dir,
+                                           'coor')):
+                vectlist.remove(dir)
+        
+        utils.ListSortLower(vectlist)
+        
+        # path to vref file
+        self.vgrpfile = os.path.join(self.grassdatabase,
+                                     self.xylocation,
+                                     self.xymapset,
+                                     'group',
+                                     self.xygroup,
+                                     'VREF')
+        
+        #
+        # buttons
+        #
+        self.btnCancel = wx.Button(parent = self,
+                                   id = wx.ID_CANCEL)
+        self.btnOK = wx.Button(parent = self,
+                                   id = wx.ID_OK)
+        self.btnOK.SetDefault()
+
+
+        #
+        # list of vector maps
+        #
+        self.listMap = wx.CheckListBox(parent = self, id = wx.ID_ANY,
+                                      choices = vectlist)
+        
+        if os.path.isfile(self.vgrpfile):
+            f = open(self.vgrpfile)
+            try:
+                checked = []
+                for line in f.readlines():
+                    line = line.replace('\n', '')
+                    if len(line) < 1:
+                        continue
+                    checked.append(line)
+                self.listMap.SetCheckedStrings(checked)
+            finally:
+                f.close()
+                
+        line = wx.StaticLine(parent = self,
+                             id = wx.ID_ANY, size = (20, -1),
+                             style = wx.LI_HORIZONTAL)
+
+        #
+        # layout
+        #
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        box = wx.BoxSizer(wx.HORIZONTAL)
+        box.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                     label = _('Select vector map(s) to add to group:')),
+                flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
+                border = 5)
+
+        box.Add(item = self.listMap,
+                flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
+                border = 5)
+
+        
+        sizer.Add(box, flag = wx.ALIGN_RIGHT | wx.ALL,
+                  border = 3)
+        
+        sizer.Add(item = line, proportion = 0,
+                  flag = wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT,
+                  border = 5)
+        
+        # buttons
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnOK)
+        btnSizer.Realize()
+
+        sizer.Add(item = btnSizer, proportion = 0,
+                  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER,
+                  border = 5)
+        
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+        self.Layout()
+        
+    def MakeVGroup(self):
+        """!Create VREF file"""
+        vgrouplist = []
+        for item in range(self.listMap.GetCount()):
+            if not self.listMap.IsChecked(item):
+                continue
+            vgrouplist.append(self.listMap.GetString(item))
+        
+        f = open(self.vgrpfile, mode='w')
+        try:
+            for vect in vgrouplist:
+                f.write(vect + '\n')
+        finally:
+            f.close()
+        
+class EditGCP(wx.Dialog):
+    def __init__(self, parent, data, gcpno, id=wx.ID_ANY,
+                 title=_("Edit GCP"),
+                 style=wx.DEFAULT_DIALOG_STYLE):
+        """!Dialog for editing GPC and map coordinates in list control"""
+
+        wx.Dialog.__init__(self, parent, id, title=title, style=style)
+
+        panel = wx.Panel(parent=self)
+
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        box = wx.StaticBox (parent=panel, id=wx.ID_ANY,
+                            label=" %s %s " % (_("Ground Control Point No."), str(gcpno)))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        # source coordinates
+        gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
+       
+        self.xcoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
+        self.ycoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
+        self.ecoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
+        self.ncoord = wx.TextCtrl(parent=panel, id=wx.ID_ANY, size=(150, -1))
+
+        # swap source N, target E
+        tmp_coord = data[1]
+        data[1] = data[2]
+        data[2] = tmp_coord
+        
+        row = 0
+        col = 0
+        idx = 0
+        for label, win in ((_("source E:"), self.xcoord),
+                           (_("target E:"), self.ecoord),
+                           (_("source N:"), self.ycoord),
+                           (_("target N:"), self.ncoord)):
+            label = wx.StaticText(parent=panel, id=wx.ID_ANY,
+                                  label=label)
+            gridSizer.Add(item=label,
+                          flag=wx.ALIGN_CENTER_VERTICAL,
+                          pos=(row, col))
+
+            col += 1
+            win.SetValue(str(data[idx]))
+
+            gridSizer.Add(item=win,
+                          pos=(row, col))
+
+            col += 1
+            idx += 1
+
+            if col > 3:
+                row += 1
+                col = 0
+
+        boxSizer.Add(item=gridSizer, proportion=1,
+                  flag=wx.EXPAND | wx.ALL, border=5)
+
+        sizer.Add(item=boxSizer, proportion=1,
+                  flag=wx.EXPAND | wx.ALL, border=5)
+
+        #
+        # buttons
+        #
+        self.btnCancel = wx.Button(panel, wx.ID_CANCEL)
+        self.btnOk = wx.Button(panel, wx.ID_OK)
+        self.btnOk.SetDefault()
+
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnOk)
+        btnSizer.Realize()
+
+        sizer.Add(item=btnSizer, proportion=0,
+                  flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
+
+        panel.SetSizer(sizer)
+        sizer.Fit(self)
+
+    def GetValues(self, columns=None):
+        """!Return list of values (as strings).
+        """
+        valuelist = []
+        try:
+            float(self.xcoord.GetValue())
+            float(self.ycoord.GetValue())
+            float(self.ecoord.GetValue())
+            float(self.ncoord.GetValue())
+        except ValueError:
+            return valuelist
+
+        valuelist.append(self.xcoord.GetValue())
+        valuelist.append(self.ycoord.GetValue())
+        valuelist.append(self.ecoord.GetValue())
+        valuelist.append(self.ncoord.GetValue())
+
+        return valuelist
+
+class GrSettingsDialog(wx.Dialog):
+    def __init__(self, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize,
+                 style=wx.DEFAULT_DIALOG_STYLE):
+        wx.Dialog.__init__(self, parent, id, title, pos, size, style)
+        """
+        Dialog to set profile text options: font, title
+        and font size, axis labels and font size
+        """
+        #
+        # initialize variables
+        #
+        self.parent = parent
+        self.new_src_map = src_map
+        self.new_tgt_map = tgt_map
+        self.sdfactor = 0
+
+        self.symbol = {}
+        
+        self.methods = ["nearest",
+                        "bilinear",
+                        "bilinear_f",
+                        "cubic", 
+                        "cubic_f"]
+
+        # notebook
+        notebook = wx.Notebook(parent=self, id=wx.ID_ANY, style=wx.BK_DEFAULT)
+        self.__CreateSymbologyPage(notebook)
+        self.__CreateRectificationPage(notebook)
+
+        # buttons
+        btnSave = wx.Button(self, wx.ID_SAVE)
+        btnApply = wx.Button(self, wx.ID_APPLY)
+        btnClose = wx.Button(self, wx.ID_CLOSE)
+        btnApply.SetDefault()
+
+        # bindings
+        btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+        btnApply.SetToolTipString(_("Apply changes for the current session"))
+        btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
+        btnSave.SetToolTipString(_("Apply and save changes to user settings file (default for next sessions)"))
+        btnClose.Bind(wx.EVT_BUTTON, self.OnClose)
+        btnClose.SetToolTipString(_("Close dialog"))
+
+        # sizers
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(btnApply, flag=wx.LEFT | wx.RIGHT, border=5)
+        btnSizer.Add(btnSave, flag=wx.LEFT | wx.RIGHT, border=5)
+        btnSizer.Add(btnClose, flag=wx.LEFT | wx.RIGHT, border=5)
+        
+        # sizers
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item=notebook, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
+        mainSizer.Add(item=btnSizer, proportion=0,
+                       flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
+        #              flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
+
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+        
+    def __CreateSymbologyPage(self, notebook):
+        """!Create notebook page with symbology settings"""
+
+        panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
+        notebook.AddPage(page=panel, text=_("Symbology"))
+
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        rmsgridSizer = wx.GridBagSizer(vgap=5, hgap=5)
+        rmsgridSizer.AddGrowableCol(1)
+
+        # highlight only highest forward RMS error
+        self.highlighthighest = wx.CheckBox(parent=panel, id=wx.ID_ANY,
+                                label=_("Highlight highest RMS error only"))
+        hh = UserSettings.Get(group='gcpman', key='rms', subkey='highestonly')
+        self.highlighthighest.SetValue(hh)
+        rmsgridSizer.Add(item=self.highlighthighest, flag=wx.ALIGN_CENTER_VERTICAL, pos=(0, 0))
+
+        # RMS forward error threshold
+        rmslabel = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Highlight RMS error > M + SD * factor:"))
+        rmslabel.SetToolTip(wx.ToolTip(_("Highlight GCPs with an RMS error larger than \n"
+                              "mean + standard deviation * given factor. \n"
+                              "Recommended values for this factor are between 1 and 2.")))
+        rmsgridSizer.Add(item=rmslabel, flag=wx.ALIGN_CENTER_VERTICAL, pos=(1, 0))
+        sdfactor = UserSettings.Get(group='gcpman', key='rms', subkey='sdfactor')
+        self.rmsWin = wx.TextCtrl(parent=panel, id=wx.ID_ANY,
+                       size=(70,-1), style=wx.TE_NOHIDESEL)
+        self.rmsWin.SetValue("%s" % str(sdfactor))
+        if (self.parent.highest_only == True):
+           self.rmsWin.Disable()
+
+        self.symbol['sdfactor'] = self.rmsWin.GetId()
+        rmsgridSizer.Add(item=self.rmsWin, flag=wx.ALIGN_RIGHT, pos=(1, 1))
+        sizer.Add(item=rmsgridSizer, flag=wx.EXPAND | wx.ALL, border=5)
+
+        box = wx.StaticBox(parent=panel, id=wx.ID_ANY,
+                           label=" %s " % _("Symbol settings"))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
+        gridSizer.AddGrowableCol(1)
+
+        #
+        # general symbol color
+        #
+        row = 0
+        label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Color:"))
+        gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+        col = UserSettings.Get(group='gcpman', key='symbol', subkey='color')
+        colWin = csel.ColourSelect(parent=panel, id=wx.ID_ANY,
+                                   colour=wx.Colour(col[0],
+                                                    col[1],
+                                                    col[2],
+                                                    255))
+        self.symbol['color'] = colWin.GetId()
+        gridSizer.Add(item=colWin,
+                      flag=wx.ALIGN_RIGHT,
+                      pos=(row, 1))
+
+        #
+        # symbol color for high forward RMS error
+        #
+        row += 1
+        label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Color for high RMS error:"))
+        gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+        hcol = UserSettings.Get(group='gcpman', key='symbol', subkey='hcolor')
+        hcolWin = csel.ColourSelect(parent=panel, id=wx.ID_ANY,
+                                   colour=wx.Colour(hcol[0],
+                                                    hcol[1],
+                                                    hcol[2],
+                                                    255))
+        self.symbol['hcolor'] = hcolWin.GetId()
+        gridSizer.Add(item=hcolWin,
+                      flag=wx.ALIGN_RIGHT,
+                      pos=(row, 1))
+
+        #
+        # symbol color for selected GCP
+        #
+        row += 1
+        label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Color for selected GCP:"))
+        gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+        scol = UserSettings.Get(group='gcpman', key='symbol', subkey='scolor')
+        scolWin = csel.ColourSelect(parent=panel, id=wx.ID_ANY,
+                                   colour=wx.Colour(scol[0],
+                                                    scol[1],
+                                                    scol[2],
+                                                    255))
+        self.symbol['scolor'] = scolWin.GetId()
+        gridSizer.Add(item=scolWin,
+                      flag=wx.ALIGN_RIGHT,
+                      pos=(row, 1))
+
+        #
+        # symbol color for unused GCP
+        #
+        row += 1
+        label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Color for unused GCPs:"))
+        gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+        ucol = UserSettings.Get(group='gcpman', key='symbol', subkey='ucolor')
+        ucolWin = csel.ColourSelect(parent=panel, id=wx.ID_ANY,
+                                   colour=wx.Colour(ucol[0],
+                                                    ucol[1],
+                                                    ucol[2],
+                                                    255))
+        self.symbol['ucolor'] = ucolWin.GetId()
+        gridSizer.Add(item=ucolWin,
+                      flag=wx.ALIGN_RIGHT,
+                      pos=(row, 1))
+
+        # show unused GCPs
+        row += 1
+        self.showunused = wx.CheckBox(parent=panel, id=wx.ID_ANY,
+                                label=_("Show unused GCPs"))
+        shuu = UserSettings.Get(group='gcpman', key='symbol', subkey='unused')
+        self.showunused.SetValue(shuu)
+        gridSizer.Add(item=self.showunused, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+
+        #
+        # symbol size
+        #
+        row += 1
+        label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Symbol size:"))
+        gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+        symsize = int(UserSettings.Get(group='gcpman', key='symbol', subkey='size'))
+        sizeWin = wx.SpinCtrl(parent=panel, id=wx.ID_ANY,
+                             min=1, max=20)
+        sizeWin.SetValue(symsize)
+        self.symbol['size'] = sizeWin.GetId()
+        gridSizer.Add(item=sizeWin,
+                      flag=wx.ALIGN_RIGHT,
+                      pos=(row, 1))
+        
+        #
+        # symbol width
+        #
+        row += 1
+        label = wx.StaticText(parent=panel, id=wx.ID_ANY, label=_("Line width:"))
+        gridSizer.Add(item=label, flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+        width = int(UserSettings.Get(group='gcpman', key='symbol', subkey='width'))
+        widWin = wx.SpinCtrl(parent=panel, id=wx.ID_ANY,
+                             min=1, max=10)
+        widWin.SetValue(width)
+        self.symbol['width'] = widWin.GetId()
+        gridSizer.Add(item=widWin,
+                      flag=wx.ALIGN_RIGHT,
+                      pos=(row, 1))
+        
+        boxSizer.Add(item=gridSizer, flag=wx.EXPAND)
+        sizer.Add(item=boxSizer, flag=wx.EXPAND | wx.ALL, border=5)
+
+        #
+        # maps to display
+        #
+        # source map to display
+        self.srcselection = Select(panel, id=wx.ID_ANY,
+                                   size=globalvar.DIALOG_GSELECT_SIZE, type='cell', updateOnPopup = False)
+        self.parent.grwiz.SwitchEnv('source')
+        self.srcselection.SetElementList(maptype)
+        # filter out all maps not in group
+        self.srcselection.tcp.GetElementList(elements = self.parent.src_maps)
+
+        # target map to display
+        self.tgtselection = 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()
+
+        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)
+        sizer.Add(item=self.srcselection, proportion=0, 
+                       flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+        self.srcselection.SetValue(src_map)
+        sizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY, label=_('Select target map to display:')),
+                       proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+        sizer.Add(item=self.tgtselection, proportion=0, 
+                       flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+        self.tgtselection.SetValue(tgt_map)
+
+        # bindings
+        self.highlighthighest.Bind(wx.EVT_CHECKBOX, self.OnHighlight)
+        self.rmsWin.Bind(wx.EVT_TEXT, self.OnSDFactor)
+        self.srcselection.Bind(wx.EVT_TEXT, self.OnSrcSelection)
+        self.tgtselection.Bind(wx.EVT_TEXT, self.OnTgtSelection)
+
+        panel.SetSizer(sizer)
+        
+        return panel
+
+    def __CreateRectificationPage(self, notebook):
+        """!Create notebook page with symbology settings"""
+
+        panel = wx.Panel(parent=notebook, id=wx.ID_ANY)
+        notebook.AddPage(page=panel, text=_("Rectification"))
+
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        # transformation order
+        self.rb_grorder = wx.RadioBox(parent=panel, id=wx.ID_ANY,
+                                       label=" %s " % _("Select rectification order"),
+                                       choices=[_('1st order'), _('2nd order'), _('3rd order')],
+                                       majorDimension=wx.RA_SPECIFY_COLS)
+        sizer.Add(item=self.rb_grorder, proportion=0,
+                       flag=wx.EXPAND | wx.ALL, border=5)
+        self.rb_grorder.SetSelection(self.parent.gr_order - 1)
+
+        # interpolation method
+        gridSizer = wx.GridBagSizer(vgap=5, hgap=5)
+        gridSizer.AddGrowableCol(1)
+        gridSizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY, label=_('Select interpolation method:')),
+                       pos=(0,0), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+        self.grmethod = wx.Choice(parent=panel, id=wx.ID_ANY,
+                                  choices = self.methods)
+        gridSizer.Add(item=self.grmethod, pos=(0,1),
+                       flag=wx.ALIGN_RIGHT, border=5)
+        self.grmethod.SetStringSelection(self.parent.gr_method)
+        sizer.Add(item=gridSizer, flag=wx.EXPAND | wx.ALL, border=5)
+
+        # clip to region
+        self.check = wx.CheckBox(parent=panel, id=wx.ID_ANY,
+                                label=_("clip to computational region in target location"))
+        sizer.Add(item=self.check, proportion=0,
+                       flag=wx.EXPAND | wx.ALL, border=5)
+        self.check.SetValue(self.parent.clip_to_region)
+
+        # extension
+        sizer.Add(item=wx.StaticText(parent=panel, id=wx.ID_ANY, label=_('Extension for output maps:')),
+                       proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+        self.ext_txt = wx.TextCtrl(parent=panel, id=wx.ID_ANY, value="", size=(350,-1))
+        self.ext_txt.SetValue(self.parent.extension)
+        sizer.Add(item=self.ext_txt,
+                       proportion=0, flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5)
+
+        # bindings
+        self.ext_txt.Bind(wx.EVT_TEXT, self.OnExtension)
+        self.Bind(wx.EVT_RADIOBOX, self.parent.OnGROrder, self.rb_grorder)
+        self.Bind(wx.EVT_CHOICE, self.OnMethod, self.grmethod)
+        self.Bind(wx.EVT_CHECKBOX, self.OnClipRegion, self.check)
+
+        panel.SetSizer(sizer)
+        
+        return panel
+
+    def OnHighlight(self, event):
+        """!Checkbox 'highlighthighest' checked/unchecked"""
+        if self.highlighthighest.IsChecked():
+            self.parent.highest_only = True
+            self.rmsWin.Disable()
+        else:
+            self.parent.highest_only = False
+            self.rmsWin.Enable()
+
+    def OnSDFactor(self,event):
+        """!New factor for RMS threshold = M + SD * factor"""
+
+        self.sdfactor = float(event.GetString())
+
+        if self.sdfactor <= 0:
+            GError(parent = self,
+                   message=_('RMS threshold factor must be > 0'))
+        elif self.sdfactor < 1:
+            GError(parent = self,
+                   message=_('RMS threshold factor is < 1\n'
+                             'Too many points might be highlighted'))
+        
+    def OnSrcSelection(self,event):
+        """!Source map to display selected"""
+        global src_map
+
+        tmp_map = event.GetString()
+
+        if not tmp_map == '' and not tmp_map == src_map:
+            self.new_src_map = tmp_map
+
+    def OnTgtSelection(self,event):
+        """!Target map to display selected"""
+        global tgt_map
+
+        tmp_map = event.GetString()
+
+        if not tmp_map == tgt_map:
+            self.new_tgt_map = tmp_map
+
+    def OnMethod(self, event):
+        self.parent.gr_method = self.methods[event.GetSelection()]
+
+    def OnClipRegion(self, event):
+        self.parent.clip_to_region = event.IsChecked()
+        
+    def OnExtension(self, event):
+        self.parent.extension = event.GetString()
+
+    def UpdateSettings(self):
+        global src_map
+        global tgt_map
+
+        layers = None
+
+        UserSettings.Set(group='gcpman', key='rms', subkey='highestonly',
+                         value=self.highlighthighest.GetValue())
+        if self.sdfactor > 0:
+            UserSettings.Set(group='gcpman', key='rms', subkey='sdfactor',
+                             value=self.sdfactor)
+
+            self.parent.sdfactor = self.sdfactor
+            if self.parent.rmsthresh > 0:
+                self.parent.rmsthresh = self.parent.mean + self.parent.sdfactor * self.parent.rmssd
+
+        UserSettings.Set(group='gcpman', key='symbol', subkey='color',
+                         value=tuple(wx.FindWindowById(self.symbol['color']).GetColour()))
+        UserSettings.Set(group='gcpman', key='symbol', subkey='hcolor',
+                         value=tuple(wx.FindWindowById(self.symbol['hcolor']).GetColour()))
+        UserSettings.Set(group='gcpman', key='symbol', subkey='scolor',
+                         value=tuple(wx.FindWindowById(self.symbol['scolor']).GetColour()))
+        UserSettings.Set(group='gcpman', key='symbol', subkey='ucolor',
+                         value=tuple(wx.FindWindowById(self.symbol['ucolor']).GetColour()))
+        UserSettings.Set(group='gcpman', key='symbol', subkey='unused',
+                         value=self.showunused.GetValue())
+        UserSettings.Set(group='gcpman', key='symbol', subkey='size',
+                         value=wx.FindWindowById(self.symbol['size']).GetValue())
+        UserSettings.Set(group='gcpman', key='symbol', subkey='width',
+                         value=wx.FindWindowById(self.symbol['width']).GetValue())
+
+        srcrender = False
+        srcrenderVector = False
+        tgtrender = False
+        tgtrenderVector = False
+        if self.new_src_map != src_map:
+            # remove old layer
+            layers = self.parent.grwiz.SrcMap.GetListOfLayers()
+            self.parent.grwiz.SrcMap.DeleteLayer(layers[0])
+            
+            src_map = self.new_src_map
+            cmdlist = ['d.rast', 'map=%s' % src_map]
+            self.parent.grwiz.SwitchEnv('source')
+            name, found = utils.GetLayerNameFromCmd(cmdlist),
+            self.parent.grwiz.SrcMap.AddLayer(type='raster', command=cmdlist, l_active=True,
+                              name=name, l_hidden=False, l_opacity=1.0, l_render=False)
+
+            self.parent.grwiz.SwitchEnv('target')
+            srcrender = True
+
+        if self.new_tgt_map != tgt_map:
+            # remove old layer
+            layers = self.parent.grwiz.TgtMap.GetListOfLayers()
+            if layers:
+                self.parent.grwiz.TgtMap.DeleteLayer(layers[0])
+            tgt_map = self.new_tgt_map
+
+            if tgt_map != '':
+                cmdlist = ['d.rast', 'map=%s' % tgt_map]
+                name, found = utils.GetLayerNameFromCmd(cmdlist)
+                self.parent.grwiz.TgtMap.AddLayer(type='raster', command=cmdlist, l_active=True,
+                                  name=name, l_hidden=False, l_opacity=1.0, l_render=False)
+
+                tgtrender = True
+                if self.parent.show_target == False:
+                    self.parent.show_target = True
+                    self.parent._mgr.GetPane("target").Show()
+                    self.parent._mgr.Update()
+                    self.parent.GetMapToolbar().Enable('zoommenu', enable = True)
+                    self.parent.activemap.Enable()
+                    self.parent.TgtMapWindow.ZoomToMap(layers = self.parent.TgtMap.GetListOfLayers())
+            else: # tgt_map == ''
+                if self.parent.show_target == True:
+                    self.parent.show_target = False
+                    self.parent._mgr.GetPane("target").Hide()
+                    self.parent._mgr.Update()
+                    self.parent.activemap.SetSelection(0)
+                    self.parent.activemap.Enable(False)
+                    self.parent.GetMapToolbar().Enable('zoommenu', enable = False)
+
+        self.parent.UpdateColours(srcrender, srcrenderVector, tgtrender, tgtrenderVector)
+
+    def OnSave(self, event):
+        """!Button 'Save' pressed"""
+        self.UpdateSettings()
+        fileSettings = {}
+        UserSettings.ReadSettingsFile(settings=fileSettings)
+        fileSettings['gcpman'] = UserSettings.Get(group='gcpman')
+        file = UserSettings.SaveToFile(fileSettings)
+        self.parent.parent.goutput.WriteLog(_('GCP Manager settings saved to file \'%s\'.') % file)
+        #self.Close()
+
+    def OnApply(self, event):
+        """!Button 'Apply' pressed"""
+        self.UpdateSettings()
+        #self.Close()
+
+    def OnClose(self, event):
+        """!Button 'Cancel' pressed"""
+        self.Close()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gcp/mapdisplay.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gcp/mapdisplay.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gcp/mapdisplay.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,629 @@
+"""!
+ at package gcp.mapdisplay
+
+ at brief Display to manage ground control points with two toolbars, one
+for various display management functions, one for manipulating GCPs.
+
+Classes:
+- mapdisplay::MapFrame
+
+(C) 2006-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Markus Metz
+"""
+
+import os
+import math
+import platform
+
+from core import globalvar
+import wx
+import wx.aui
+
+from core.render       import EVT_UPDATE_PRGBAR
+from mapdisp.toolbars  import MapToolbar
+from gcp.toolbars      import GCPDisplayToolbar, GCPManToolbar
+from mapdisp.gprint    import PrintOptions
+from core.gcmd         import GMessage
+from gui_core.dialogs  import GetImageHandlers, ImageSizeDialog
+from gui_core.mapdisp  import MapFrameBase
+from core.settings     import UserSettings
+from mapdisp.mapwindow import BufferedWindow
+
+import mapdisp.statusbar as sb
+
+# for standalone app
+cmdfilename = None
+
+class MapFrame(MapFrameBase):
+    """!Main frame for map display window. Drawing takes place in
+    child double buffered drawing window.
+    """
+    def __init__(self, parent=None, title=_("GRASS GIS Manage Ground Control Points"),
+                 toolbars=["gcpdisp"], tree=None, notebook=None, lmgr=None,
+                 page=None, Map=None, auimgr=None, name = 'GCPMapWindow', **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 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
+        """
+        
+        MapFrameBase.__init__(self, parent = parent, title = title, toolbars = toolbars,
+                              Map = Map, auimgr = auimgr, name = name, **kwargs)
+        
+        self._layerManager = lmgr   # Layer Manager object
+        self.tree       = tree      # Layer Manager layer tree object
+        self.page       = page      # Notebook page holding the layer tree
+        self.layerbook  = notebook  # Layer Manager layer tree notebook
+        #
+        # Add toolbars
+        #
+        for toolb in toolbars:
+            self.AddToolbar(toolb)
+
+        self.activemap = self.toolbars['gcpdisp'].togglemap
+        self.activemap.SetSelection(0)
+        
+        self.SrcMap        = self.grwiz.SrcMap       # instance of render.Map
+        self.TgtMap        = self.grwiz.TgtMap       # instance of render.Map
+        self._mgr.SetDockSizeConstraint(0.5, 0.5)
+
+        #
+        # Add statusbar
+        #
+        
+        # items for choice
+        self.statusbarItems = [sb.SbCoordinates,
+                               sb.SbRegionExtent,
+                               sb.SbCompRegionExtent,
+                               sb.SbShowRegion,
+                               sb.SbResolution,
+                               sb.SbDisplayGeometry,
+                               sb.SbMapScale,
+                               sb.SbProjection,
+                               sb.SbGoToGCP,
+                               sb.SbRMSError]
+                            
+        
+        # create statusbar and its manager
+        statusbar = self.CreateStatusBar(number = 4, style = 0)
+        statusbar.SetStatusWidths([-5, -2, -1, -1])
+        self.statusbarManager = sb.SbManager(mapframe = self, statusbar = statusbar)
+        
+        # fill statusbar manager
+        self.statusbarManager.AddStatusbarItemsByClass(self.statusbarItems, mapframe = self, statusbar = statusbar)
+        self.statusbarManager.AddStatusbarItem(sb.SbMask(self, statusbar = statusbar, position = 2))
+        self.statusbarManager.AddStatusbarItem(sb.SbRender(self, statusbar = statusbar, position = 3))
+        
+        self.statusbarManager.SetMode(8) # goto GCP
+        self.statusbarManager.Update()
+        
+
+        #
+        # Init map display (buffered DC & set default cursor)
+        #
+        self.grwiz.SwitchEnv('source')
+        self.SrcMapWindow = BufferedWindow(self, id=wx.ID_ANY,
+                                          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, lmgr=self._layerManager)
+        self.MapWindow = self.SrcMapWindow
+        self.Map = self.SrcMap
+        self.SrcMapWindow.SetCursor(self.cursors["cross"])
+        self.TgtMapWindow.SetCursor(self.cursors["cross"])
+
+        #
+        # initialize region values
+        #
+        self._initMap(map = self.SrcMap) 
+        self._initMap(map = self.TgtMap) 
+
+        #
+        # Bind various events
+        #
+        self.Bind(wx.EVT_ACTIVATE, self.OnFocus)
+        self.Bind(EVT_UPDATE_PRGBAR, self.OnUpdateProgress)
+        self.Bind(wx.EVT_SIZE,     self.OnDispResize)
+        self.activemap.Bind(wx.EVT_CHOICE, self.OnUpdateActive)
+        
+        #
+        # Update fancy gui style
+        #
+        # AuiManager wants a CentrePane, workaround to get two equally sized windows
+        self.list = self.CreateGCPList()
+
+        #self.SrcMapWindow.SetSize((300, 300))
+        #self.TgtMapWindow.SetSize((300, 300))
+        self.list.SetSize((100, 150))
+        self._mgr.AddPane(self.list, wx.aui.AuiPaneInfo().
+                  Name("gcplist").Caption(_("GCP List")).LeftDockable(False).
+                  RightDockable(False).PinButton().FloatingSize((600,200)).
+                  CloseButton(False).DestroyOnClose(True).
+                  Top().Layer(1).MinSize((200,100)))
+        self._mgr.AddPane(self.SrcMapWindow, wx.aui.AuiPaneInfo().
+                  Name("source").Caption(_("Source Display")).Dockable(False).
+                  CloseButton(False).DestroyOnClose(True).Floatable(False).
+                  Centre())
+        self._mgr.AddPane(self.TgtMapWindow, wx.aui.AuiPaneInfo().
+                  Name("target").Caption(_("Target Display")).Dockable(False).
+                  CloseButton(False).DestroyOnClose(True).Floatable(False).
+                  Right().Layer(0))
+
+        srcwidth, srcheight = self.SrcMapWindow.GetSize()
+        tgtwidth, tgtheight = self.TgtMapWindow.GetSize()
+        srcwidth = (srcwidth + tgtwidth) / 2
+        self._mgr.GetPane("target").Hide()
+        self._mgr.Update()
+        self._mgr.GetPane("source").BestSize((srcwidth, srcheight))
+        self._mgr.GetPane("target").BestSize((srcwidth, srcheight))
+        if self.show_target:
+            self._mgr.GetPane("target").Show()
+        else:
+            self.activemap.Enable(False)
+        # needed by Mac OS, does not harm on Linux, breaks display on Windows
+        if platform.system() != 'Windows':
+            self._mgr.Update()
+
+        #
+        # Init print module and classes
+        #
+        self.printopt = PrintOptions(self, self.MapWindow)
+        
+        #
+        # Initialization of digitization tool
+        #
+        self.digit = None
+
+        # set active map
+        self.MapWindow = self.SrcMapWindow
+        self.Map = self.SrcMap
+        
+        # do not init zoom history here, that happens when zooming to map(s)
+
+        #
+        # Re-use dialogs
+        #
+        self.dialogs = {}
+        self.dialogs['attributes'] = None
+        self.dialogs['category'] = None
+        self.dialogs['barscale'] = None
+        self.dialogs['legend'] = None
+
+        self.decorationDialog = None # decoration/overlays
+
+    def AddToolbar(self, name):
+        """!Add defined toolbar to the window
+        
+        Currently known toolbars are:
+         - 'map'     - basic map toolbar
+         - 'vdigit'  - vector digitizer
+         - 'gcpdisp' - GCP Manager, Display
+         - 'gcpman'  - GCP Manager, points management
+         - 'nviz'    - 3D view mode
+        """
+        # default toolbar
+        if name == "map":
+            self.toolbars['map'] = 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'] = GCPDisplayToolbar(self)
+
+            self._mgr.AddPane(self.toolbars['gcpdisp'],
+                              wx.aui.AuiPaneInfo().
+                              Name("gcpdisplaytoolbar").Caption(_("GCP Display toolbar")).
+                              ToolbarPane().Top().
+                              LeftDockable(False).RightDockable(False).
+                              BottomDockable(False).TopDockable(True).
+                              CloseButton(False).Layer(2))
+
+            if self.show_target == False:
+                self.toolbars['gcpdisp'].Enable('zoommenu', enable = False)
+
+            self.toolbars['gcpman'] = GCPManToolbar(self)
+
+            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))
+            
+        self._mgr.Update()
+
+    def OnUpdateProgress(self, event):
+        """
+        Update progress bar info
+        """
+        self.GetProgressBar().SetValue(event.value)
+        
+        event.Skip()
+        
+    def OnFocus(self, event):
+        """
+        Change choicebook page to match display.
+        Or set display for georectifying
+        """
+        if self._layerManager and \
+                self._layerManager.gcpmanagement:
+            # 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 OnDraw(self, event):
+        """!Re-display current map composition
+        """
+        self.MapWindow.UpdateMap(render = False)
+        
+    def OnRender(self, event):
+        """!Re-render map composition (each map layer)
+        """
+        # delete tmp map layers (queries)
+        qlayer = self.Map.GetListOfLayers(l_name=globalvar.QUERYLAYER)
+        for layer in qlayer:
+            self.Map.DeleteLayer(layer)
+
+        self.SrcMapWindow.UpdateMap(render=True)
+        if self.show_target:
+            self.TgtMapWindow.UpdateMap(render=True)
+        
+        # update statusbar
+        self.StatusbarUpdate()
+
+    def OnPointer(self, event):
+        """!Pointer button clicked
+        """
+        # change the cursor
+        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):
+        """
+        Zoom in the map.
+        Set mouse cursor, zoombox attributes, and zoom direction
+        """
+        if self.GetToolbar('map'):
+            self.toolbars['map'].OnTool(event)
+            self.toolbars['map'].action['desc'] = ''
+        
+        self.MapWindow.mouse['use'] = "zoom"
+        self.MapWindow.mouse['box'] = "box"
+        self.MapWindow.zoomtype = 1
+        self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
+        
+        # change the cursor
+        self.MapWindow.SetCursor(self.cursors["cross"])
+
+        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"])
+
+    def OnZoomOut(self, event):
+        """
+        Zoom out the map.
+        Set mouse cursor, zoombox attributes, and zoom direction
+        """
+        if self.GetToolbar('map'):
+            self.toolbars['map'].OnTool(event)
+            self.toolbars['map'].action['desc'] = ''
+        
+        self.MapWindow.mouse['use'] = "zoom"
+        self.MapWindow.mouse['box'] = "box"
+        self.MapWindow.zoomtype = -1
+        self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
+        
+        # change the cursor
+        self.MapWindow.SetCursor(self.cursors["cross"])
+
+        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"])
+
+    def OnPan(self, event):
+        """
+        Panning, set mouse to drag
+        """
+        if self.GetToolbar('map'):
+            self.toolbars['map'].OnTool(event)
+            self.toolbars['map'].action['desc'] = ''
+        
+        self.MapWindow.mouse['use'] = "pan"
+        self.MapWindow.mouse['box'] = "pan"
+        self.MapWindow.zoomtype = 0
+        
+        # change the cursor
+        self.MapWindow.SetCursor(self.cursors["hand"])
+
+        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"])
+
+    def OnErase(self, event):
+        """
+        Erase the canvas
+        """
+        self.MapWindow.EraseMap()
+
+        if self.MapWindow == self.SrcMapWindow:
+            win = self.TgtMapWindow
+        elif self.MapWindow == self.TgtMapWindow:
+            win = self.SrcMapWindow
+
+        win.EraseMap()
+
+    def OnZoomRegion(self, event):
+        """
+        Zoom to region
+        """
+        self.Map.getRegion()
+        self.Map.getResolution()
+        self.UpdateMap()
+        # event.Skip()
+
+    def OnAlignRegion(self, event):
+        """
+        Align region
+        """
+        if not self.Map.alignRegion:
+            self.Map.alignRegion = True
+        else:
+            self.Map.alignRegion = False
+        # event.Skip()
+    
+    def SaveToFile(self, event):
+        """!Save map to image
+        """
+        img = self.MapWindow.img
+        if not img:
+            GMessage(parent = self,
+                     message = _("Nothing to render (empty map). Operation canceled."))
+            return
+        filetype, ltype = GetImageHandlers(img)
+
+        # get size
+        dlg = 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:
+            path = dlg.GetPath()
+            if not path:
+                dlg.Destroy()
+                return
+            
+            base, ext = os.path.splitext(path)
+            fileType = ltype[dlg.GetFilterIndex()]['type']
+            extType  = ltype[dlg.GetFilterIndex()]['ext']
+            if ext != extType:
+                path = base + '.' + extType
+            
+            self.MapWindow.SaveToFile(path, fileType,
+                                      width, height)
+            
+        dlg.Destroy()
+
+    def PrintMenu(self, event):
+        """
+        Print options and output menu for map display
+        """
+        point = wx.GetMousePosition()
+        printmenu = wx.Menu()
+        # Add items to the menu
+        setup = wx.MenuItem(printmenu, wx.ID_ANY, _('Page setup'))
+        printmenu.AppendItem(setup)
+        self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
+
+        preview = wx.MenuItem(printmenu, wx.ID_ANY, _('Print preview'))
+        printmenu.AppendItem(preview)
+        self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
+
+        doprint = wx.MenuItem(printmenu, wx.ID_ANY, _('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 FormatDist(self, dist):
+        """!Format length numbers and units in a nice way,
+        as a function of length. From code by Hamish Bowman
+        Grass Development Team 2006"""
+
+        mapunits = self.Map.projinfo['units']
+        if mapunits == 'metres': mapunits = 'meters'
+        outunits = mapunits
+        dist = float(dist)
+        divisor = 1.0
+
+        # figure out which units to use
+        if mapunits == 'meters':
+            if dist > 2500.0:
+                outunits = 'km'
+                divisor = 1000.0
+            else: outunits = 'm'
+        elif mapunits == 'feet':
+            # nano-bug: we match any "feet", but US Survey feet is really
+            #  5279.9894 per statute mile, or 10.6' per 1000 miles. As >1000
+            #  miles the tick markers are rounded to the nearest 10th of a
+            #  mile (528'), the difference in foot flavours is ignored.
+            if dist > 5280.0:
+                outunits = 'miles'
+                divisor = 5280.0
+            else:
+                outunits = 'ft'
+        elif 'degree' in mapunits:
+            if dist < 1:
+                outunits = 'min'
+                divisor = (1/60.0)
+            else:
+                outunits = 'deg'
+
+        # format numbers in a nice way
+        if (dist/divisor) >= 2500.0:
+            outdist = round(dist/divisor)
+        elif (dist/divisor) >= 1000.0:
+            outdist = round(dist/divisor,1)
+        elif (dist/divisor) > 0.0:
+            outdist = round(dist/divisor,int(math.ceil(3-math.log10(dist/divisor))))
+        else:
+            outdist = float(dist/divisor)
+
+        return (outdist, outunits)
+
+    def OnZoomToRaster(self, event):
+        """!
+        Set display extents to match selected raster map (ignore NULLs)
+        """
+        self.MapWindow.ZoomToMap(ignoreNulls = True)
+        
+    def OnZoomToSaved(self, event):
+        """!Set display geometry to match extents in
+        saved region file
+        """
+        self.MapWindow.ZoomToSaved()
+        
+    def OnDisplayToWind(self, event):
+        """!Set computational region (WIND file) to match display
+        extents
+        """
+        self.MapWindow.DisplayToWind()
+ 
+    def SaveDisplayRegion(self, event):
+        """!Save display extents to named region file.
+        """
+        self.MapWindow.SaveDisplayRegion()
+        
+    def OnZoomMenu(self, event):
+        """!Popup Zoom menu
+        """
+        point = wx.GetMousePosition()
+        zoommenu = wx.Menu()
+        # Add items to the menu
+
+        zoomwind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to computational region (set with g.region)'))
+        zoommenu.AppendItem(zoomwind)
+        self.Bind(wx.EVT_MENU, self.OnZoomToWind, zoomwind)
+
+        zoomdefault = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to default region'))
+        zoommenu.AppendItem(zoomdefault)
+        self.Bind(wx.EVT_MENU, self.OnZoomToDefault, zoomdefault)
+
+        zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to saved region'))
+        zoommenu.AppendItem(zoomsaved)
+        self.Bind(wx.EVT_MENU, self.OnZoomToSaved, zoomsaved)
+
+        savewind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Set computational region from display'))
+        zoommenu.AppendItem(savewind)
+        self.Bind(wx.EVT_MENU, self.OnDisplayToWind, savewind)
+
+        savezoom = wx.MenuItem(zoommenu, wx.ID_ANY, _('Save display geometry to named region'))
+        zoommenu.AppendItem(savezoom)
+        self.Bind(wx.EVT_MENU, self.SaveDisplayRegion, savezoom)
+
+        # Popup the menu. If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.PopupMenu(zoommenu)
+        zoommenu.Destroy()
+        
+        
+    def IsStandalone(self):
+        """!Check if Map display is standalone"""
+        if self._layerManager:
+            return False
+        
+        return True
+    
+    def GetLayerManager(self):
+        """!Get reference to Layer Manager
+
+        @return window reference
+        @return None (if standalone)
+        """
+        return self._layerManager
+    
+    def GetSrcWindow(self):
+        return self.SrcMapWindow
+        
+    def GetTgtWindow(self):
+        return self.TgtMapWindow
+    
+    def GetShowTarget(self):
+        return self.show_target
+        
+    def GetMapToolbar(self):
+        """!Returns toolbar with zooming tools"""
+        return self.toolbars['gcpdisp']


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gcp/toolbars.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gcp/toolbars.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gcp/toolbars.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,152 @@
+"""!
+ at package gcp.toolbars
+
+ at brief Georectification module - toolbars
+
+Classes:
+ - toolbars::GCPManToolbar
+ - toolbars::GCPDisplayToolbar
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Markus Metz
+"""
+
+import os
+import sys
+
+import wx
+
+from core              import globalvar
+from gui_core.toolbars import BaseToolbar, BaseIcons
+from icon              import MetaIcon
+   
+class GCPManToolbar(BaseToolbar):
+    """!Toolbar for managing ground control points
+
+    @param parent reference to GCP widget
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        icons = {
+            'gcpSave'    : MetaIcon(img = 'gcp-save',
+                                    label = _('Save GCPs to POINTS file')),
+            'gcpReload'  : MetaIcon(img = 'reload',
+                                    label = _('Reload GCPs from POINTS file')),
+            'gcpAdd'     : MetaIcon(img = 'gcp-add',
+                                    label = _('Add new GCP')),
+            'gcpDelete'  : MetaIcon(img = 'gcp-delete',
+                                    label = _('Delete selected GCP')),
+            'gcpClear'   : MetaIcon(img = 'gcp-remove',
+                                    label = _('Clear selected GCP')),
+            'gcpRms'     : MetaIcon(img = 'gcp-rms',
+                                    label = _('Recalculate RMS error')),
+            'georectify' : MetaIcon(img = 'georectify',
+                                    label = _('Georectify')),
+            }
+        
+        return self._getToolbarData((('gcpSave', icons["gcpSave"],
+                                      self.parent.SaveGCPs),
+                                     ('gcpReload', icons["gcpReload"],
+                                      self.parent.ReloadGCPs),
+                                     (None, ),
+                                     ('gcpAdd', icons["gcpAdd"],
+                                      self.parent.AddGCP),
+                                     ('gcpDelete', icons["gcpDelete"],
+                                      self.parent.DeleteGCP),
+                                     ('gcpClear', icons["gcpClear"],
+                                      self.parent.ClearGCP),
+                                     (None, ),
+                                     ('rms', icons["gcpRms"],
+                                      self.parent.OnRMS),
+                                     ('georect', icons["georectify"],
+                                      self.parent.OnGeorect))
+                                    )
+    
+class GCPDisplayToolbar(BaseToolbar):
+    """
+    GCP Display toolbar
+    """
+    def __init__(self, parent):
+        """!
+        GCP Display toolbar constructor
+        """
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # add tool to toggle active map window
+        self.togglemapid = wx.NewId()
+        self.togglemap = wx.Choice(parent = self, id = self.togglemapid,
+                                   choices = [_('source'), _('target')],
+                                   style = wx.CB_READONLY)
+
+        self.InsertControl(10, self.togglemap)
+
+        self.SetToolShortHelp(self.togglemapid, '%s %s %s' % (_('Set map canvas for '),
+                                                              BaseIcons["zoomBack"].GetLabel(),
+                                                              _(' / Zoom to map')))
+
+        # realize the toolbar
+        self.Realize()
+        
+        self.action = { 'id' : self.gcpset }
+        self.defaultAction = { 'id' : self.gcpset,
+                               'bind' : self.parent.OnPointer }
+        
+        self.OnTool(None)
+        
+        self.EnableTool(self.zoomback, False)
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        icons = {
+            'gcpSet'    : MetaIcon(img = 'gcp-create',
+                                   label = _('Set GCP'),
+                                   desc = _('Define GCP (Ground Control Points)')),
+            'quit'      : BaseIcons['quit'].SetLabel(_('Quit georectification tool')),
+            'settings'  : BaseIcons['settings'].SetLabel( _('Georectifier settings')),
+            'help'      : BaseIcons['help'].SetLabel(_('Georectifier manual')),
+            }
+        
+        return self._getToolbarData((("displaymap", BaseIcons["display"],
+                                      self.parent.OnDraw),
+                                     ("rendermap", BaseIcons["render"],
+                                      self.parent.OnRender),
+                                     ("erase", BaseIcons["erase"],
+                                      self.parent.OnErase),
+                                     (None, ),
+                                     ("gcpset", icons["gcpSet"],
+                                      self.parent.OnPointer),
+                                     ("pan", BaseIcons["pan"],
+                                      self.parent.OnPan),
+                                     ("zoomin", BaseIcons["zoomIn"],
+                                      self.parent.OnZoomIn),
+                                     ("zoomout", BaseIcons["zoomOut"],
+                                      self.parent.OnZoomOut),
+                                     ("zoommenu", BaseIcons["zoomMenu"],
+                                      self.parent.OnZoomMenuGCP),
+                                     (None, ),
+                                     ("zoomback", BaseIcons["zoomBack"],
+                                      self.parent.OnZoomBack),
+                                     ("zoomtomap", BaseIcons["zoomExtent"],
+                                      self.parent.OnZoomToMap),
+                                     (None, ),
+                                     ('settings', icons["settings"],
+                                      self.parent.OnSettings),
+                                     ('help', icons["help"],
+                                      self.parent.OnHelp),
+                                     (None, ),
+                                     ('quit', icons["quit"],
+                                      self.parent.OnQuit))
+                                    )


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

Modified: grass/branches/releasebranch_6_4/gui/wxpython/gis_set.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gis_set.py	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gis_set.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -1,5 +1,5 @@
 """!
- at package gis_set.py
+ at package gis_set
 
 GRASS start-up screen.
 
@@ -7,9 +7,9 @@
 Location/mapset management (selection, creation, etc.).
 
 Classes:
- - GRASSStartup
- - GListBox
- - StartUp
+ - gis_set::GRASSStartup
+ - gis_set::GListBox
+ - gis_set::StartUp
 
 (C) 2006-2011 by the GRASS Development Team
 
@@ -22,7 +22,6 @@
 
 import os
 import sys
-import glob
 import shutil
 import copy
 import platform
@@ -32,17 +31,16 @@
 import gettext
 gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
 
-from gui_modules import globalvar
+if __name__ == "__main__":
+    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython'))
+from core import globalvar
 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
 
-from gui_modules import goutput
-from gui_modules.ghelp import HelpFrame
-from gui_modules.gcmd  import GMessage, GError
+from gui_core.ghelp import HelpFrame
+from core.gcmd      import GMessage, GError, DecodeString, RunCommand
+from core.utils     import GetListOfLocations, GetListOfMapsets
 
 sys.stderr = codecs.getwriter('utf8')(sys.stderr)
 
@@ -146,8 +144,7 @@
                                      style = wx.TE_PROCESS_ENTER)
 
         # Locations
-        self.lpanel = wx.Panel(parent = self.panel, id = wx.ID_ANY)
-        self.lblocations = GListBox(parent = self.lpanel,
+        self.lblocations = GListBox(parent = self.panel,
                                     id = wx.ID_ANY, size = (180, 200),
                                     choices = self.listOfLocations)
         
@@ -155,8 +152,7 @@
 
         # 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,
+        self.lbmapsets = GListBox(parent = self.panel,
                                   id = wx.ID_ANY, size = (180, 200),
                                   choices = self.listOfMapsets)
         
@@ -224,9 +220,8 @@
                                               force = True)
                 self.lblocations.EnsureVisible(self.listOfLocations.index(location))
             except ValueError:
-                print >> sys.stderr, _("ERROR: Location <%s> not found") % \
-                    (utils.UnicodeString(location))
-                
+                print >> sys.stderr, _("ERROR: Location <%s> not found") % location
+            
             # list of mapsets
             self.UpdateMapsets(os.path.join(self.gisdbase, location))
             mapset = self.GetRCValue("MAPSET")
@@ -237,89 +232,75 @@
                     self.lbmapsets.EnsureVisible(self.listOfMapsets.index(mapset))
                 except ValueError:
                     self.lbmapsets.Clear()
-                    print >> sys.stderr, _("ERROR: Mapset <%s> not found") % \
-                        (utils.UnicodeString(mapset))
-            
+                    print >> sys.stderr, _("ERROR: Mapset <%s> not found") % 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.BoxSizer(wx.HORIZONTAL)
         select_boxsizer = wx.StaticBoxSizer(self.select_box, wx.VERTICAL)
         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)
+        select_sizer.AddGrowableRow(1)
+        select_sizer.AddGrowableCol(0)
+        select_sizer.AddGrowableCol(1)
+        manage_sizer    = wx.StaticBoxSizer(self.manage_box, wx.VERTICAL)
         btns_sizer      = wx.BoxSizer(wx.HORIZONTAL)
-
+        
         # gis data directory
         dbase_sizer.Add(item = self.ldbase, proportion = 0,
                         flag = wx.ALIGN_CENTER_VERTICAL |
                         wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
                         border = 3)
-        dbase_sizer.Add(item = self.tgisdbase, proportion = 0,
-                        flag = wx.ALIGN_CENTER_VERTICAL |
-                        wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
+        dbase_sizer.Add(item = self.tgisdbase, proportion = 1,
+                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL,
                         border = 3)
         dbase_sizer.Add(item = self.bbrowse, proportion = 0,
-                        flag = wx.ALIGN_CENTER_VERTICAL |
-                        wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
+                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL,
                         border = 3)
-
+        
         # select sizer
         select_sizer.Add(item = self.llocation, proportion = 0,
-                         flag = label_style | wx.ALL,
+                         flag = wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
                          border = 3)
         select_sizer.Add(item = self.lmapset, proportion = 0,
-                         flag = label_style | wx.ALL,
+                         flag = wx.ALIGN_CENTER_HORIZONTAL | 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 |
-                         wx.ALIGN_CENTER_VERTICAL |
-                         wx.ALIGN_CENTER_HORIZONTAL)
-
-        select_boxsizer.Add(item = select_sizer, proportion = 0)
-
+        select_sizer.Add(item = self.lblocations, proportion = 1,
+                         flag = wx.EXPAND)
+        select_sizer.Add(item = self.lbmapsets, proportion = 1,
+                         flag = wx.EXPAND)
+        
+        select_boxsizer.Add(item = select_sizer, proportion = 1,
+                            flag = wx.EXPAND)
+        
         # define new location and mapset
         manage_sizer.Add(item = self.ldefine, proportion = 0,
-                         flag = label_style | wx.ALL,
+                         flag = wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
                          border = 3)
         manage_sizer.Add(item = self.bwizard, proportion = 0,
-                         flag = label_style | wx.BOTTOM,
+                         flag = wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM,
                          border = 5)
         manage_sizer.Add(item = self.lcreate, proportion = 0,
-                         flag = label_style | wx.ALL,
+                         flag = wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
                          border = 3)
         manage_sizer.Add(item = self.bmapset, proportion = 0,
-                         flag = label_style | wx.BOTTOM,
+                         flag = wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM,
                          border = 5)
         manage_sizer.Add(item = self.lmanageloc, proportion = 0,
-                         flag = label_style | wx.ALL,
+                         flag = wx.ALIGN_CENTER_HORIZONTAL | wx.ALL,
                          border = 3)
         manage_sizer.Add(item = self.manageloc, proportion = 0,
-                         flag = label_style | wx.BOTTOM,
+                         flag = wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM,
                          border = 5)
-
-        manage_boxsizer.Add(item = manage_sizer, proportion = 0)
-
+        
         # location sizer
-        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 = 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,
+        location_sizer.Add(item = select_boxsizer, proportion = 1,
+                           flag = wx.LEFT | wx.RIGHT | wx.EXPAND,
+                           border = 3) 
+        location_sizer.Add(item = manage_sizer, proportion = 0,
+                           flag = wx.RIGHT | wx.EXPAND,
                            border = 3)
-
+        
         # buttons
         btns_sizer.Add(item = self.bstart, proportion = 0,
                        flag = wx.ALIGN_CENTER_HORIZONTAL |
@@ -336,7 +317,7 @@
                        wx.ALIGN_CENTER_VERTICAL |
                        wx.ALL,
                        border = 5)
-
+        
         # main sizer
         sizer.Add(item = self.hbitmap,
                   proportion = 0,
@@ -356,24 +337,22 @@
                   wx.ALIGN_CENTER_HORIZONTAL)
         sizer.Add(item = dbase_sizer, proportion = 0,
                   flag = wx.ALIGN_CENTER_HORIZONTAL |
-                  wx.RIGHT | wx.LEFT,
-                  border = 1) # GISDBASE setting
+                  wx.RIGHT | wx.LEFT | wx.EXPAND,
+                  border = 20) # GISDBASE setting
         sizer.Add(item = location_sizer, proportion = 1,
-                  flag = wx.ALIGN_CENTER_VERTICAL |
-                  wx.ALIGN_CENTER_HORIZONTAL |
-                  wx.RIGHT | wx.LEFT,
+                  flag = wx.RIGHT | wx.LEFT | wx.EXPAND,
                   border = 1)
         sizer.Add(item = btns_sizer, proportion = 0,
                   flag = wx.ALIGN_CENTER_VERTICAL |
                   wx.ALIGN_CENTER_HORIZONTAL |
                   wx.RIGHT | wx.LEFT,
                   border = 1)
-
+        
         self.panel.SetAutoLayout(True)
         self.panel.SetSizer(sizer)
         sizer.Fit(self.panel)
         sizer.SetSizeHints(self)
-
+        
         self.Layout()
 
     def _readGisRC(self):
@@ -390,7 +369,7 @@
                 rc = open(gisrc, "r")
                 for line in rc.readlines():
                     key, val = line.split(":", 1)
-                    grassrc[key.strip()] = utils.DecodeString(val.strip())
+                    grassrc[key.strip()] = DecodeString(val.strip())
             finally:
                 rc.close()
         
@@ -406,9 +385,9 @@
         
     def OnWizard(self, event):
         """!Location wizard started"""
-        from gui_modules import location_wizard
-        gWizard = location_wizard.LocationWizard(parent = self,
-                                                 grassdatabase = self.tgisdbase.GetValue())
+        from location_wizard.wizard import LocationWizard
+        gWizard = LocationWizard(parent = self,
+                                 grassdatabase = self.tgisdbase.GetValue())
         if gWizard.location !=  None:
             self.OnSetDatabase(event)
             self.UpdateMapsets(os.path.join(self.gisdbase, gWizard.location))
@@ -433,8 +412,8 @@
     def RenameMapset(self):
         """!Rename selected mapset
         """
-        location = utils.UnicodeString(self.listOfLocations[self.lblocations.GetSelection()])
-        mapset   = utils.UnicodeString(self.listOfMapsets[self.lbmapsets.GetSelection()])
+        location = self.listOfLocations[self.lblocations.GetSelection()]
+        mapset   = self.listOfMapsets[self.lbmapsets.GetSelection()]
         if mapset ==  'PERMANENT':
             GMessage(parent = self,
                      message = _('Mapset <PERMANENT> is required for valid GRASS location.\n\n'
@@ -474,7 +453,7 @@
     def RenameLocation(self):
         """!Rename selected location
         """
-        location = utils.UnicodeString(self.listOfLocations[self.lblocations.GetSelection()])
+        location = self.listOfLocations[self.lblocations.GetSelection()]
 
         dlg = wx.TextEntryDialog(parent = self,
                                  message = _('Current name: %s\n\nEnter new name:') % location,
@@ -565,7 +544,7 @@
     def UpdateLocations(self, dbase):
         """!Update list of locations"""
         try:
-            self.listOfLocations = utils.GetListOfLocations(dbase)
+            self.listOfLocations = GetListOfLocations(dbase)
         except UnicodeEncodeError:
             wx.MessageBox(parent = self, caption = _("Error"),
                           message = _("Unable to set GRASS database. "
@@ -587,29 +566,29 @@
         self.FormerMapsetSelection = wx.NOT_FOUND # for non-selectable item
         
         self.listOfMapsetsSelectable = list()
-        self.listOfMapsets = utils.GetListOfMapsets(self.gisdbase, location)
+        self.listOfMapsets = GetListOfMapsets(self.gisdbase, location)
         
         self.lbmapsets.Clear()
         
         # disable mapset with denied permission
         locationName = os.path.basename(location)
         
-        ret = gcmd.RunCommand('g.mapset',
-                              read = True,
-                              flags = 'l',
-                              location = locationName,
-                              gisdbase = self.gisdbase)
+        ret = RunCommand('g.mapset',
+                         read = True,
+                         flags = 'l',
+                         location = locationName,
+                         gisdbase = self.gisdbase)
             
         if ret:
             for line in ret.splitlines():
                 self.listOfMapsetsSelectable += line.split(' ')
         else:
-            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")
+            RunCommand("g.gisenv",
+                       set = "GISDBASE=%s" % self.gisdbase)
+            RunCommand("g.gisenv",
+                       set = "LOCATION_NAME=%s" % locationName)
+            RunCommand("g.gisenv",
+                       set = "MAPSET=PERMANENT")
             # first run only
             self.listOfMapsetsSelectable = copy.copy(self.listOfMapsets)
         
@@ -711,6 +690,11 @@
 
         if dlg.ShowModal() ==  wx.ID_OK:
             mapset = dlg.GetValue()
+            if mapset in self.listOfMapsets:
+                GMessage(parent = self,
+                         message = _("Mapset <%s> already exists.") % mapset)
+                return
+            
             try:
                 os.mkdir(os.path.join(self.gisdbase, location, mapset))
                 # copy WIND file and its permissions from PERMANENT and set permissions to u+rw,go+r
@@ -775,12 +759,12 @@
             else:
                 return
         
-        gcmd.RunCommand("g.gisenv",
-                        set = "GISDBASE=%s" % dbase)
-        gcmd.RunCommand("g.gisenv",
-                        set = "LOCATION_NAME=%s" % location)
-        gcmd.RunCommand("g.gisenv",
-                        set = "MAPSET=%s" % mapset)
+        RunCommand("g.gisenv",
+                   set = "GISDBASE=%s" % dbase)
+        RunCommand("g.gisenv",
+                   set = "LOCATION_NAME=%s" % location)
+        RunCommand("g.gisenv",
+                   set = "MAPSET=%s" % mapset)
         
         self.Destroy()
         sys.exit(0)
@@ -881,15 +865,11 @@
         return 1
 
 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)
-
-        import gui_modules.gcmd as gcmd
-        import gui_modules.utils as utils
-
-        GRASSStartUp = StartUp(0)
-        GRASSStartUp.MainLoop()
+        sys.exit("Failed to start GUI, GRASS GIS is not running.")
+        
+    import gettext
+    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+    
+    GRASSStartUp = StartUp(0)
+    GRASSStartUp.MainLoop()

Added: grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/dialogs.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/dialogs.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/dialogs.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,967 @@
+"""!
+ at package gmodeler.dialogs
+
+ at brief wxGUI Graphical Modeler - dialogs
+
+Classes:
+ - dialogs::ModelDataDialog
+ - dialogs::ModelSearchDialog
+ - dialogs::ModelRelationDialog
+ - dialogs::ModelItemDialog
+ - dialogs::ModelLoopDialog
+ - dialogs::ModelConditionDialog
+ - dialogs::ModelListCtrl
+ - dialogs::ValiableListCtrl
+ - dialogs::ItemListCtrl
+ - dialogs::ItemCheckListCtrl
+
+(C) 2010-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+
+import wx
+import wx.lib.mixins.listctrl as listmix
+
+from core                 import globalvar
+from core                 import utils
+from gui_core.widgets     import GNotebook
+from core.gcmd            import GError, EncodeString
+from gui_core.dialogs     import ElementDialog, MapLayersDialog
+from gui_core.ghelp       import SearchModuleWindow
+from gui_core.prompt      import GPromptSTC
+from gui_core.forms       import CmdPanel
+from gui_core.gselect     import Select
+from gmodeler.model       import *
+
+from grass.script import task as gtask
+
+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
+        
+        label, etype = self._getLabel()
+        ElementDialog.__init__(self, parent, title, label = label, etype = etype)
+                
+        self.element = Select(parent = self.panel,
+                              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 _getLabel(self):
+        etype = False
+        prompt = self.shape.GetPrompt()
+        if prompt == 'raster':
+            label = _('Name of raster map:')
+        elif prompt == 'vector':
+            label = _('Name of vector map:')
+        else:
+            etype = True
+            label = _('Name of element:')
+
+        return label, etype
+    
+    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())
+        if self.etype:
+            elem = self.GetType()
+            if elem == 'rast':
+                self.shape.SetPrompt('raster')
+            elif elem == 'vect':
+                self.shape.SetPrompt('raster')
+        
+        self.parent.canvas.Refresh()
+        self.parent.SetStatusText('', 0)
+        self.shape.SetPropDialog(None)
+        
+        if self.IsModal():
+            event.Skip() 
+        else:
+            self.Destroy()
+    
+    def OnCancel(self, event):
+        """!Cancel pressed"""
+        self.shape.SetPropDialog(None)
+        if self.IsModal():
+            event.Skip()
+        else:
+            self.Destroy()
+
+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 = 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_KEY_UP, 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 = utils.split(str(line))
+        except UnicodeError:
+            cmd = utils.split(utils.EncodeString((line)))
+            
+        return cmd
+    
+    def OnOk(self, event):
+        """!Button 'OK' pressed"""
+        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:
+            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 in prompt changed"""
+        if self.cmd_prompt.AutoCompActive():
+            event.Skip()
+            return
+        
+        if isinstance(event, wx.KeyEvent):
+            entry = self.cmd_prompt.GetTextLeft()
+        elif isinstance(event, wx.stc.StyledTextEvent):
+            entry = event.GetText()
+        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)
+        self.btnOk.Enable(False)
+        self.cmd_prompt.SetFocus()
+
+class ModelRelationDialog(wx.Dialog):
+    """!Relation properties dialog"""
+    def __init__(self, parent, shape, id = wx.ID_ANY, title = _("Relation properties"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        self.parent = parent
+        self.shape = shape
+        
+        options = self._getOptions()
+        if not options:
+            self.valid = False
+            return
+        
+        self.valid = True
+        wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        self.fromBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                    label = " %s " % _("From"))
+        self.toBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                  label = " %s " % _("To"))
+        
+        self.option = wx.ComboBox(parent = self.panel, id = wx.ID_ANY,
+                                  style = wx.CB_READONLY,
+                                  choices = options)
+        self.option.Bind(wx.EVT_COMBOBOX, self.OnOption)
+        
+        self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
+        self.btnOk     = wx.Button(self.panel, wx.ID_OK)
+        self.btnOk.Enable(False)
+        
+        self._layout()
+
+    def _layout(self):
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+
+        fromSizer = wx.StaticBoxSizer(self.fromBox, wx.VERTICAL)
+        self._layoutShape(shape = self.shape.GetFrom(), sizer = fromSizer)
+        toSizer = wx.StaticBoxSizer(self.toBox, wx.VERTICAL)
+        self._layoutShape(shape = self.shape.GetTo(), sizer = toSizer)
+
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnOk)
+        btnSizer.Realize()
+        
+        mainSizer.Add(item = fromSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL, border = 5)
+        mainSizer.Add(item = toSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+        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()
+        self.SetSize(self.GetBestSize())
+        
+    def _layoutShape(self, shape, sizer):
+        if isinstance(shape, ModelData):
+            sizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                           label = _("Data: %s") % shape.GetLog()),
+                      proportion = 1, flag = wx.EXPAND | wx.ALL,
+                      border = 5)
+        elif isinstance(shape, ModelAction):
+            gridSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+            gridSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                               label = _("Command:")),
+                          pos = (0, 0))
+            gridSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                               label = shape.GetName()),
+                          pos = (0, 1))
+            gridSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                               label = _("Option:")),
+                          flag = wx.ALIGN_CENTER_VERTICAL,
+                          pos = (1, 0))
+            gridSizer.Add(item = self.option,
+                          pos = (1, 1))
+            sizer.Add(item = gridSizer,
+                      proportion = 1, flag = wx.EXPAND | wx.ALL,
+                      border = 5)
+            
+    def _getOptions(self):
+        """!Get relevant options"""
+        items = []
+        fromShape = self.shape.GetFrom()
+        if not isinstance(fromShape, ModelData):
+            GError(parent = self.parent,
+                   message = _("Relation doesn't start with data item.\n"
+                               "Unable to add relation."))
+            return items
+        
+        toShape = self.shape.GetTo()
+        if not isinstance(toShape, ModelAction):
+            GError(parent = self.parent,
+                   message = _("Relation doesn't point to GRASS command.\n"
+                               "Unable to add relation."))
+            return items
+        
+        prompt = fromShape.GetPrompt()
+        task = toShape.GetTask()
+        for p in task.get_options()['params']:
+            if p.get('prompt', '') == prompt and \
+                    'name' in p:
+                items.append(p['name'])
+        
+        if not items:
+            GError(parent = self.parent,
+                   message = _("No relevant option found.\n"
+                               "Unable to add relation."))
+        return items
+    
+    def GetOption(self):
+        """!Get selected option"""
+        return self.option.GetStringSelection()
+    
+    def IsValid(self):
+        """!Check if relation is valid"""
+        return self.valid
+    
+    def OnOption(self, event):
+        """!Set option"""
+        if event.GetString():
+            self.btnOk.Enable()
+        else:
+            self.btnOk.Enable(False)
+
+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 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.btnSeries = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                   label = _("Series"))
+        self.btnSeries.SetToolTipString(_("Define map series as condition for the loop"))
+        self.btnSeries.Bind(wx.EVT_BUTTON, self.OnSeries)
+        
+        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.HORIZONTAL)
+        condSizer.Add(item = self.condText, proportion = 1,
+                      flag = wx.ALL, border = 3)
+        condSizer.Add(item = self.btnSeries, proportion = 0,
+                      flag = wx.EXPAND)
+
+        listSizer = wx.StaticBoxSizer(self.listBox, wx.VERTICAL)
+        listSizer.Add(item = self.itemList, proportion = 1,
+                      flag = wx.EXPAND | wx.ALL, border = 3)
+        
+        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()
+
+    def OnSeries(self, event):
+        """!Define map series as condition"""
+        dialog = MapLayersDialog(parent = self, title = _("Define series of maps"), modeler = True)
+        if dialog.ShowModal() != wx.ID_OK:
+            dialog.Destroy()
+            return
+        
+        cond = dialog.GetDSeries()
+        if not cond:
+            cond = 'map in %s' % map(lambda x: str(x), dialog.GetMapLayers())
+        
+        self.condText.SetValue(cond)
+                               
+        dialog.Destroy()
+
+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 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 VariableListCtrl(ModelListCtrl):
+    def __init__(self, parent, columns, **kwargs):
+        """!List of model variables"""
+        ModelListCtrl.__init__(self, parent, columns, **kwargs)
+
+        self.SetColumnWidth(2, 200) # default value
+
+    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 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 isinstance(action, ModelData) or \
+                    action == self.shape:
+                continue
+            
+            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


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/frame.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/frame.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/frame.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1471 @@
+"""!
+ at package gmodeler.frame
+
+ at brief wxGUI Graphical Modeler for creating, editing, and managing models
+
+Classes:
+ - frame::ModelFrame
+ - frame::ModelCanvas
+ - frame::ModelEvtHandler
+ - frame::VariablePanel
+ - frame::ItemPanel
+
+(C) 2010-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+import time
+import stat
+import textwrap
+import tempfile
+import copy
+import re
+
+if __name__ == "__main__":
+    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'wxpython'))
+from core                 import globalvar
+import wx
+from wx.lib import ogl
+import wx.lib.flatnotebook    as FN
+
+from gui_core.widgets     import GNotebook
+from gui_core.goutput     import GMConsole
+from core.debug           import Debug
+from core.gcmd            import GMessage, GException, GWarning, GError, RunCommand
+from gui_core.dialogs     import GetImageHandlers
+from gui_core.preferences import PreferencesBaseDialog
+from core.settings        import UserSettings
+from core.menudata        import MenuData
+from gui_core.menu        import Menu
+from gmodeler.menudata    import ModelerData
+from gui_core.forms       import GUI
+from gmodeler.preferences import PreferencesDialog, PropertiesDialog
+from gmodeler.toolbars    import ModelerToolbar
+
+from gmodeler.model       import *
+from gmodeler.dialogs     import *
+
+from grass.script import core as grass
+
+class ModelFrame(wx.Frame):
+    def __init__(self, parent, id = wx.ID_ANY,
+                 title = _("GRASS GIS Graphical Modeler (experimental prototype)"), **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(parent = self, data = ModelerData())
+        
+        self.SetMenuBar(self.menubar)
+        
+        self.toolbar = ModelerToolbar(parent = self)
+        self.SetToolBar(self.toolbar)
+        
+        self.statusbar = self.CreateStatusBar(number = 1)
+        
+        self.notebook = GNotebook(parent = self,
+                                  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 = GMConsole(parent = self, notebook = self.notebook)
+        
+        self.notebook.AddPage(page = self.canvas, text=_('Model'), name = 'model')
+        self.notebook.AddPage(page = self.itemPanel, text=_('Items'), name = 'items')
+        self.notebook.AddPage(page = self.variablePanel, text=_('Variables'), name = 'variables')
+        self.notebook.AddPage(page = self.goutput, text=_('Command output'), name = 'output')
+        wx.CallAfter(self.notebook.SetSelectionByName, 'model')
+        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.SetSelectionByName('variables')
+        
+    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 OnCmdPrepare(self, event):
+        """!Prepare for running command"""
+        if not event.userData:
+            return
+        
+        event.onPrepare(item = event.userData['item'],
+                        params = event.userData['params'])
+        
+    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:
+            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 = _('No intermediate data 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()
+        self.itemPanel.Update()
+        self.variablePanel.Reset()
+        
+        # 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"""
+        self.model.Run(self.goutput, self.OnDone, parent = self)
+        
+    def OnDone(self, cmd, returncode):
+        """!Computation finished"""
+        self.SetStatusText('', 0)
+        # restore original files
+        if hasattr(self.model, "fileInput"):
+            for finput in self.model.fileInput:
+                data = self.model.fileInput[finput]
+                if not data:
+                    continue
+                
+                fd = open(finput, "w")
+                try:
+                    fd.write(data)
+                finally:
+                    fd.close()
+            del self.model.fileInput
+        
+    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
+        
+        
+        self.SetStatusText(_('Validating model...'), 0)
+        errList = self.model.Validate()
+        self.SetStatusText('', 0)
+        
+        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:
+                yminImg = ymin
+            if ymax > ymaxImg:
+                ymaxImg = ymax
+        size = wx.Size(int(xmaxImg - xminImg) + 50,
+                       int(ymaxImg - yminImg) + 50)
+        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 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()
+        
+        action = ModelAction(self.model, cmd = cmd, x = width/2, y = height/2,
+                             id = self.model.GetNextId())
+        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:
+            if action.IsValid():
+                self.GetOptData(dcmd = action.GetLog(string = False), layer = action,
+                                params = action.GetParams(), propwin = None)
+            else:
+                GUI(parent = self, show = True).ParseCommand(action.GetLog(string = False),
+                                                             completed = (self.GetOptData, action, action.GetParams()))
+        elif win and not win.IsShown():
+            win.Show()
+        
+        if win:
+            win.Raise()
+        
+    def OnAddData(self, event):
+        """!Add data item to model
+        """
+        # add action to canvas
+        width, height = self.canvas.GetSize()
+        data = ModelData(self, x = width/2, y = height/2)
+       
+        dlg = ModelDataDialog(parent = self, shape = data)
+        data.SetPropDialog(dlg)
+        dlg.CentreOnParent()
+        ret = dlg.ShowModal()
+        dlg.Destroy()
+        if ret != wx.ID_OK:
+            return
+        
+        data.Update()
+        self.canvas.diagram.AddShape(data)
+        data.Show(True)
+        
+        self.ModelChanged()
+        
+        self._addEvent(data)
+        self.model.AddItem(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')
+        year = grass.version()['date']
+        info.SetDescription(_('(C) 2010-%s by the GRASS Development Team\n\n') % year + 
+                            '\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
+            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(parent = self, fromShape = data,
+                                                toShape = layer, param = p.get('name', ''))
+                        else:
+                            rel = ModelRelation(parent = self, fromShape = layer,
+                                                toShape = data, param = 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(parent = self, fromShape = data,
+                                            toShape = layer, param = p.get('name', ''))
+                    else:
+                        rel = ModelRelation(parent = self, fromShape = layer,
+                                            toShape = data, param = p.get('name', ''))
+                    layer.AddRelation(rel)
+                    data.AddRelation(rel)
+                    self.AddLine(rel)
+                    data.Update()
+            
+            # valid / parameterized ?
+            layer.SetValid(params)
+            
+            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)
+        
+        # final updates
+        for action in self.model.GetItems(objType = ModelAction):
+            action.SetValid(action.GetParams())
+            action.Update()
+        
+        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 = self, fromShape = parent, toShape = 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(parent = self, fromShape = item, toShape = 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 = self, fromShape = parent,
+                                    toShape = 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)
+            shape.__del__()
+            for item in remList:
+                diagram.RemoveShape(item)
+                item.__del__()
+            
+            for item in upList:
+                item.Update()
+        
+        self.Refresh()
+
+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(parent = self.frame, fromShape = drel['from'],
+                                    toShape = drel['to'])
+                dlg = ModelRelationDialog(parent = self.frame,
+                                          shape = rel)
+                if dlg.IsValid():
+                    ret = dlg.ShowModal()
+                    if ret == wx.ID_OK:
+                        option = dlg.GetOption()
+                        rel.SetName(option)
+                        drel['from'].AddRelation(rel)
+                        drel['to'].AddRelation(rel)
+                        drel['from'].Update()
+                        params = { 'params' : [{ 'name' : option,
+                                                 'value' : drel['from'].GetValue()}] }
+                        drel['to'].MergeParams(params)
+                        self.frame.AddLine(rel)
+                
+                    dlg.Destroy()
+                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 = GUI(parent = self.frame, show = True).ParseCommand(shape.GetLog(string = False),
+                                                                        completed = (self.frame.GetOptData, shape, shape.GetParams()))
+        
+        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.SetStatusText(shape.GetLog(), 0)
+            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, "popupID"):
+            self.popupID = dict()
+            for key in ('remove', 'enable', 'addPoint',
+                        'delPoint', 'intermediate', 'props', 'id'):
+                self.popupID[key] = wx.NewId()
+        
+        # record coordinates
+        self.x = x
+        self.y = y
+        
+        shape = self.GetShape()
+        popupMenu = wx.Menu()
+        popupMenu.Append(self.popupID['remove'], text=_('Remove'))
+        self.frame.Bind(wx.EVT_MENU, self.OnRemove, id = self.popupID['remove'])
+        if isinstance(shape, ModelAction) or isinstance(shape, ModelLoop):
+            if shape.IsEnabled():
+                popupMenu.Append(self.popupID['enable'], text=_('Disable'))
+                self.frame.Bind(wx.EVT_MENU, self.OnDisable, id = self.popupID['enable'])
+            else:
+                popupMenu.Append(self.popupID['enable'], text=_('Enable'))
+                self.frame.Bind(wx.EVT_MENU, self.OnEnable, id = self.popupID['enable'])
+        
+        if isinstance(shape, ModelRelation):
+            popupMenu.AppendSeparator()
+            popupMenu.Append(self.popupID['addPoint'], text=_('Add control point'))
+            self.frame.Bind(wx.EVT_MENU, self.OnAddPoint, id = self.popupID['addPoint'])
+            popupMenu.Append(self.popupID['delPoint'], text=_('Remove control point'))
+            self.frame.Bind(wx.EVT_MENU, self.OnRemovePoint, id = self.popupID['delPoint'])
+            if len(shape.GetLineControlPoints()) == 2:
+                popupMenu.Enable(self.popupID['delPoint'], False)
+        
+        if isinstance(shape, ModelData) and '@' not in shape.GetValue():
+            popupMenu.AppendSeparator()
+            popupMenu.Append(self.popupID['intermediate'], text=_('Intermediate'),
+                             kind = wx.ITEM_CHECK)
+            if self.GetShape().IsIntermediate():
+                popupMenu.Check(self.popupID['intermediate'], True)
+            
+            self.frame.Bind(wx.EVT_MENU, self.OnIntermediate, id = self.popupID['intermediate'])
+            
+        if isinstance(shape, ModelData) or \
+                isinstance(shape, ModelAction) or \
+                isinstance(shape, ModelLoop):
+            popupMenu.AppendSeparator()
+            popupMenu.Append(self.popupID['props'], text=_('Properties'))
+            self.frame.Bind(wx.EVT_MENU, self.OnProperties, id = self.popupID['props'])
+        
+        self.frame.PopupMenu(popupMenu)
+        popupMenu.Destroy()
+
+    def OnDisable(self, event):
+        """!Disable action"""
+        self._onEnable(False)
+        
+    def OnEnable(self, event):
+        """!Disable action"""
+        self._onEnable(True)
+        
+    def _onEnable(self, enable):
+        shape = self.GetShape()
+        shape.Enable(enable)
+        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 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"),
+                                         _("mapset"),
+                                         _("file")])
+        self.type.SetSelection(2) # string
+        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(2) # string
+            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)
+        
+    def Reset(self):
+        """!Remove all variables"""
+        self.list.DeleteAllItems()
+        self.parent.GetModel().SetVariables([])
+        
+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)
+        
+def main():
+    import gettext
+    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+    
+    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/gmodeler/frame.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Added: grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/menudata.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/menudata.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/menudata.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,28 @@
+"""!
+ at package gmodeler.menudata
+
+ at brief wxGUI Graphical Modeler - menu data
+
+Classes:
+ - menudata::ModelerData
+
+(C) 2010-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+
+from core                 import globalvar
+from core.menudata        import MenuData
+
+class ModelerData(MenuData):
+    def __init__(self, filename = None):
+        if not filename:
+            gisbase = os.getenv('GISBASE')
+	    filename = os.path.join(globalvar.ETCWXDIR, 'xml', 'menudata_modeler.xml')
+        
+        MenuData.__init__(self, filename)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/model.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/model.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/model.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,2249 @@
+"""!
+ at package gmodeler.model
+
+ at brief wxGUI Graphical Modeler (base classes & read/write)
+
+Classes:
+ - model::Model
+ - model::ModelObject
+ - model::ModelAction
+ - model::ModelData
+ - model::ModelRelation
+ - model::ModelItem
+ - model::ModelLoop
+ - model::ModelCondition
+ - model::ProcessModelFile
+ - model::WriteModelFile
+ - model::WritePythonFile
+ - model::ModelParamDialog
+
+(C) 2010-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import getpass
+import copy
+import re
+import mimetypes
+import time
+try:
+    import xml.etree.ElementTree as etree
+except ImportError:
+    import elementtree.ElementTree as etree # Python <= 2.4
+
+import wx
+from wx.lib import ogl
+
+from core                import globalvar
+from core                import utils
+from core.gcmd           import GMessage, GException, GError, RunCommand, EncodeString, GWarning
+from core.settings       import UserSettings
+from gui_core.forms      import GUI, CmdPanel
+from gui_core.widgets    import GNotebook
+
+from grass.script import core as grass
+from grass.script import task as gtask
+
+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 GetNextId(self):
+        """!Get next id (data ignored)
+
+        @return next id to be used (default: 1)
+        """
+        if len(self.items) < 1:
+            return 1
+        
+        currId = self.items[-1].GetId()
+        if currId > 0:
+            return currId + 1
+        
+        return 1
+
+    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())
+        
+        elif isinstance(item, ModelLoop):
+            for rel in item.GetRelations():
+                relList.append(rel)
+            for action in self.GetItems():
+                action.UnSetBlock(item)
+        
+        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()
+        dataItems = self.GetItems(objType = ModelData)
+        
+        for action in self.GetItems(objType = ModelAction):
+            for rel in action.GetRelations():
+                dataItem = rel.GetData()
+                if dataItem not in result:
+                    result.append(dataItem)
+                if dataItem in dataItems:
+                    dataItems.remove(dataItem)
+        
+        # standalone data
+        if dataItems:
+            result += dataItems
+        
+        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)
+            
+            actionItem.SetValid(actionItem.GetTask().get_options())
+            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(parent = self, fromShape = dataItem,
+                                             toShape = actionItem, param = rel['name'])
+                else:
+                    relation = ModelRelation(parent = self, fromShape = actionItem,
+                                             toShape = dataItem, param = rel['name'])
+                relation.SetControlPoints(rel['points'])
+                actionItem.AddRelation(relation)
+                dataItem.AddRelation(relation)
+
+            if self.canvas:
+                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()
+
+        variables = self.GetVariables().keys()
+        pattern = re.compile(r'(.*)(%.+\s?)(.*)')
+        for action in self.GetItems(objType = ModelAction):
+            cmd = action.GetLog(string = False)
+            
+            task = GUI(show = None).ParseCommand(cmd = cmd)
+            errList += map(lambda x: cmd[0] + ': ' + x, task.get_cmd_error())
+            
+            # check also variables
+            for opt in cmd[1:]:
+                if '=' not in opt:
+                    continue
+                key, value = opt.split('=', 1)
+                sval = pattern.search(value)
+                if sval:
+                    var = sval.group(2).strip()[1:] # ignore '%'
+                    if var not in variables:
+                        report = True
+                        for item in filter(lambda x: isinstance(x, ModelLoop), action.GetBlock()):
+                            if var in item.GetText():
+                                report = False
+                                break
+                        if report:
+                            errList.append(cmd[0] + ": " + _("undefined variable '%s'") % var)
+            ### TODO: check variables in file only optionally
+            ### errList += self._substituteFile(action, checkOnly = True)
+        
+        return errList
+
+    def _substituteFile(self, item, params = None, checkOnly = False):
+        """!Subsitute variables in command file inputs
+
+        @param checkOnly tuble - True to check variable, don't touch files
+        
+        @return list of undefined variables
+        """
+        errList = list()
+        
+        self.fileInput = dict()
+        
+        # collect ascii inputs
+        for p in item.GetParams()['params']:
+            if p.get('element', '') == 'file' and \
+                    p.get('prompt', '') == 'input' and \
+                    p.get('age', '') == 'old_file':
+                filename = p.get('value', p.get('default', ''))
+                if filename and \
+                        mimetypes.guess_type(filename)[0] == 'text/plain':
+                    self.fileInput[filename] = None
+        
+        for finput in self.fileInput:
+            # read lines
+            fd = open(finput, "r")
+            try:
+                data = self.fileInput[finput] = fd.read()
+            finally:
+                fd.close()
+            
+            # substitute variables
+            write = False
+            variables = self.GetVariables()
+            for variable in variables:
+                pattern = re.compile('%' + variable)
+                value = ''
+                if params and 'variables' in params:
+                    for p in params['variables']['params']:
+                        if variable == p.get('name', ''):
+                            if p.get('type', 'string') == 'string':
+                                value = p.get('value', '')
+                            else:
+                                value = str(p.get('value', ''))
+                            break
+                
+                if not value:
+                    value = variables[variable].get('value', '')
+                
+                data = pattern.sub(value, data)
+                if not checkOnly:
+                    write = True
+            
+            pattern = re.compile(r'(.*)(%.+\s?)(.*)')
+            sval = pattern.search(data)
+            if sval:
+                var = sval.group(2).strip()[1:] # ignore '%'
+                cmd = item.GetLog(string = False)[0]
+                errList.append(cmd + ": " + _("undefined variable '%s'") % var)
+            
+            if not checkOnly:
+                if write:
+                    fd = open(finput, "w")
+                    try:
+                        fd.write(data)
+                    finally:
+                        fd.close()
+                else:
+                    self.fileInput[finput] = None
+        
+        return errList
+    
+    def OnPrepare(self, item, params):
+        self._substituteFile(item, params, checkOnly = False)
+
+    def RunAction(self, item, params, log, onDone, onPrepare = None, statusbar = None):
+        """!Run given action
+
+        @param item action item
+        @param params parameters dict
+        @param log logging window
+        @param onDone on-done method
+        @param onPrepare on-prepare method
+        @param statusbar wx.StatusBar instance or None
+        """
+        name = item.GetName()
+        if name in params:
+            paramsOrig = item.GetParams(dcopy = True)
+            item.MergeParams(params[name])
+        
+        if statusbar:
+            statusbar.SetStatusText(_('Running model...'), 0)
+            
+        data = { 'item' : item,
+                 'params' : copy.deepcopy(params) }
+        log.RunCmd(command = item.GetLog(string = False, substitute = params),
+                   onDone = onDone, onPrepare = self.OnPrepare, userData = data)
+        
+        if name in params:
+            item.SetParams(paramsOrig)
+
+    def Run(self, log, onDone, parent = None):
+        """!Run model
+
+        @param log logging window (see goutput.GMConsole)
+        @param onDone on-done method
+        @param parent window for messages or None
+        """
+        if self.GetNumItems() < 1:
+            GMessage(parent = parent,
+                     message = _('Model is empty. Nothing to run.'))
+            return
+        
+        statusbar = None
+        if isinstance(parent, wx.Frame):
+            statusbar = parent.GetStatusBar()
+        
+        # validation
+        if statusbar:
+            statusbar.SetStatusText(_('Validating model...'), 0)
+        errList = self.Validate()
+        if statusbar:
+            statusbar.SetStatusText('', 0)
+        if errList:
+            dlg = wx.MessageDialog(parent = parent,
+                                   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()
+            dlg.Destroy()
+            if ret != wx.ID_YES:
+                return
+        
+        # parametrization
+        params = self.Parameterize()
+        delInterData = False
+        if params:
+            dlg = ModelParamDialog(parent = parent,
+                                   params = params)
+            dlg.CenterOnParent()
+            
+            ret = dlg.ShowModal()
+            if ret != wx.ID_OK:
+                dlg.Destroy()
+                return
+            
+            err = dlg.GetErrors()
+            delInterData = dlg.DeleteIntermediateData()
+            dlg.Destroy()
+            if err:
+                GError(parent = parent, message = unicode('\n'.join(err)))
+                return
+            
+            err = list()
+            for key, item in params.iteritems():
+                for p in item['params']:
+                    if p.get('value', '') == '':
+                        err.append((key, p.get('name', ''), p.get('description', '')))
+            if err:
+                GError(parent = parent,
+                       message = _("Variables below not defined:") + \
+                           "\n\n" + unicode('\n'.join(map(lambda x: "%s: %s (%s)" % (x[0], x[1], x[2]), err))))
+                return
+        
+        log.cmdThread.SetId(-1)
+        for item in self.GetItems():
+            if not item.IsEnabled():
+                continue
+            if isinstance(item, ModelAction):
+                if item.GetBlockId():
+                    continue
+                self.RunAction(item, params, log, onDone)
+            elif isinstance(item, ModelLoop):
+                cond = item.GetText()
+                # substitute variables in condition
+                variables = self.GetVariables()
+                for variable in variables:
+                    pattern = re.compile('%' + variable)
+                    if pattern.search(cond):
+                        value = ''
+                        if params and 'variables' in params:
+                            for p in params['variables']['params']:
+                                if variable == p.get('name', ''):
+                                    value = p.get('value', '')
+                                    break
+                        
+                        if not value:
+                            value = variables[variable].get('value', '')
+                        
+                        if not value:
+                            continue
+                        
+                        vtype = variables[variable].get('type', 'string')
+                        if vtype == 'string':
+                            value = '"' + value + '"'
+                        cond = pattern.sub(value, cond)
+                
+                # split condition
+                condVar, condText = map(lambda x: x.strip(), re.split('\s*in\s*', cond))
+                pattern = re.compile('%' + condVar)
+                ### for vars()[condVar] in eval(condText): ?
+                if condText[0] == '`' and condText[-1] == '`':
+                    # run command
+                    cmd, dcmd = utils.CmdToTuple(condText[1:-1].split(' '))
+                    ret = RunCommand(cmd,
+                                     read = True,
+                                     **dcmd)
+                    if ret:
+                        vlist = ret.splitlines()
+                else:
+                    vlist = eval(condText)
+                
+                if 'variables' not in params:
+                    params['variables'] = { 'params' : [] }
+                varDict = { 'name' : condVar, 'value' : '' }
+                params['variables']['params'].append(varDict)
+                                
+                for var in vlist:
+                    for action in item.GetItems():
+                        if not isinstance(action, ModelAction) or \
+                                not action.IsEnabled():
+                            continue
+                        
+                        varDict['value'] = var
+                                                
+                        self.RunAction(item = action, params = params,
+                                       log = log, onDone = onDone)
+                params['variables']['params'].remove(varDict)
+        
+        if delInterData:
+            self.DeleteIntermediateData(log)
+        
+        # discard values
+        if params:
+            for item in params.itervalues():
+                for p in item['params']:
+                    p['value'] = ''
+        
+    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', 'mapset', 'file'):
+                    gisprompt = True
+                    prompt = gtype
+                    if gtype == 'raster':
+                        element = 'cell'
+                    else:
+                        element = gtype
+                    ptype = 'string'
+                else:
+                    gisprompt = False
+                    prompt = None
+                    element = None
+                    ptype = gtype
+                params.append({ 'gisprompt' : gisprompt,
+                                'multiple'  : False,
+                                'description' : values.get('description', ''),
+                                'guidependency' : '',
+                                'default' : '',
+                                'age' : None,
+                                'required' : True,
+                                '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 name not in result:
+                        result[name] = { 'flags' : list(),
+                                         'params': list(),
+                                         'idx'   : idx }
+                    result[name]['flags'].append(f)
+            for p in params['params']:
+                if p.get('parameterized', False):
+                    if name not in result:
+                        result[name] = { 'flags' : list(),
+                                         'params': list(),
+                                         'idx'   : idx }
+                    result[name]['params'].append(p)
+            if name in result:
+                idx += 1
+        
+        self.variablesParams = result # record parameters
+        
+        return result
+
+class ModelObject(object):
+    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 and cmd[0] in ('r.mapcalc', 'v.type'):
+            cmd[0] += '_wrapper'
+        
+        if cmd:
+            self.task = GUI(show = None).ParseCommand(cmd = cmd)
+        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)
+        
+        if self.task:
+            self.SetValid(self.task.get_options())
+        
+    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='disabled',
+                                     subkey='color')
+        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.get_cmd(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, substitute = None):
+        """!Get logging info
+
+        @param string True to get cmd as a string otherwise a list
+        @param substitute dictionary of parameter to substitute or None
+        """
+        cmd = self.task.get_cmd(ignoreErrors = True, ignoreRequired = True,
+                                ignoreDefault = False)
+        
+        # substitute variables
+        if substitute:
+            variables = []
+            if 'variables' in substitute:
+                for p in substitute['variables']['params']:
+                    variables.append(p.get('name', ''))
+            else:
+                variables = self.parent.GetVariables()
+            for variable in variables:
+                pattern= re.compile('%' + variable)
+                value = ''
+                if substitute and 'variables' in substitute:
+                    for p in substitute['variables']['params']:
+                        if variable == p.get('name', ''):
+                            if p.get('type', 'string') == 'string':
+                                value = p.get('value', '')
+                            else:
+                                value = str(p.get('value', ''))
+                            break
+                    
+                if not value:
+                    value = variables[variable].get('value', '')
+                
+                if not value:
+                    continue
+                
+                for idx in range(len(cmd)):
+                    if pattern.search(cmd[idx]):
+                        cmd[idx] = pattern.sub(value, cmd[idx])
+                        break
+                    idx += 1
+        
+        if string:
+            if cmd is None:
+                return ''
+            else:
+                return ' '.join(cmd)
+        
+        return cmd
+    
+    def GetName(self):
+        """!Get name"""
+        cmd = self.task.get_cmd(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 'flags' in params:
+            for f in params['flags']:
+                self.task.set_flag(f['name'],
+                                   f.get('value', False))
+        if 'params' in params:
+            for p in params['params']:
+                self.task.set_param(p['name'],
+                                    p.get('value', ''))
+        
+    def SetValid(self, options):
+        """!Set validity for action
+        
+        @param options dictionary with flags and params (gtask)
+        """
+        self.isValid = True
+        self.isParameterized = False
+        
+        for f in options['flags']:
+            if f.get('parameterized', False):
+                self.IsParameterized = True
+                break
+        
+        for p in options['params']:
+            if self.isValid and p.get('required', False) and \
+                    p.get('value', '') == '' and \
+                    p.get('default', '') == '':
+                self.isValid = False
+            if not self.isParameterized and p.get('parameterized', False):
+                self.isParameterized = True
+        
+        if self.parent.GetCanvas():
+            self._setBrush()
+            self._setPen()
+        
+    def IsValid(self):
+        """!Check validity (all required parameters set)"""
+        return self.isValid
+    
+    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.Recentre(self, dc) # re-center text
+        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 self.value + ' (' + self.prompt + ')'
+
+    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 SetPrompt(self, prompt):
+        """!Set prompt
+        
+        @param prompt
+        """
+        self.prompt = prompt
+        
+    def GetValue(self):
+        """!Get value"""
+        return self.value
+
+    def SetValue(self, value):
+        """!Set value
+
+        @param 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 = GUI(show = None).ParseCommand(cmd = action.GetLog(string = False))
+                task.set_param(rel.GetName(), self.value)
+                action.SetParams(params = task.get_options())
+        
+    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 ModelRelation(ogl.LineShape):
+    """!Data - action relation"""
+    def __init__(self, parent, fromShape, toShape, param = ''):
+        self.fromShape = fromShape
+        self.toShape   = toShape
+        self.param     = param
+        self.parent    = parent
+        
+        self._points    = None
+        
+        if self.parent.GetCanvas():        
+            ogl.LineShape.__init__(self)
+    
+    def __del__(self):
+        if self in self.fromShape.rels:
+            self.fromShape.rels.remove(self)
+        if self in self.toShape.rels:
+            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)
+    
+    def SetName(self, param):
+        self.param = param
+
+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 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) + ')')
+        
+        self._setBrush()
+        
+    def _setBrush(self):
+        """!Set brush"""
+        if not self.isEnabled:
+            color = UserSettings.Get(group='modeler', key='disabled',
+                                     subkey='color')
+        else:
+            color = UserSettings.Get(group='modeler', key='loop',
+                                     subkey=('color', 'valid'))
+        
+        wxColor = wx.Color(color[0], color[1], color[2])
+        self.SetBrush(wx.Brush(wxColor))
+
+    def Enable(self, enabled = True):
+        """!Enable/disable action"""
+        for item in self.items:
+            if not isinstance(item, ModelAction):
+                continue
+            item.Enable(enabled)
+        
+        ModelObject.Enable(self, enabled)
+        
+    def Update(self):
+        self._setBrush()
+        
+    def GetName(self):
+        """!Get name"""
+        return _("loop")
+    
+    def SetItems(self, items):
+        """!Set items (id)"""
+        self.items = items
+
+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 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 = GUI(show = None, checkError = True).ParseCommand(cmd = cmd)
+        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,
+                                                                 utils.EncodeString(self.properties['description'])))
+        if self.properties['author']:
+            self.fd.write('%s<author>%s</author>\n' % (' ' * self.indent,
+                                                       utils.EncodeString(self.properties['author'])))
+        
+        if 'overwrite' in self.properties 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 'value' in values:
+                self.fd.write('%s<value>%s</value>\n' % \
+                                  (' ' * self.indent, values['value']))
+            if 'description' in values:
+                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', '') and not p.get('parameterized', False):
+                        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 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, variables = []):
+        """!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, variables = variables)
+        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):
+                condVar, condText = map(lambda x: x.strip(), re.split('\s*in\s*', cond))
+                cond = "%sfor %s in " % (' ' * self.indent, condVar)
+                if condText[0] == '`' and condText[-1] == '`':
+                    task = GUI(show = None).ParseCommand(cmd = utils.split(condText[1:-1]))
+                    cond += "grass.read_command("
+                    cond += self._getPythonActionCmd(task, len(cond), variables = [condVar]) + ".splitlines()"
+                else:
+                    cond += condText
+                self.fd.write('%s:\n' % cond)
+                self.indent += 4
+                for action in item.GetItems():
+                    self._writePythonItem(action, ignoreBlock = False, variables = [condVar])
+                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, variables = []):
+        """!Write model action to Python file"""
+        task = GUI(show = None).ParseCommand(cmd = item.GetLog(string = False))
+        strcmd = "%sgrass.run_command(" % (' ' * self.indent)
+        self.fd.write(strcmd + self._getPythonActionCmd(task, len(strcmd), variables) + '\n')
+        
+    def _getPythonActionCmd(self, task, cmdIndent, variables = []):
+        opts = task.get_options()
+        
+        ret = ''
+        flags = ''
+        params = list()
+        
+        for f in opts['flags']:
+            if f.get('value', False):
+                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 value[0] == '%':
+                    params.append("%s = %s" % (name, value[1:]))
+                elif ptype == 'string':
+                    params.append('%s = "%s"' % (name, value))
+                else:
+                    params.append("%s = %s" % (name, value))
+        
+        ret += '"%s"' % task.get_name()
+        if flags:
+            ret += ",\n%sflags = '%s'" % (' ' * cmdIndent, flags)
+        if len(params) > 0:
+            ret += ",\n"
+            for opt in params[:-1]:
+                ret += "%s%s,\n" % (' ' * cmdIndent, opt)
+            ret += "%s%s)" % (' ' * cmdIndent, params[-1])
+        else:
+            ret += ")"
+        
+        return ret
+
+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)
+        
+        self.notebook = GNotebook(parent = self, 
+                                  style = globalvar.FNPageDStyle)
+        
+        panel = self._createPages()
+        wx.CallAfter(self.notebook.SetSelection, 0)
+
+        # intermediate data?
+        self.interData = wx.CheckBox(parent = self, label = _("Delete intermediate data when finish"))
+        self.interData.SetValue(True)
+        rast, vect, rast3d, msg = self.parent.GetModel().GetIntermediateData()
+        if not rast and not vect and not rast3d:
+            self.interData.Hide()
+        
+        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)
+        if self.interData.IsShown():
+            mainSizer.Add(item = self.interData, proportion = 0,
+                          flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+            mainSizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY,
+                                               style = wx.LI_HORIZONTAL),
+                          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)
+        
+        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)
+            if name == 'variables':
+                name = _('Variables')
+            self.notebook.AddPage(page = panel, text = name)
+        
+        return panel
+    
+    def _createPage(self, name, params):
+        """!Define notebook page"""
+        if name in globalvar.grassCmd:
+            task = gtask.grassTask(name)
+        else:
+            task = gtask.grassTask()
+        task.flags  = params['flags']
+        task.params = params['params']
+        
+        panel = 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.get_cmd_error()
+        
+        return errList
+
+    def DeleteIntermediateData(self):
+        """!Check if to detele intermediate data"""
+        if self.interData.IsShown() and self.interData.IsChecked():
+            return True
+        
+        return False


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/preferences.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/preferences.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/preferences.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,528 @@
+"""!
+ at package gmodeler.preferences
+
+ at brief wxGUI Graphical Modeler - preferences
+
+Classes:
+ - preferences::PreferencesDialog
+ - preferences::PropertiesDialog
+
+(C) 2010-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import wx
+import wx.lib.colourselect    as csel
+
+from core                 import globalvar
+from gui_core.preferences import PreferencesBaseDialog
+from core.settings        import UserSettings
+
+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._createGeneralPage(self.notebook)
+        self._createActionPage(self.notebook)
+        self._createDataPage(self.notebook)
+        self._createLoopPage(self.notebook)
+        
+        self.SetMinSize(self.GetBestSize())
+        self.SetSize(self.size)
+
+    def _createGeneralPage(self, notebook):
+        """!Create notebook page for action settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("General"))
+        
+        # colors
+        border = wx.BoxSizer(wx.VERTICAL)
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Item properties"))
+        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 = _("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='disabled', subkey='color'),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        rColor.SetName('GetColour')
+        self.winId['modeler:disabled:color'] = 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)
+
+        panel.SetSizer(border)
+        
+        return panel
+
+    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 " % _("Color"))
+        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))
+        
+        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 _createLoopPage(self, notebook):
+        """!Create notebook page for loop settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("Loop"))
+        
+        # colors
+        border = wx.BoxSizer(wx.VERTICAL)
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Color"))
+        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='loop', subkey=('color', 'valid')),
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        vColor.SetName('GetColour')
+        self.winId['modeler:loop:color:valid'] = 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='loop', subkey=('size', 'width')))
+        width.SetName('GetValue')
+        self.winId['modeler:loop: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='loop', subkey=('size', 'height')))
+        height.SetName('GetValue')
+        self.winId['modeler:loop: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 'overwrite' in prop:
+            self.overwrite.SetValue(prop['overwrite'])


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/toolbars.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/toolbars.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gmodeler/toolbars.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,109 @@
+"""!
+ at package gmodeler.toolbars
+
+ at brief wxGUI Graphical Modeler toolbars classes
+
+Classes:
+ - toolbars::ModelerToolbar
+
+(C) 2010-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+
+import wx
+
+from core              import globalvar
+from gui_core.toolbars import BaseToolbar, BaseIcons
+
+from icons.icon        import MetaIcon
+
+class ModelerToolbar(BaseToolbar):
+    """!Graphical modeler toolbaro (see gmodeler.py)
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        icons = {
+            'new'        : MetaIcon(img = 'create',
+                                    label = _('Create new model (Ctrl+N)')),
+            'open'       : MetaIcon(img = 'open',
+                                    label = _('Load model from file (Ctrl+O)')),
+            'save'       : MetaIcon(img = 'save',
+                                    label = _('Save current model to file (Ctrl+S)')),
+            'toImage'    : MetaIcon(img = 'image-export',
+                                    label = _('Export model to image')),
+            'toPython'   : MetaIcon(img = 'python-export',
+                                    label = _('Export model to Python script')),
+            'actionAdd'  : MetaIcon(img = 'module-add',
+                                    label = _('Add command (GRASS module) to model')),
+            'dataAdd'    : MetaIcon(img = 'data-add',
+                                    label = _('Add data to model')),
+            'relation'   : MetaIcon(img = 'relation-create',
+                                    label = _('Manually define relation between data and commands')),
+            'loop'       : MetaIcon(img = 'loop-add',
+                                    label = _('Add loop/series')),
+            'run'        : MetaIcon(img = 'execute',
+                                    label = _('Run model')),
+            'validate'   : MetaIcon(img = 'check',
+                                    label = _('Validate model')),
+            'settings'   : BaseIcons['settings'].SetLabel(_('Modeler settings')),
+            'properties' : MetaIcon(img = 'options',
+                                    label = _('Show model properties')),
+            'variables'  : MetaIcon(img = 'modeler-variables',
+                                    label = _('Manage model variables')),
+            'redraw'     : MetaIcon(img = 'redraw',
+                                    label = _('Redraw model canvas')),
+            'quit'       : BaseIcons['quit'].SetLabel(_('Quit Graphical Modeler')),
+            }
+        
+        return self._getToolbarData((('new', icons['new'],
+                                      self.parent.OnModelNew),
+                                     ('open', icons['open'],
+                                      self.parent.OnModelOpen),
+                                     ('save', icons['save'],
+                                      self.parent.OnModelSave),
+                                     ('image', icons['toImage'],
+                                      self.parent.OnExportImage),
+                                     ('python', icons['toPython'],
+                                      self.parent.OnExportPython),
+                                     (None, ),
+                                     ('action', icons['actionAdd'],
+                                      self.parent.OnAddAction),
+                                     ('data', icons['dataAdd'],
+                                      self.parent.OnAddData),
+                                     ('relation', icons['relation'],
+                                      self.parent.OnDefineRelation),
+                                     ('loop', icons['loop'],
+                                      self.parent.OnDefineLoop),
+                                     (None, ),
+                                     ('redraw', icons['redraw'],
+                                      self.parent.OnCanvasRefresh),
+                                     ('validate', icons['validate'],
+                                      self.parent.OnValidateModel),
+                                     ('run', icons['run'],
+                                      self.parent.OnRunModel),
+                                     (None, ),
+                                     ("variables", icons['variables'],
+                                      self.parent.OnVariables),
+                                     ("settings", icons['settings'],
+                                      self.parent.OnPreferences),
+                                     ("help", BaseIcons['help'],
+                                      self.parent.OnHelp),
+                                     (None, ),
+                                     ('quit', icons['quit'],
+                                      self.parent.OnCloseWindow))
+                                    )


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_core/dialogs.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_core/dialogs.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_core/dialogs.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,2346 @@
+"""!
+ at package gui_core.dialogs
+
+ at brief Various dialogs used in wxGUI.
+
+List of classes:
+ - dialogs::ElementDialog
+ - dialogs::LocationDialog
+ - dialogs::MapsetDialog
+ - dialogs::NewVectorDialog
+ - dialogs::SavedRegion
+ - dialogs::DecorationDialog
+ - dialogs::TextLayerDialog 
+ - dialogs::GroupDialog
+ - dialogs::MapLayersDialog
+ - dialogs::ImportDialog
+ - dialogs::GdalImportDialog
+ - dialogs::DxfImportDialog
+ - dialogs::LayersList (used by MultiImport) 
+ - dialogs::SetOpacityDialog
+ - dialogs::ImageSizeDialog
+
+(C) 2008-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+ at author Anna Kratochvilova <kratochanna gmail.com> (GroupDialog)
+"""
+
+import os
+import sys
+import re
+from bisect import bisect
+
+import wx
+import wx.lib.filebrowsebutton as filebrowse
+import wx.lib.mixins.listctrl as listmix
+
+from grass.script import core as grass
+from grass.script import task as gtask
+
+from core             import globalvar
+from core.gcmd        import GError, RunCommand, GMessage
+from gui_core.gselect import ElementSelect, LocationSelect, MapsetSelect, Select, GdalSelect
+from gui_core.forms   import GUI
+from gui_core.widgets import SingleSymbolPanel, EVT_SYMBOL_SELECTION_CHANGED
+from core.utils       import GetListOfMapsets, GetLayerNameFromCmd, GetValidLayerName
+from core.settings    import UserSettings
+from core.debug       import Debug
+
+class ElementDialog(wx.Dialog):
+    def __init__(self, parent, title, label, id = wx.ID_ANY,
+                 etype = False, style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+                 **kwargs):
+        """!General dialog to choose given element (location, mapset, vector map, etc.)
+        
+        @param parent window
+        @param title window title
+        @param label element label
+        @param etype show also ElementSelect
+        """
+        wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
+        
+        self.etype = etype
+        self.label = label
+        
+        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)
+        
+        if self.etype:
+            self.typeSelect = ElementSelect(parent = self.panel,
+                                            size = globalvar.DIALOG_GSELECT_SIZE)
+            self.typeSelect.Bind(wx.EVT_CHOICE, self.OnType)
+        
+        self.element = None # must be defined 
+        
+        self.__layout()
+        
+    def PostInit(self):
+        self.element.SetFocus()
+        self.element.Bind(wx.EVT_TEXT, self.OnElement)
+        
+    def OnType(self, event):
+        """!Select element type"""
+        if not self.etype:
+            return
+        evalue = self.typeSelect.GetValue(event.GetString())
+        self.element.SetType(evalue)
+        
+    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)
+        
+        if self.etype:
+            self.dataSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                                    label = _("Type of element:")),
+                               proportion = 0, flag = wx.ALL, border = 1)
+            self.dataSizer.Add(item = self.typeSelect,
+                               proportion = 0, flag = wx.ALL, border = 1)
+        
+        self.dataSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                                label = 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()
+    
+    def GetType(self):
+        """!Get element type"""
+        return self.element.tcp.GetType()
+        
+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:"))
+
+        self.element = LocationSelect(parent = self.panel, id = wx.ID_ANY,
+                                      size = globalvar.DIALOG_GSELECT_SIZE)
+
+        self.element1 = MapsetSelect(parent = self.panel, id = wx.ID_ANY,
+                                     size = globalvar.DIALOG_GSELECT_SIZE,
+                                     setItems = False, skipCurrent = True)
+        
+        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.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.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()
+        
+        if location:
+            dbase = grass.gisenv()['GISDBASE']
+            self.element1.UpdateItems(dbase = dbase, location = location)
+            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 = MapsetSelect(parent = self.panel, id = wx.ID_ANY, skipCurrent = True,
+                                    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):
+    def __init__(self, parent, id = wx.ID_ANY, title = _('Create new vector map'),
+                 disableAdd = False, disableTable = False,
+                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, *kwargs):
+        """!Dialog for creating new vector map
+
+        @param parent parent window
+        @param id window id
+        @param title window title
+        @param disableAdd disable 'add layer' checkbox
+        @param disableTable disable 'create table' checkbox
+        @param style window style
+        @param kwargs other argumentes for ElementDialog
+        
+        @return dialog instance       
+        """
+        ElementDialog.__init__(self, parent, title, label = _("Name for new vector map:"))
+        
+        self.element = 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.keycol = wx.TextCtrl(parent = self.panel, id =  wx.ID_ANY,
+                                  size = globalvar.DIALOG_SPIN_SIZE)
+        self.keycol.SetValue(UserSettings.Get(group = 'atm', key = 'keycolumn', subkey = 'value'))
+        if disableTable:
+            self.keycol.Enable(False)
+        
+        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.table.Bind(wx.EVT_CHECKBOX, self.OnTable)
+        
+        self.PostInit()
+        
+        self._layout()
+        self.SetMinSize(self.GetSize())
+        
+    def OnMapName(self, event):
+        """!Name for vector map layer given"""
+        self.OnElement(event)
+        
+    def OnTable(self, event):
+        self.keycol.Enable(event.IsChecked())
+        
+    def _layout(self):
+        """!Do layout"""
+        self.dataSizer.Add(self.element, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL, border = 1)
+        
+        self.dataSizer.Add(self.table, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL, border = 1)
+
+        keySizer = wx.BoxSizer(wx.HORIZONTAL)
+        keySizer.Add(item = wx.StaticText(parent = self.panel, label = _("Key column:")),
+                     proportion = 0,
+                     flag = wx.ALIGN_CENTER_VERTICAL)
+        keySizer.AddSpacer(10)
+        keySizer.Add(item = self.keycol, proportion = 0,
+                     flag = wx.ALIGN_RIGHT)
+        self.dataSizer.Add(item = keySizer, proportion = 1,
+                           flag = wx.EXPAND | wx.ALL, border = 1)
+
+        self.dataSizer.AddSpacer(5)
+        
+        self.dataSizer.Add(item = self.addbox, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL, border = 1)
+        
+        self.panel.SetSizer(self.sizer)
+        self.sizer.Fit(self)
+
+    def GetName(self, full = False):
+        """!Get name of vector map to be created
+
+        @param full True to get fully qualified name
+        """
+        name = self.GetElement()
+        if full:
+            if '@' in name:
+                return name
+            else:
+                return name + '@' + grass.gisenv()['MAPSET']
+        
+        return name.split('@', 1)[0]
+
+    def GetKey(self):
+        """!Get key column name"""
+        return self.keycol.GetValue()
+    
+    def IsChecked(self, key):
+        """!Get dialog properties
+
+        @param key window key ('add', 'table')
+
+        @return True/False
+        @return None on error
+        """
+        if key == 'add':
+            return self.addbox.IsChecked()
+        elif key == 'table':
+            return self.table.IsChecked()
+        
+        return None
+    
+def CreateNewVector(parent, cmd, title = _('Create new vector map'),
+                    exceptMap = None, log = None, disableAdd = False, disableTable = False):
+    """!Create new vector map layer
+    
+    @param cmd (prog, **kwargs)
+    @param title window title
+    @param exceptMap list of maps to be excepted
+    @param log
+    @param disableAdd disable 'add layer' checkbox
+    @param disableTable disable 'create table' checkbox
+
+    @return dialog instance
+    @return None on error
+    """
+    dlg = NewVectorDialog(parent, title = title,
+                          disableAdd = disableAdd, disableTable = disableTable)
+    
+    if dlg.ShowModal() != wx.ID_OK:
+        dlg.Destroy()
+        return None
+
+    outmap = dlg.GetName()
+    key    = dlg.GetKey()
+    if outmap == exceptMap:
+        GError(parent = parent,
+               message = _("Unable to create vector map <%s>.") % outmap)
+        dlg.Destroy()
+        return None
+    if dlg.table.IsEnabled() and not key:
+        GError(parent = parent,
+               message = _("Invalid or empty key column.\n"
+                           "Unable to create vector map <%s>.") % outmap)
+        dlg.Destroy()
+        return
+        
+    if outmap == '': # should not happen
+        dlg.Destroy()
+        return None
+    
+    # update cmd -> output name defined
+    cmd[1][cmd[2]] = outmap
+        
+    listOfVectors = grass.list_grouped('vect')[grass.gisenv()['MAPSET']]
+    
+    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 "
+                                                     "in the current mapset. "
+                                                     "Do you want to overwrite it?") % outmap,
+                                 caption = _("Overwrite?"),
+                                 style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+        if dlgOw.ShowModal() == wx.ID_YES:
+            overwrite = True
+        else:
+            dlgOw.Destroy()
+            dlg.Destroy()
+            return None
+    
+    if UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'):
+        overwrite = True
+        
+    ret = RunCommand(prog = cmd[0],
+                     parent = parent,
+                     overwrite = overwrite,
+                     **cmd[1])
+    if ret != 0:
+        dlg.Destroy()
+        return None
+    
+    # create attribute table
+    if dlg.table.IsEnabled() and dlg.table.IsChecked():
+        sql = 'CREATE TABLE %s (%s INTEGER)' % (outmap, key)
+        
+        RunCommand('db.connect',
+                   flags = 'c')
+        
+        Debug.msg(1, "SQL: %s" % sql)
+        RunCommand('db.execute',
+                   quiet = True,
+                   parent = parent,
+                   input = '-',
+                   stdin = sql)
+        
+        RunCommand('v.db.connect',
+                   quiet = True,
+                   parent = parent,
+                   map = outmap,
+                   table = outmap,
+                   key = key,
+                   layer = '1')
+    
+    # return fully qualified map name
+    if '@' not in outmap:
+        outmap += '@' + grass.gisenv()['MAPSET']
+    
+    if log:
+        log.WriteLog(_("New vector map <%s> created") % outmap)
+        
+    return dlg
+
+class SavedRegion(wx.Dialog):
+    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?
+        """
+        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.SetLabel(_("Load region:"))
+            selection = Select(parent = self, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
+                               type = 'windows')
+        elif loadsave == 'save':
+            label.SetLabel(_("Save region:"))
+            selection = 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(parent = self, id = wx.ID_OK)
+        btn.SetDefault()
+        btnsizer.AddButton(btn)
+        
+        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)
+        self.Layout()
+        
+    def OnRegion(self, event):
+        self.wind = event.GetString()
+    
+class DecorationDialog(wx.Dialog):
+    """
+    Controls setting options and displaying/hiding map overlay decorations
+    """
+    def __init__(self, parent, ovlId, title, cmd, name = None,
+                 pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE,
+                 checktxt = '', ctrltxt = ''):
+
+        wx.Dialog.__init__(self, parent, wx.ID_ANY, title, pos, size, style)
+
+        self.ovlId   = ovlId   # PseudoDC id
+        self.cmd     = cmd
+        self.name    = name    # overlay name
+        self.parent  = parent  # MapFrame
+
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        box = wx.BoxSizer(wx.HORIZONTAL)
+        self.chkbox = wx.CheckBox(parent = self, id = wx.ID_ANY, label = checktxt)
+        if self.parent.Map.GetOverlay(self.ovlId) is None:
+            self.chkbox.SetValue(True)
+        else:
+            self.chkbox.SetValue(self.parent.MapWindow.overlays[self.ovlId]['layer'].IsActive())
+        box.Add(item = self.chkbox, proportion = 0,
+                flag = wx.ALIGN_CENTRE|wx.ALL, border = 5)
+        sizer.Add(item = box, proportion = 0,
+                  flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+
+        box = wx.BoxSizer(wx.HORIZONTAL)
+        optnbtn = wx.Button(parent = self, id = wx.ID_ANY, label = _("Set options"))
+        box.Add(item = optnbtn, proportion = 0, flag = wx.ALIGN_CENTRE|wx.ALL, border = 5)
+        sizer.Add(item = box, proportion = 0,
+                  flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+        if self.name == 'legend':
+            box = wx.BoxSizer(wx.HORIZONTAL)
+            resize = wx.ToggleButton(parent = self, id = wx.ID_ANY, label = _("Set size and position"))
+            resize.SetToolTipString(_("Click and drag on the map display to set legend"
+                                        " size and position and then press OK"))
+            resize.SetName('resize')
+            if self.parent.IsPaneShown('3d'):
+                resize.Disable()
+            box.Add(item = resize, proportion = 0, flag = wx.ALIGN_CENTRE|wx.ALL, border = 5)
+            sizer.Add(item = box, proportion = 0,
+                      flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+
+        box = wx.BoxSizer(wx.HORIZONTAL)
+        label = wx.StaticText(parent = self, id = wx.ID_ANY,
+                              label = _("Drag %s with mouse in pointer mode to position.\n"
+                                      "Double-click to change options." % ctrltxt))
+        if self.name == 'legend':
+            label.SetLabel(label.GetLabel() + _('\nDefine raster map name for legend in '
+                                                'properties dialog.'))
+        box.Add(item = label, proportion = 0,
+                flag = wx.ALIGN_CENTRE|wx.ALL, border = 5)
+        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.ALL, border = 5)
+
+        # buttons
+        btnsizer = wx.StdDialogButtonSizer()
+
+        self.btnOK = wx.Button(parent = self, id = wx.ID_OK)
+        self.btnOK.SetDefault()
+        if self.name == 'legend':
+            self.btnOK.Enable(False)
+        btnsizer.AddButton(self.btnOK)
+
+        btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL)
+        btnsizer.AddButton(btnCancel)
+        btnsizer.Realize()
+
+        sizer.Add(item = btnsizer, proportion = 0,
+                  flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5)
+
+        #
+        # bindings
+        #
+        self.Bind(wx.EVT_BUTTON,   self.OnOptions, optnbtn)
+        if self.name == 'legend':
+            self.Bind(wx.EVT_TOGGLEBUTTON,   self.OnResize, resize)
+        self.Bind(wx.EVT_BUTTON,   self.OnCancel,  btnCancel)
+        self.Bind(wx.EVT_BUTTON,   self.OnOK,      self.btnOK)
+
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+
+        # create overlay if doesn't exist
+        self._createOverlay()
+        
+        if len(self.parent.MapWindow.overlays[self.ovlId]['cmd']) > 1:
+            if name == 'legend':
+                mapName, found = GetLayerNameFromCmd(self.parent.MapWindow.overlays[self.ovlId]['cmd'])
+                if found:
+                    # enable 'OK' button
+                    self.btnOK.Enable()
+                    # set title
+                    self.SetTitle(_('Legend of raster map <%s>') % \
+                                      mapName)
+            
+        
+    def _createOverlay(self):
+        """!Creates overlay"""
+        if not self.parent.Map.GetOverlay(self.ovlId):
+            self.newOverlay = self.parent.Map.AddOverlay(id = self.ovlId, type = self.name,
+                                                         command = self.cmd,
+                                                         l_active = False, l_render = False, l_hidden = True)
+            prop = { 'layer' : self.newOverlay,
+                     'params' : None,
+                     'propwin' : None,
+                     'cmd' : self.cmd,
+                     'coords': (0, 0),
+                     'pdcType': 'image' }
+            self.parent.MapWindow2D.overlays[self.ovlId] = prop
+            if self.parent.MapWindow3D:
+                self.parent.MapWindow3D.overlays[self.ovlId] = prop
+                
+        else:
+            if self.parent.MapWindow.overlays[self.ovlId]['propwin'] == None:
+                return
+
+            self.parent.MapWindow.overlays[self.ovlId]['propwin'].get_dcmd = self.GetOptData
+
+
+    def OnOptions(self, event):
+        """!Sets option for decoration map overlays
+        """
+        if self.parent.MapWindow.overlays[self.ovlId]['propwin'] is None:
+            # build properties dialog
+            GUI(parent = self.parent).ParseCommand(cmd = self.cmd,
+                                                   completed = (self.GetOptData, self.name, ''))
+            
+        else:
+            if self.parent.MapWindow.overlays[self.ovlId]['propwin'].IsShown():
+                self.parent.MapWindow.overlays[self.ovlId]['propwin'].SetFocus()
+            else:
+                self.parent.MapWindow.overlays[self.ovlId]['propwin'].Show()
+    
+    def OnResize(self, event):
+        if self.FindWindowByName('resize').GetValue():
+            self.parent.MapWindow.SetCursor(self.parent.cursors["cross"])
+            self.parent.MapWindow.mouse['use'] = 'legend'
+            self.parent.MapWindow.mouse['box'] = 'box'
+            self.parent.MapWindow.pen = wx.Pen(colour = 'Black', width = 2, style = wx.SHORT_DASH)
+        else:
+            self.parent.MapWindow.SetCursor(self.parent.cursors["default"])
+            self.parent.MapWindow.mouse['use'] = 'pointer'
+            
+    def OnCancel(self, event):
+        """!Cancel dialog"""
+        if self.name == 'legend' and self.FindWindowByName('resize').GetValue():
+            self.FindWindowByName('resize').SetValue(False)
+            self.OnResize(None)
+            
+        self.parent.dialogs['barscale'] = None
+        
+        if event and hasattr(self, 'newOverlay'):
+            self.parent.Map.DeleteOverlay(self.newOverlay)
+        
+        self.Destroy()
+
+    def OnOK(self, event):
+        """!Button 'OK' pressed"""
+        # enable or disable overlay
+        self.parent.Map.GetOverlay(self.ovlId).SetActive(self.chkbox.IsChecked())
+
+        # update map
+        if self.parent.IsPaneShown('3d'):
+            self.parent.MapWindow.UpdateOverlays()
+            
+        self.parent.MapWindow.UpdateMap()
+
+        # close dialog
+        self.OnCancel(None)
+
+    def GetOptData(self, dcmd, layer, params, propwin):
+        """!Process decoration layer data"""
+        # update layer data
+        if params:
+            self.parent.MapWindow.overlays[self.ovlId]['params'] = params
+        if dcmd:
+            self.parent.MapWindow.overlays[self.ovlId]['cmd'] = dcmd
+        self.parent.MapWindow.overlays[self.ovlId]['propwin'] = propwin
+
+        # change parameters for item in layers list in render.Map
+        # "Use mouse..." (-m) flag causes GUI freeze and is pointless here, trac #119
+        
+        try:
+            self.parent.MapWindow.overlays[self.ovlId]['cmd'].remove('-m')
+        except ValueError:
+            pass
+            
+        self.parent.Map.ChangeOverlay(id = self.ovlId, type = self.name,
+                                      command = self.parent.MapWindow.overlays[self.ovlId]['cmd'],
+                                      l_active = self.parent.MapWindow.overlays[self.ovlId]['layer'].IsActive(),
+                                      l_render = False, l_hidden = True)
+        if  self.name == 'legend':
+            if params and not self.btnOK.IsEnabled():
+                self.btnOK.Enable()
+            
+class TextLayerDialog(wx.Dialog):
+    """
+    Controls setting options and displaying/hiding map overlay decorations
+    """
+
+    def __init__(self, parent, ovlId, title, name = 'text',
+                 pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE):
+
+        wx.Dialog.__init__(self, parent, wx.ID_ANY, title, pos, size, style)
+        from wx.lib.expando import ExpandoTextCtrl, EVT_ETC_LAYOUT_NEEDED
+
+        self.ovlId = ovlId
+        self.parent = parent
+
+        if self.ovlId in self.parent.MapWindow.textdict.keys():
+            self.currText = self.parent.MapWindow.textdict[self.ovlId]['text']
+            self.currFont = self.parent.MapWindow.textdict[self.ovlId]['font']
+            self.currClr  = self.parent.MapWindow.textdict[self.ovlId]['color']
+            self.currRot  = self.parent.MapWindow.textdict[self.ovlId]['rotation']
+            self.currCoords = self.parent.MapWindow.textdict[self.ovlId]['coords']
+            self.currBB = self.parent.MapWindow.textdict[self.ovlId]['bbox']
+        else:
+            self.currClr = wx.BLACK
+            self.currText = ''
+            self.currFont = self.GetFont()
+            self.currRot = 0.0
+            self.currCoords = [10, 10, 10, 10]
+            self.currBB = wx.Rect()
+
+        self.sizer = wx.BoxSizer(wx.VERTICAL)
+        box = wx.GridBagSizer(vgap = 5, hgap = 5)
+
+        # show/hide
+        self.chkbox = wx.CheckBox(parent = self, id = wx.ID_ANY,
+                                  label = _('Show text object'))
+        if self.parent.Map.GetOverlay(self.ovlId) is None:
+            self.chkbox.SetValue(True)
+        else:
+            self.chkbox.SetValue(self.parent.MapWindow.overlays[self.ovlId]['layer'].IsActive())
+        box.Add(item = self.chkbox, span = (1,2),
+                flag = wx.ALIGN_LEFT|wx.ALL, border = 5,
+                pos = (0, 0))
+
+        # text entry
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Enter text:"))
+        box.Add(item = label,
+                flag = wx.ALIGN_CENTER_VERTICAL,
+                pos = (1, 0))
+
+        self.textentry = ExpandoTextCtrl(parent = self, id = wx.ID_ANY, value = "", size = (300,-1))
+        self.textentry.SetFont(self.currFont)
+        self.textentry.SetForegroundColour(self.currClr)
+        self.textentry.SetValue(self.currText)
+        # get rid of unneeded scrollbar when text box first opened
+        self.textentry.SetClientSize((300,-1))
+        
+        box.Add(item = self.textentry,
+                pos = (1, 1))
+
+        # rotation
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Rotation:"))
+        box.Add(item = label,
+                flag = wx.ALIGN_CENTER_VERTICAL,
+                pos = (2, 0))
+        self.rotation = wx.SpinCtrl(parent = self, id = wx.ID_ANY, value = "", pos = (30, 50),
+                                    size = (75,-1), style = wx.SP_ARROW_KEYS)
+        self.rotation.SetRange(-360, 360)
+        self.rotation.SetValue(int(self.currRot))
+        box.Add(item = self.rotation,
+                flag = wx.ALIGN_RIGHT,
+                pos = (2, 1))
+
+        # font
+        fontbtn = wx.Button(parent = self, id = wx.ID_ANY, label = _("Set font"))
+        box.Add(item = fontbtn,
+                flag = wx.ALIGN_RIGHT,
+                pos = (3, 1))
+
+        self.sizer.Add(item = box, proportion = 1,
+                  flag = wx.ALL, border = 10)
+
+        # note
+        box = wx.BoxSizer(wx.HORIZONTAL)
+        label = wx.StaticText(parent = self, id = wx.ID_ANY,
+                              label = _("Drag text with mouse in pointer mode "
+                                      "to position.\nDouble-click to change options"))
+        box.Add(item = label, proportion = 0,
+                flag = wx.ALIGN_CENTRE | wx.ALL, border = 5)
+        self.sizer.Add(item = box, proportion = 0,
+                  flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER | wx.ALL, border = 5)
+
+        line = wx.StaticLine(parent = self, id = wx.ID_ANY,
+                             size = (20,-1), style = wx.LI_HORIZONTAL)
+        self.sizer.Add(item = line, proportion = 0,
+                  flag = wx.EXPAND | wx.ALIGN_CENTRE | wx.ALL, border = 5)
+
+        btnsizer = wx.StdDialogButtonSizer()
+
+        btn = wx.Button(parent = self, id = wx.ID_OK)
+        btn.SetDefault()
+        btnsizer.AddButton(btn)
+
+        btn = wx.Button(parent = self, id = wx.ID_CANCEL)
+        btnsizer.AddButton(btn)
+        btnsizer.Realize()
+
+        self.sizer.Add(item = btnsizer, proportion = 0,
+                       flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+        self.SetSizer(self.sizer)
+        self.sizer.Fit(self)
+
+        # bindings
+        self.Bind(EVT_ETC_LAYOUT_NEEDED, self.OnRefit, self.textentry)
+        self.Bind(wx.EVT_BUTTON,     self.OnSelectFont, fontbtn)
+        self.Bind(wx.EVT_TEXT,       self.OnText,       self.textentry)
+        self.Bind(wx.EVT_SPINCTRL,   self.OnRotation,   self.rotation)
+
+    def OnRefit(self, event):
+        """!Resize text entry to match text"""
+        self.sizer.Fit(self)
+
+    def OnText(self, event):
+        """!Change text string"""
+        self.currText = event.GetString()
+
+    def OnRotation(self, event):
+        """!Change rotation"""
+        self.currRot = event.GetInt()
+
+        event.Skip()
+
+    def OnSelectFont(self, event):
+        """!Change font"""
+        data = wx.FontData()
+        data.EnableEffects(True)
+        data.SetColour(self.currClr)         # set colour
+        data.SetInitialFont(self.currFont)
+
+        dlg = wx.FontDialog(self, data)
+
+        if dlg.ShowModal() == wx.ID_OK:
+            data = dlg.GetFontData()
+            self.currFont = data.GetChosenFont()
+            self.currClr = data.GetColour()
+
+            self.textentry.SetFont(self.currFont)
+            self.textentry.SetForegroundColour(self.currClr)
+
+            self.Layout()
+
+        dlg.Destroy()
+
+    def GetValues(self):
+        """!Get text properties"""
+        return { 'text' : self.currText,
+                 'font' : self.currFont,
+                 'color' : self.currClr,
+                 'rotation' : self.currRot,
+                 'coords' : self.currCoords,
+                 'active' : self.chkbox.IsChecked() }
+
+class GroupDialog(wx.Dialog):
+    """!Dialog for creating/editing groups"""
+    def __init__(self, parent = None, defaultGroup = None, 
+                 title = _("Create or edit imagery groups"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+                     
+        wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title,
+                            style = style, **kwargs)
+                            
+        self.parent = parent
+        self.defaultGroup = defaultGroup
+        self.currentGroup = self.defaultGroup
+        self.groupChanged = False
+        
+        self.bodySizer = self._createDialogBody()
+        
+        # buttons
+        btnOk = wx.Button(parent = self, id = wx.ID_OK)
+        btnApply = wx.Button(parent = self, id = wx.ID_APPLY)
+        btnClose = wx.Button(parent = self, id = wx.ID_CANCEL)
+        
+        btnOk.SetToolTipString(_("Apply changes to selected group and close dialog"))
+        btnApply.SetToolTipString(_("Apply changes to selected group"))
+        btnClose.SetToolTipString(_("Close dialog, changes are not applied"))
+
+        btnOk.SetDefault()
+        
+        # sizers & do layout
+        # btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        # btnSizer.Add(item = btnClose, proportion = 0,
+        #              flag = wx.RIGHT | wx.ALIGN_RIGHT | wx.EXPAND, border = 5)
+        # btnSizer.Add(item = btnApply, proportion = 0,
+        #              flag = wx.LEFT, border = 5)
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(btnOk)
+        btnSizer.AddButton(btnApply)
+        btnSizer.AddButton(btnClose)
+        btnSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = self.bodySizer, proportion = 1,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 10)
+        mainSizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY,
+                      style = wx.LI_HORIZONTAL), proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 10) 
+        
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.ALL | wx.ALIGN_RIGHT, border = 10)
+
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+        
+        btnOk.Bind(wx.EVT_BUTTON, self.OnOk)
+        btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+        btnClose.Bind(wx.EVT_BUTTON, self.OnClose)
+
+        # set dialog min size
+        self.SetMinSize(self.GetSize())
+        
+    def _createDialogBody(self):
+        bodySizer = wx.BoxSizer(wx.VERTICAL)
+    
+        # group selection
+        bodySizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                           label = _("Select the group you want to edit or "
+                                                     "enter name of new group:")),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.TOP, border = 10)
+        self.groupSelect = Select(parent = self, type = 'group',
+                                  mapsets = [grass.gisenv()['MAPSET']],
+                                  size = globalvar.DIALOG_GSELECT_SIZE) # searchpath?
+            
+        bodySizer.Add(item = self.groupSelect, flag = wx.TOP | wx.EXPAND, border = 5)
+        
+        bodySizer.AddSpacer(10)
+        # layers in group
+        bodySizer.Add(item = wx.StaticText(parent = self, label = _("Layers in selected group:")),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM, border = 5)
+        
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        gridSizer.AddGrowableCol(0)
+        
+        self.layerBox = wx.ListBox(parent = self,  id = wx.ID_ANY, size = (-1, 150),
+                                   style = wx.LB_MULTIPLE | wx.LB_NEEDED_SB)
+        
+        gridSizer.Add(item = self.layerBox, pos = (0, 0), span = (2, 1), flag = wx.EXPAND)
+        
+        self.addLayer = wx.Button(self, id = wx.ID_ADD)
+        self.addLayer.SetToolTipString(_("Select map layers and add them to the list."))
+        gridSizer.Add(item = self.addLayer, pos = (0, 1), flag = wx.EXPAND)
+        
+        self.removeLayer = wx.Button(self, id = wx.ID_REMOVE)
+        self.removeLayer.SetToolTipString(_("Remove selected layer(s) from list."))
+        gridSizer.Add(item = self.removeLayer, pos = (1, 1))
+        
+        bodySizer.Add(item = gridSizer, proportion = 1, flag = wx.EXPAND)
+        
+        self.infoLabel = wx.StaticText(parent = self, id = wx.ID_ANY)
+        bodySizer.Add(item = self.infoLabel, 
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.TOP | wx.BOTTOM, border = 5)
+        
+        self.subGroup = wx.CheckBox(parent = self, id = wx.ID_ANY,
+                                    label = _("Define also sub-group (same name as group)"))
+        bodySizer.Add(item = self.subGroup, flag = wx.BOTTOM | wx.EXPAND, border = 5)
+        
+        # bindings
+        self.groupSelect.GetTextCtrl().Bind(wx.EVT_TEXT, self.OnGroupSelected)
+        self.addLayer.Bind(wx.EVT_BUTTON, self.OnAddLayer)
+        self.removeLayer.Bind(wx.EVT_BUTTON, self.OnRemoveLayer)
+        
+        if self.defaultGroup:
+            self.groupSelect.SetValue(self.defaultGroup)
+        
+        return bodySizer
+        
+    def OnAddLayer(self, event):
+        """!Add new layer to listbox"""
+        dlg = MapLayersDialog(parent = self, title = _("Add selected map layers into group"),
+                              mapType = 'raster', selectAll = False,
+                              fullyQualified = True, showFullyQualified = False)
+        if dlg.ShowModal() != wx.ID_OK:
+            dlg.Destroy()
+            return
+        
+        layers = dlg.GetMapLayers()
+        for layer in layers:
+            if layer not in self.GetLayers():
+                self.layerBox.Append(layer)
+                self.groupChanged = True
+            
+    
+    def OnRemoveLayer(self, event):
+        """!Remove layer from listbox"""
+        while self.layerBox.GetSelections():
+            sel = self.layerBox.GetSelections()[0]
+            self.layerBox.Delete(sel)
+            self.groupChanged = True
+                
+    def GetLayers(self):
+        """!Get layers"""
+        return self.layerBox.GetItems()
+        
+    def OnGroupSelected(self, event):
+        """!Text changed in group selector"""
+        # callAfter must be called to close popup before other actions
+        wx.CallAfter(self.GroupSelected)
+        
+    def GroupSelected(self):
+        """!Group was selected, check if changes were apllied"""
+        group = self.GetSelectedGroup()
+        if self.groupChanged:
+            dlg = wx.MessageDialog(self, message = _("Group <%s> was changed, "
+                                                     "do you want to apply changes?") % self.currentGroup,
+                                   caption = _("Unapplied changes"),
+                                   style = wx.YES_NO | wx.ICON_QUESTION | wx.YES_DEFAULT)
+            if dlg.ShowModal() == wx.ID_YES:
+                self.ApplyChanges(showResult = True)
+                
+            dlg.Destroy()
+            
+            
+        
+        groups = self.GetExistGroups()
+        if group in groups:
+            self.ShowGroupLayers(self.GetGroupLayers(group))
+            
+        self.currentGroup = group
+        self.groupChanged = False
+        
+        self.ClearNotification()
+        
+    def ShowGroupLayers(self, mapList):
+        """!Show map layers in currently selected group"""
+        self.layerBox.Set(mapList)
+        
+        
+    def EditGroup(self, group):
+        """!Edit selected group"""
+        layersNew = self.GetLayers()
+        layersOld = self.GetGroupLayers(group)
+        
+        add = []
+        remove = []
+        for layerNew in layersNew:
+            if layerNew not in layersOld:
+                add.append(layerNew)
+                
+        for layerOld in layersOld:
+            if layerOld not in layersNew:
+                remove.append(layerOld)
+        
+        kwargs = {}
+        if self.subGroup.IsChecked():
+            kwargs['subgroup'] = group
+        
+        ret = None
+        if remove:
+            ret = RunCommand('i.group',
+                             parent = self,
+                             group = group,
+                             flags = 'r',
+                             input = ','.join(remove),
+                             **kwargs)
+        
+        if add:
+            ret = RunCommand('i.group',
+                             parent = self,
+                             group = group,
+                             input = ','.join(add),
+                             **kwargs)
+        
+        return ret
+        
+    def CreateNewGroup(self, group):
+        """!Create new group"""
+        layers = self.GetLayers()
+        
+        kwargs = {}
+        if self.subGroup.IsChecked():
+            kwargs['subgroup'] = group
+        
+        return RunCommand('i.group',
+                          parent = self,
+                          group = group,
+                          input = layers,
+                          **kwargs)
+    
+    def GetExistGroups(self):
+        """!Returns existing groups in current mapset"""
+        return grass.list_grouped('group')[grass.gisenv()['MAPSET']]
+        
+    def ShowResult(self, group, returnCode, create):
+        """!Show if operation was successfull."""
+        group += '@' + grass.gisenv()['MAPSET']
+        if returnCode is None:
+            label = _("No changes to apply in group <%s>.") % group
+        elif returnCode == 0:
+            if create:
+                label = _("Group <%s> was successfully created.") % group
+            else:
+                label = _("Group <%s> was successfully changed.") % group
+        else:
+            if create:
+                label = _("Creating of new group <%s> failed.") % group
+            else:
+                label = _("Changing of group <%s> failed.") % group
+                
+        self.infoLabel.SetLabel(label)
+        wx.FutureCall(4000, self.ClearNotification)
+        
+    def GetSelectedGroup(self):
+        """!Return currently selected group (without mapset)"""
+        return self.groupSelect.GetValue().split('@')[0]
+        
+    def GetGroupLayers(self, group):
+        """!Get layers in group"""
+        res = RunCommand('i.group',
+                         parent = self,
+                         flags = 'g',
+                         group = group,
+                         read = True).strip()
+        if res.split('\n')[0]:
+            return res.split('\n')
+        return []
+        
+    def ClearNotification(self):
+        """!Clear notification string"""
+        self.infoLabel.SetLabel("")
+       
+    def ApplyChanges(self, showResult):
+        """!Create or edit group"""
+        group = self.currentGroup
+        if not group:
+            GMessage(parent = self,
+                     message = _("No group selected."))
+            return False
+        
+        groups = self.GetExistGroups()
+        if group in groups:
+            ret = self.EditGroup(group)
+            self.ShowResult(group = group, returnCode = ret, create = False)
+            
+        else:
+            ret = self.CreateNewGroup(group)
+            self.ShowResult(group = group, returnCode = ret, create = True)
+            
+        self.groupChanged = False
+        
+        return True
+        
+    def OnApply(self, event):
+        """!Apply changes"""
+        self.ApplyChanges(showResult = True)
+        
+    def OnOk(self, event):
+        """!Apply changes and close dialog"""
+        if self.ApplyChanges(showResult = False):
+            self.OnClose(event)
+        
+    def OnClose(self, event):
+        """!Close dialog"""
+        if not self.IsModal():
+            self.Destroy()
+        event.Skip()
+        
+class MapLayersDialog(wx.Dialog):
+    def __init__(self, parent, title, modeler = False,
+                 mapType = None, selectAll = True, fullyQualified = True, showFullyQualified = True, 
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        """!Dialog for selecting map layers (raster, vector)
+        
+        Valid mapType values:
+         - raster
+         - raster3d
+         - vector
+        
+        @param mapType type of map (if None: raster, vector, 3d raster, if one only: selects it and disables selection)
+        @param selectAll all/none maps should be selected by default
+        @param fullyQualified True if dialog should return full map names by default
+        @param showFullyQualified True to show 'fullyQualified' checkbox, otherwise hide it
+        """
+        wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title,
+                           style = style, **kwargs)
+        
+        self.parent = parent # GMFrame or ?
+        self.mapType = mapType
+        self.selectAll = selectAll
+        
+        # dialog body
+        self.bodySizer = self._createDialogBody()
+        # update list of layer to be loaded
+        self.map_layers = [] # list of map layers (full list type/mapset)
+        self.LoadMapLayers(self.GetLayerType(cmd = True),
+                           self.mapset.GetStringSelection())
+        
+        self.fullyQualified = wx.CheckBox(parent = self, id = wx.ID_ANY,
+                                          label = _("Use fully-qualified map names"))
+        self.fullyQualified.SetValue(fullyQualified)
+        self.fullyQualified.Show(showFullyQualified)
+
+        self.dseries = None
+        if modeler:
+            self.dseries = wx.CheckBox(parent = self, id = wx.ID_ANY,
+                                       label = _("Dynamic series (%s)") % 'g.mlist')
+            self.dseries.SetValue(False)
+        
+        # buttons
+        btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL)
+        btnOk = wx.Button(parent = self, id = wx.ID_OK)
+        btnOk.SetDefault()
+        
+        # sizers & do layout
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(btnCancel)
+        btnSizer.AddButton(btnOk)
+        btnSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = self.bodySizer, proportion = 1,
+                      flag = wx.EXPAND | wx.ALL, border = 5)
+        mainSizer.Add(item = self.fullyQualified, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5)
+        if self.dseries:
+            mainSizer.Add(item = self.dseries, 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)
+
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+
+        # set dialog min size
+        self.SetMinSize(self.GetSize())
+        
+    def _createDialogBody(self):
+        bodySizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        bodySizer.AddGrowableCol(1)
+        bodySizer.AddGrowableRow(3)
+        
+        # layer type
+        bodySizer.Add(item = wx.StaticText(parent = self, label = _("Map type:")),
+                      flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (0,0))
+        
+        self.layerType = wx.Choice(parent = self, id = wx.ID_ANY,
+                                   choices = [_('raster'), _('3D raster'), _('vector')], size = (100,-1))
+        
+        if self.mapType:
+            if self.mapType == 'raster':
+                self.layerType.SetSelection(0)
+            elif self.mapType == 'raster3d':
+                self.layerType.SetSelection(1)
+            elif self.mapType == 'vector':
+                self.layerType.SetSelection(2)
+            self.layerType.Disable()
+        else:
+            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(self.selectAll)
+        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 = MapsetSelect(parent = self, searchPath = True)
+        self.mapset.SetStringSelection(grass.gisenv()['MAPSET'])
+        bodySizer.Add(item = self.mapset,
+                      pos = (1,1), span = (1, 2))
+        
+        # map name filter
+        bodySizer.Add(item = wx.StaticText(parent = self, label = _("Pattern:")),
+                      flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (2,0))
+        
+        self.filter = wx.TextCtrl(parent = self, id = wx.ID_ANY,
+                                  value = "",
+                                  size = (250,-1))
+        bodySizer.Add(item = self.filter,
+                      flag = wx.EXPAND,
+                      pos = (2,1), span = (1, 2))
+        
+        # layer list 
+        bodySizer.Add(item = wx.StaticText(parent = self, label = _("List of maps:")),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_TOP,
+                      pos = (3,0))
+        self.layers = wx.CheckListBox(parent = self, id = wx.ID_ANY,
+                                      size = (250, 100),
+                                      choices = [])
+        bodySizer.Add(item = self.layers,
+                      flag = wx.EXPAND,
+                      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
+
+        @param type layer type ('raster' or 'vector')
+        @param mapset mapset name
+        """
+        self.map_layers = grass.mlist_grouped(type = type)[mapset]
+        self.layers.Set(self.map_layers)
+        
+        # check all items by default
+        for item in range(self.layers.GetCount()):
+            
+            self.layers.Check(item, check = self.selectAll)
+        
+    def OnChangeParams(self, event):
+        """!Filter parameters changed by user"""
+        # update list of layer to be loaded
+        self.LoadMapLayers(self.GetLayerType(cmd = True),
+                           self.mapset.GetStringSelection())
+        
+        event.Skip()
+        
+    def OnMenu(self, event):
+        """!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.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, _("Invert selection"))
+        menu.Append(self.popupDataID3, _("Deselect all"))
+        
+        self.PopupMenu(menu)
+        menu.Destroy()
+        
+    def OnSelectAll(self, event):
+        """!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"""
+        for item in range(self.layers.GetCount()):
+            self.layers.Check(item, False)
+        
+    def OnFilter(self, event):
+        """!Apply filter for map names"""
+        if len(event.GetString()) == 0:
+           self.layers.Set(self.map_layers) 
+           return 
+        
+        list = []
+        for layer in self.map_layers:
+            try:
+                if re.compile('^' + event.GetString()).search(layer):
+                    list.append(layer)
+            except:
+                pass
+        
+        self.layers.Set(list)
+        self.OnSelectAll(None)
+        
+        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"""
+        layerNames = []
+        for indx in self.layers.GetSelections():
+            # layers.append(self.layers.GetStringSelec(indx))
+            pass
+
+        fullyQualified = self.fullyQualified.IsChecked()
+        mapset = self.mapset.GetStringSelection()
+        for item in range(self.layers.GetCount()):
+            if not self.layers.IsChecked(item):
+                continue
+            if fullyQualified:
+                layerNames.append(self.layers.GetString(item) + '@' + mapset)
+            else:
+                layerNames.append(self.layers.GetString(item))
+        
+        return layerNames
+    
+    def GetLayerType(self, cmd = False):
+        """!Get selected layer type
+
+        @param cmd True for g.mlist
+        """
+        if not cmd:
+            return self.layerType.GetStringSelection()
+        
+        sel = self.layerType.GetSelection()
+        if sel == 0:
+            ltype = 'rast'
+        elif sel == 1:
+            ltype = 'rast3d'
+        else:
+            ltype = 'vect'
+        
+        return ltype
+
+    def GetDSeries(self):
+        """!Used by modeler only
+
+        @return g.mlist command
+        """
+        if not self.dseries or not self.dseries.IsChecked():
+            return ''
+        
+        cond = 'map in `g.mlist type=%s ' % self.GetLayerType(cmd = True)
+        patt = self.filter.GetValue()
+        if patt:
+            cond += 'pattern=%s ' % patt
+        cond += 'mapset=%s`' % self.mapset.GetStringSelection()
+        
+        return cond
+    
+class ImportDialog(wx.Dialog):
+    """!Dialog for bulk import of various data (base class)"""
+    def __init__(self, parent, itype,
+                 id = wx.ID_ANY, title = _("Multiple import"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+        self.parent = parent    # GMFrame 
+        self.importType = itype
+        self.options = dict()   # list of options
+        
+        self.commandId = -1  # id of running command
+        
+        wx.Dialog.__init__(self, parent, id, title, style = style,
+                           name = "MultiImportDialog")
+        
+        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())
+        
+        #
+        # list of layers
+        #
+        self.list = LayersList(self.panel)
+        self.list.LoadData()
+
+        self.optionBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                      label = "%s" % _("Options"))
+        
+        cmd = self._getCommand()
+        task = gtask.parse_interface(cmd)
+        for f in task.get_options()['flags']:
+            name = f.get('name', '')
+            desc = f.get('label', '')
+            if not desc:
+                desc = f.get('description', '')
+            if not name and not desc:
+                continue
+            if cmd == 'r.in.gdal' and name not in ('o', 'e', 'l', 'k'):
+                continue
+            elif cmd == 'r.external' and name not in ('o', 'e', 'r', 'h', 'v'):
+                continue
+            elif cmd == 'v.in.ogr' and name not in ('c', 'z', 't', 'o', 'r', 'e', 'w'):
+                continue
+            elif cmd == 'v.external' and name not in ('b'):
+                continue
+            elif cmd == 'v.in.dxf' and name not in ('e', 't', 'b', 'f', 'i'):
+                continue
+            self.options[name] = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
+                                             label = desc)
+        
+        
+        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
+        #
+        # cancel
+        self.btn_cancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL)
+        self.btn_cancel.SetToolTipString(_("Close dialog"))
+        self.btn_cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+        # run
+        self.btn_run = wx.Button(parent = self.panel, id = wx.ID_OK, label = _("&Import"))
+        self.btn_run.SetToolTipString(_("Import selected layers"))
+        self.btn_run.SetDefault()
+        self.btn_run.Enable(False)
+        self.btn_run.Bind(wx.EVT_BUTTON, self.OnRun)
+        # run command dialog
+        self.btn_cmd = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                 label = _("Command dialog"))
+        self.btn_cmd.Bind(wx.EVT_BUTTON, self.OnCmdDialog)
+        
+    def doLayout(self):
+        """!Do layout"""
+        dialogSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        # dsn input
+        dialogSizer.Add(item = self.dsnInput, proportion = 0,
+                        flag = wx.EXPAND)
+        
+        #
+        # list of DXF layers
+        #
+        layerSizer = wx.StaticBoxSizer(self.layerBox, wx.HORIZONTAL)
+
+        layerSizer.Add(item = self.list, proportion = 1,
+                      flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        dialogSizer.Add(item = layerSizer, proportion = 1,
+                        flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+
+        # options
+        optionSizer = wx.StaticBoxSizer(self.optionBox, wx.VERTICAL)
+        for key in self.options.keys():
+            optionSizer.Add(item = self.options[key], proportion = 0)
+            
+        dialogSizer.Add(item = optionSizer, 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, border = 5)
+        
+        dialogSizer.Add(item = self.add, proportion = 0,
+                        flag = wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+        
+        #
+        # buttons
+        #
+        btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
+        
+        btnsizer.Add(item = self.btn_cmd, proportion = 0,
+                     flag = wx.RIGHT | wx.ALIGN_CENTER,
+                     border = 10)
+        
+        btnsizer.Add(item = self.btn_cancel, proportion = 0,
+                     flag = wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER,
+                     border = 10)
+        
+        btnsizer.Add(item = self.btn_run, proportion = 0,
+                     flag = wx.RIGHT | wx.ALIGN_CENTER,
+                     border = 10)
+        
+        dialogSizer.Add(item = btnsizer, proportion = 0,
+                        flag = wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.ALIGN_RIGHT,
+                        border = 10)
+        
+        # dialogSizer.SetSizeHints(self.panel)
+        self.panel.SetAutoLayout(True)
+        self.panel.SetSizer(dialogSizer)
+        dialogSizer.Fit(self.panel)
+        
+        # auto-layout seems not work here - FIXME
+        size = wx.Size(globalvar.DIALOG_GSELECT_SIZE[0] + 225, 550)
+        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 _getCommand(self):
+        """!Get command"""
+        return ''
+    
+    def OnCancel(self, event = None):
+        """!Close dialog"""
+        self.Close()
+
+    def OnRun(self, event):
+        """!Import/Link data (each layes as separate vector map)"""
+        pass
+
+    def OnCmdDialog(self, event):
+        """!Show command dialog"""
+        pass
+    
+    def AddLayers(self, returncode, cmd = None):
+        """!Add imported/linked layers into layer tree"""
+        if not self.add.IsChecked() or returncode != 0:
+            return
+        
+        self.commandId += 1
+        maptree = self.parent.curr_page.maptree
+        
+        layer, output = self.list.GetLayers()[self.commandId]
+        
+        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, lchecked = False,
+                                    lcmd = cmd)
+        else:
+            item = maptree.AddLayer(ltype = 'vector',
+                                    lname = name, lchecked = False,
+                                    lcmd = ['d.vect',
+                                            'map=%s' % name])
+        
+        maptree.mapdisplay.MapWindow.ZoomToMap()
+        
+    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
+        self.ogr  = ogr
+        
+        if ogr:
+            ImportDialog.__init__(self, parent, itype = 'ogr')
+            if link:
+                self.SetTitle(_("Link external vector data"))
+            else:
+                self.SetTitle(_("Import vector data"))
+        else:
+            ImportDialog.__init__(self, parent, itype = 'gdal') 
+            if link:
+                self.SetTitle(_("Link external raster data"))
+            else:
+                self.SetTitle(_("Import raster data"))
+       
+        self.dsnInput = GdalSelect(parent = self, panel = self.panel, ogr = ogr)
+
+        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"))
+            if ogr:
+                self.btn_cmd.SetToolTipString(_('Open %s dialog') % 'v.external')
+            else:
+                self.btn_cmd.SetToolTipString(_('Open %s dialog') % 'r.external')
+        else:
+            self.btn_run.SetLabel(_("&Import"))
+            self.btn_run.SetToolTipString(_("Import selected layers"))
+            if ogr:
+                self.btn_cmd.SetToolTipString(_('Open %s dialog') % 'v.in.ogr')
+            else:
+                self.btn_cmd.SetToolTipString(_('Open %s dialog') % 'r.in.gdal')
+        
+        self.doLayout()
+
+    def OnRun(self, event):
+        """!Import/Link data (each layes as separate vector map)"""
+        self.commandId = -1
+        data = self.list.GetLayers()
+        
+        dsn = self.dsnInput.GetDsn()
+        ext = self.dsnInput.GetFormatExt()
+            
+        for layer, output in data:
+            if self.importType == 'ogr':
+                if ext and layer.rfind(ext) > -1:
+                    layer = layer.replace('.' + ext, '')
+                if self.link:
+                    cmd = ['v.external',
+                           'dsn=%s' % dsn,
+                           'output=%s' % output,
+                           'layer=%s' % layer]
+                else:
+                    cmd = ['v.in.ogr',
+                           'dsn=%s' % dsn,
+                           'layer=%s' % layer,
+                           'output=%s' % output]
+            else: # gdal
+                if self.dsnInput.GetType() == 'dir':
+                    idsn = os.path.join(dsn, layer)
+                else:
+                    idsn = dsn
+                
+                if self.link:
+                    cmd = ['r.external',
+                           'input=%s' % idsn,
+                           'output=%s' % output]
+                else:
+                    cmd = ['r.in.gdal',
+                           'input=%s' % idsn,
+                           'output=%s' % output]
+            
+            if self.overwrite.IsChecked():
+                cmd.append('--overwrite')
+            
+            for key in self.options.keys():
+                if self.options[key].IsChecked():
+                    cmd.append('-%s' % key)
+            
+            if 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)
+        
+    def _getCommand(self):
+        """!Get command"""
+        if self.link:
+            if self.ogr:
+                return 'v.external'
+            else:
+                return 'r.external'
+        else:
+            if self.ogr:
+                return 'v.in.ogr'
+            else:
+                return 'r.in.gdal'
+        
+        return ''
+    
+    def OnCmdDialog(self, event):
+        """!Show command dialog"""
+        name = self._getCommand()
+        GUI(parent = self, modal = True).ParseCommand(cmd = [name])
+                
+class DxfImportDialog(ImportDialog):
+    """!Dialog for bulk import of DXF layers""" 
+    def __init__(self, parent):
+        ImportDialog.__init__(self, parent, itype = 'dxf',
+                              title = _("Import DXF layers"))
+        
+        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")
+        
+        self.add.SetLabel(_("Add imported layers into layer tree"))
+        
+        self.add.SetValue(UserSettings.Get(group = 'cmd', key = 'addNewLayer', subkey = 'enabled'))
+        
+        self.doLayout()
+
+    def _getCommand(self):
+        """!Get command"""
+        return 'v.in.dxf'
+    
+    def OnRun(self, event):
+        """!Import/Link data (each layes as separate vector map)"""
+        data = self.list.GetLayers()
+        
+        # hide dialog
+        self.Hide()
+        
+        inputDxf = self.dsnInput.GetValue()
+        
+        for layer, output in data:
+            cmd = ['v.in.dxf',
+                   'input=%s' % inputDxf,
+                   'layers=%s' % layer,
+                   'output=%s' % output]
+
+            for key in self.options.keys():
+                if self.options[key].IsChecked():
+                    cmd.append('-%s' % key)
+            
+            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.OnCancel()
+
+    def OnSetDsn(self, event):
+        """!Input DXF file defined, update list of layer widget"""
+        path = event.GetString()
+        if not path:
+            return 
+        
+        data = list()        
+        ret = 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 = GetValidLayerName(layerName)
+            data.append((layerId, layerName.strip(), grassName.strip()))
+        
+        self.list.LoadData(data)
+        if len(data) > 0:
+            self.btn_run.Enable(True)
+        else:
+            self.btn_run.Enable(False)
+
+    def OnCmdDialog(self, event):
+        """!Show command dialog"""
+        GUI(parent = self, modal = True).ParseCommand(cmd = ['v.in.dxf'])
+                
+class LayersList(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,
+                 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,
+                             style = wx.LC_REPORT)
+        listmix.CheckListCtrlMixin.__init__(self)
+        self.log = log
+
+        # setup mixins
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+        listmix.TextEditMixin.__init__(self)
+
+        self.InsertColumn(0, _('Layer id'))
+        self.InsertColumn(1, _('Layer name'))
+        self.InsertColumn(2, _('Name for GRASS map (editable)'))
+        
+        self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnPopupMenu) #wxMSW
+        self.Bind(wx.EVT_RIGHT_UP,            self.OnPopupMenu) #wxGTK
+
+    def LoadData(self, data=None):
+        """!Load data into list"""
+        if data is None:
+            return
+
+        self.DeleteAllItems()
+        
+        for id, name, grassName in data:
+            index = self.InsertStringItem(sys.maxint, str(id))
+            self.SetStringItem(index, 1, "%s" % str(name))
+            self.SetStringItem(index, 2, "%s" % str(grassName))
+        
+        # check by default only on one item
+        if len(data) == 1:
+            self.CheckItem(index, True)
+        
+        self.SetColumnWidth(col = 0, width = wx.LIST_AUTOSIZE_USEHEADER)
+
+    def OnPopupMenu(self, event):
+        """!Show popup menu"""
+        if self.GetItemCount() < 1:
+            return
+        
+        if not hasattr(self, "popupDataID1"):
+            self.popupDataID1 = wx.NewId()
+            self.popupDataID2 = wx.NewId()
+            
+            self.Bind(wx.EVT_MENU, self.OnSelectAll,  id = self.popupDataID1)
+            self.Bind(wx.EVT_MENU, self.OnSelectNone, id = self.popupDataID2)
+        
+        # generate popup-menu
+        menu = wx.Menu()
+        menu.Append(self.popupDataID1, _("Select all"))
+        menu.Append(self.popupDataID2, _("Deselect all"))
+
+        self.PopupMenu(menu)
+        menu.Destroy()
+
+    def OnSelectAll(self, event):
+        """!Select all items"""
+        item = -1
+        
+        while True:
+            item = self.GetNextItem(item)
+            if item == -1:
+                break
+            self.CheckItem(item, True)
+
+        event.Skip()
+        
+    def OnSelectNone(self, event):
+        """!Deselect items"""
+        item = -1
+        
+        while True:
+            item = self.GetNextItem(item, wx.LIST_STATE_SELECTED)
+            if item == -1:
+                break
+            self.CheckItem(item, False)
+
+        event.Skip()
+        
+    def OnLeftDown(self, event):
+        """!Allow editing only output name
+
+        Code taken from TextEditMixin class.
+        """
+        x, y = event.GetPosition()
+        
+        colLocs = [0]
+        loc = 0
+        for n in range(self.GetColumnCount()):
+            loc = loc + self.GetColumnWidth(n)
+            colLocs.append(loc)
+        
+        col = bisect(colLocs, x + self.GetScrollPos(wx.HORIZONTAL)) - 1
+        
+        if col == 2:
+            listmix.TextEditMixin.OnLeftDown(self, event)
+        else:
+            event.Skip()
+        
+    def GetLayers(self):
+        """!Get list of layers (layer name, output name)"""
+        data = []
+        item = -1
+        while True:
+            item = self.GetNextItem(item)
+            if item == -1:
+                break
+            if self.IsChecked(item):
+                # layer / output name
+                data.append((self.GetItem(item, 1).GetText(),
+                             self.GetItem(item, 2).GetText()))
+
+        return data
+
+class SetOpacityDialog(wx.Dialog):
+    """!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):
+
+        self.parent = parent    # GMFrame
+        self.opacity = opacity  # current opacity
+
+        super(SetOpacityDialog, self).__init__(parent, id = id, pos = pos,
+                                               size = size, style = style, title = title)
+
+        panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        box = wx.GridBagSizer(vgap = 5, hgap = 5)
+        self.value = wx.Slider(panel, id = wx.ID_ANY, value = self.opacity,
+                               style = wx.SL_HORIZONTAL | wx.SL_AUTOTICKS | \
+                                   wx.SL_TOP | wx.SL_LABELS,
+                               minValue = 0, maxValue = 100,
+                               size = (350, -1))
+
+        box.Add(item = self.value,
+                flag = wx.ALIGN_CENTRE, pos = (0, 0), span = (1, 2))
+        box.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                   label = _("transparent")),
+                pos = (1, 0))
+        box.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                   label = _("opaque")),
+                flag = wx.ALIGN_RIGHT,
+                pos = (1, 1))
+
+        sizer.Add(item = box, proportion = 0,
+                  flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5)
+
+        line  =  wx.StaticLine(parent = panel, id = wx.ID_ANY,
+                             style = wx.LI_HORIZONTAL)
+        sizer.Add(item = line, proportion = 0,
+                  flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5)
+
+        # buttons
+        btnsizer = wx.StdDialogButtonSizer()
+
+        btnOK = wx.Button(parent = panel, id = wx.ID_OK)
+        btnOK.SetDefault()
+        btnsizer.AddButton(btnOK)
+
+        btnCancel = wx.Button(parent = panel, id = wx.ID_CANCEL)
+        btnsizer.AddButton(btnCancel)
+        btnsizer.Realize()
+
+        sizer.Add(item = btnsizer, proportion = 0,
+                  flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5)
+
+        panel.SetSizer(sizer)
+        sizer.Fit(panel)
+
+        self.SetSize(self.GetBestSize())
+
+        self.Layout()
+
+    def GetOpacity(self):
+        """!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 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)
+
+class SymbolDialog(wx.Dialog):
+    """!Dialog for GRASS symbols selection.
+    
+    Dialog is called in gui_core::forms module.
+    """
+    def __init__(self, parent, symbolPath, currentSymbol = None, title = _("Symbols")):
+        """!Dialog constructor.
+        
+        It is assumed that symbolPath contains folders with symbols.
+        
+        @param parent dialog parent
+        @param symbolPath absolute path to symbols
+        @param currentSymbol currently selected symbol (e.g. 'basic/x')
+        @param title dialog title
+        """
+        wx.Dialog.__init__(self, parent = parent, title = title, id = wx.ID_ANY)
+        
+        self.symbolPath = symbolPath
+        self.currentSymbol = currentSymbol # default basic/x
+        self.selected = None
+        self.selectedDir = None
+        
+        self._layout()
+        
+    def _layout(self):
+        mainPanel = wx.Panel(self, id = wx.ID_ANY)
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        vSizer = wx.BoxSizer( wx.VERTICAL)
+        fgSizer = wx.FlexGridSizer(rows = 2, vgap = 5, hgap = 5)
+        self.folderChoice = wx.Choice(mainPanel, id = wx.ID_ANY, choices = os.listdir(self.symbolPath))
+        self.folderChoice.Bind(wx.EVT_CHOICE, self.OnFolderSelect)
+        
+        fgSizer.Add(item = wx.StaticText(mainPanel, id = wx.ID_ANY, label = _("Symbol directory:")),
+                   proportion = 0,
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+                   
+        fgSizer.Add(item = self.folderChoice, proportion = 0,
+                   flag = wx.ALIGN_CENTER, border = 0)
+                   
+        self.infoLabel = wx.StaticText(mainPanel, id = wx.ID_ANY)
+        fgSizer.Add(wx.StaticText(mainPanel, id = wx.ID_ANY, label = _("Symbol name:")), 
+                    flag = wx.ALIGN_CENTRE_VERTICAL)
+        fgSizer.Add(self.infoLabel, proportion = 0, 
+                    flag = wx.ALIGN_CENTRE_VERTICAL)
+        vSizer.Add(fgSizer, proportion = 0, flag = wx.ALL, border = 5)
+        
+        self.panels = self._createSymbolPanels(mainPanel)
+        for panel in self.panels:
+            vSizer.Add(panel, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+            panel.Bind(EVT_SYMBOL_SELECTION_CHANGED, self.SelectionChanged)
+        
+        mainSizer.Add(vSizer, proportion = 1, flag = wx.ALL| wx.EXPAND, border = 5)
+        self.btnCancel = wx.Button(parent = mainPanel, id = wx.ID_CANCEL)
+        self.btnOK     = wx.Button(parent = mainPanel, id = wx.ID_OK)
+        self.btnOK.SetDefault()
+        self.btnOK.Enable(False)
+        
+        # buttons
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnOK)
+        btnSizer.Realize()
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL, border = 5)
+                      
+        # show panel with the largest number of images and fit size
+        count = []
+        for folder in os.listdir(self.symbolPath):
+            count.append(len(os.listdir(os.path.join(self.symbolPath, folder))))
+            
+        index = count.index(max(count))
+        self.folderChoice.SetSelection(index)
+        self.OnFolderSelect(None)
+        self.infoLabel.Show()
+        
+        mainPanel.SetSizerAndFit(mainSizer)
+        self.SetSize(self.GetBestSize())
+        
+        # show currently selected symbol
+        if self.currentSymbol:
+            # set directory
+            self.selectedDir, self.selected = os.path.split(self.currentSymbol)
+            self.folderChoice.SetStringSelection(self.selectedDir)
+            # select symbol
+            panelIdx = self.folderChoice.GetSelection()
+            for panel in self.symbolPanels[panelIdx]:
+                if panel.GetName() == self.selected:
+                    panel.Select()
+        else:
+            self.folderChoice.SetSelection(0)
+            
+        self.OnFolderSelect(None)
+        
+    def _createSymbolPanels(self, parent):
+        """!Creates multiple panels with symbols.
+        
+        Panels are shown/hidden according to selected folder."""
+        folders = os.listdir(self.symbolPath)
+        
+        panels = []
+        self.symbolPanels = []
+        maxImages = 0
+        
+        for folder in folders:
+            panel = wx.Panel(parent, style = wx.BORDER_RAISED)
+            sizer = wx.GridSizer(cols = 6, vgap = 3, hgap = 3)
+            images = self._getSymbols(path = os.path.join(self.symbolPath, folder))
+        
+            symbolPanels = []
+            for img in images:
+                iP = SingleSymbolPanel(parent = panel, symbolPath = img)
+                sizer.Add(item = iP, proportion = 0, flag = wx.ALIGN_CENTER)
+                symbolPanels.append(iP)
+            
+            panel.SetSizerAndFit(sizer)
+            panel.Hide()
+            panels.append(panel)
+            self.symbolPanels.append(symbolPanels)
+            
+        return panels
+        
+    def _getSymbols(self, path):
+        # we assume that images are in subfolders (1 level only)
+        imageList = []
+        for image in os.listdir(path):
+            imageList.append(os.path.join(path, image))
+                
+        return sorted(imageList)
+            
+    def OnFolderSelect(self, event):
+        """!Selected folder with symbols changed."""
+        idx = self.folderChoice.GetSelection()
+        for i in range(len(self.panels)):
+            sizer = self.panels[i].GetContainingSizer()
+            sizer.Show(self.panels[i], i == idx, recursive = True)
+            sizer.Layout()
+        
+        if self.selectedDir == self.folderChoice.GetStringSelection():
+            self.btnOK.Enable()
+            self.infoLabel.SetLabel(self.selected)
+        else:
+            self.btnOK.Disable()
+            self.infoLabel.SetLabel('')
+        
+    def SelectionChanged(self, event):
+        """!Selected symbol changed."""
+        if event.doubleClick:
+            self.EndModal(wx.ID_OK)
+        # deselect all
+        for i in range(len(self.panels)):
+            for panel in self.symbolPanels[i]:
+                if panel.GetName() != event.name:
+                    panel.Deselect()
+                
+        self.btnOK.Enable()
+        
+        self.selected = event.name
+        self.selectedDir = self.folderChoice.GetStringSelection()
+        
+        self.infoLabel.SetLabel(event.name)
+        
+    def GetSelectedSymbol(self, fullPath = False):
+        """!Returns currently selected symbol.
+        
+        @param fullPath true to return absolute path to symbol,
+        otherwise returns e.g. 'basic/x'
+        """
+        if fullPath:
+            return os.path.join(self.symbolPath, self.selectedDir, self.selected)
+            
+        return os.path.join(self.selectedDir, self.selected)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_core/forms.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_core/forms.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_core/forms.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,2085 @@
+"""
+ at package gui_core.forms
+
+ at brief Construct simple wxPython GUI from a GRASS command interface
+description.
+
+Classes:
+ - forms::UpdateThread
+ - forms::UpdateQThread
+ - forms::TaskFrame
+ - forms::CmdPanel
+ - forms::GUI
+ - forms::GrassGUIApp
+
+This program is just a coarse approach to automatically build a GUI
+from a xml-based GRASS user interface description.
+
+You need to have Python 2.4, wxPython 2.8 and python-xml.
+
+The XML stream is read from executing the command given in the
+command line, thus you may call it for instance this way:
+
+python <this file.py> r.basins.fill
+
+Or you set an alias or wrap the call up in a nice shell script, GUI
+environment ... please contribute your idea.
+
+Updated to wxPython 2.8 syntax and contrib widgets.  Methods added to
+make it callable by gui.  Method added to automatically re-run with
+pythonw on a Mac.
+
+ at todo
+ - verify option value types
+
+Copyright(C) 2000-2011 by the GRASS Development Team
+
+This program is free software under the GPL(>=v2) Read the file
+COPYING coming with GRASS for details.
+
+ at author Jan-Oliver Wagner <jan at intevation.de>
+ at author Bernhard Reiter <bernhard at intevation.de>
+ at author Michael Barton, Arizona State University
+ at author Daniel Calvelo <dca.gis at gmail.com>
+ at author Martin Landa <landa.martin at gmail.com>
+ at author Luca Delucchi <lucadeluge at gmail.com>
+"""
+
+import sys
+import string
+import textwrap
+import os
+import time
+import copy
+import locale
+from threading import Thread
+import Queue
+
+gisbase = os.getenv("GISBASE")
+if gisbase is None:
+    print >>sys.stderr, "We don't seem to be properly installed, or we are being run outside GRASS. Expect glitches."
+    gisbase = os.path.join(os.path.dirname(sys.argv[0]), os.path.pardir)
+    wxbase = gisbase
+else:
+    wxbase = os.path.join(gisbase, 'etc', 'wxpython')
+
+sys.path.append(wxbase)
+
+from core import globalvar
+import wx
+try:
+    import wx.lib.agw.flatnotebook as FN
+except ImportError:
+    import wx.lib.flatnotebook as FN
+import wx.lib.colourselect     as csel
+import wx.lib.filebrowsebutton as filebrowse
+import wx.lib.scrolledpanel    as scrolled
+from wx.lib.newevent import NewEvent
+
+try:
+    import xml.etree.ElementTree as etree
+except ImportError:
+    import elementtree.ElementTree as etree # Python <= 2.4
+
+from grass.script import core as grass
+from grass.script import task as gtask
+
+from gui_core.widgets import StaticWrapText
+from gui_core.ghelp   import HelpPanel
+from gui_core         import gselect
+from core             import gcmd
+from core             import utils
+from core.settings    import UserSettings
+from gui_core.widgets import FloatValidator, GNotebook
+
+wxUpdateDialog, EVT_DIALOG_UPDATE = NewEvent()
+
+# From lib/gis/col_str.c, except purple which is mentioned
+# there but not given RGB values
+str2rgb = {'aqua': (100, 128, 255),
+           'black': (0, 0, 0),
+           'blue': (0, 0, 255),
+           'brown': (180, 77, 25),
+           'cyan': (0, 255, 255),
+           'gray': (128, 128, 128),
+           'green': (0, 255, 0),
+           'grey': (128, 128, 128),
+           'indigo': (0, 128, 255),
+           'magenta': (255, 0, 255),
+           'orange': (255, 128, 0),
+           'purple': (128, 0, 128),
+           'red': (255, 0, 0),
+           'violet': (128, 0, 255),
+           'white': (255, 255, 255),
+           'yellow': (255, 255, 0)}
+rgb2str = {}
+for (s,r) in str2rgb.items():
+    rgb2str[ r ] = s
+
+"""!Hide some options in the GUI"""
+_blackList = { 'enabled' : False,
+               'items'   : { 'd.legend' : { 'flags' : ['m'] } }
+               }
+
+def color_resolve(color):
+    if len(color) > 0 and color[0] in "0123456789":
+        rgb = tuple(map(int, color.split(':')))
+        label = color
+    else:
+        # Convert color names to RGB
+        try:
+            rgb = str2rgb[ color ]
+            label = color
+        except KeyError:
+            rgb = (200,200,200)
+            label = _('Select Color')
+    return (rgb, label)
+
+def text_beautify(someString , width = 70):
+    """
+    Make really long texts shorter, clean up whitespace and
+    remove trailing punctuation.
+    """
+    if width > 0:
+        return escape_ampersand(string.strip(
+                os.linesep.join(textwrap.wrap(utils.normalize_whitespace(someString), width)),
+                ".,;:"))
+    else:
+        return escape_ampersand(string.strip(utils.normalize_whitespace(someString), ".,;:"))
+    
+def escape_ampersand(text):
+    """!Escapes ampersands with additional ampersand for GUI"""
+    return string.replace(text, "&", "&&")
+
+class UpdateThread(Thread):
+    """!Update dialog widgets in the thread"""
+    def __init__(self, parent, event, eventId, task):
+        Thread.__init__(self)
+        
+        self.parent = parent
+        self.event = event
+        self.eventId = eventId
+        self.task = task
+        self.setDaemon(True)
+        
+        # list of functions which updates the dialog
+        self.data = {}
+        
+    def run(self):
+        # get widget id
+        if not self.eventId:
+            for p in self.task.params:
+                if p.get('gisprompt', False) == False:
+                    continue
+                prompt = p.get('element', '')
+                if prompt == 'vector':
+                    name = p.get('name', '')
+                    if name in ('map', 'input'):
+                        self.eventId = p['wxId'][0]
+            if self.eventId is None:
+                return
+        
+        p = self.task.get_param(self.eventId, element = 'wxId', raiseError = False)
+        if not p or 'wxId-bind' not in p:
+            return
+        
+        # get widget prompt
+        pType = p.get('prompt', '')
+        if not pType:
+            return
+        
+        # check for map/input parameter
+        pMap = self.task.get_param('map', raiseError = False)
+        
+        if not pMap:
+            pMap = self.task.get_param('input', raiseError = False)
+
+        if pMap:
+            map = pMap.get('value', '')
+        else:
+            map = None
+
+        # avoid running db.describe several times
+        cparams = dict()
+        cparams[map] = { 'dbInfo' : None,
+                         'layers' : None, }
+        
+        # update reference widgets
+        for uid in p['wxId-bind']:
+            win = self.parent.FindWindowById(uid)
+            if not win:
+                continue
+            
+            name = win.GetName()
+            pBind = self.task.get_param(uid, element = 'wxId', raiseError = False)
+            if pBind:
+                pBind['value'] = ''
+            
+            if name == 'LayerSelect':
+                if map in cparams and not cparams[map]['layers']:
+                    win.InsertLayers(vector = map)
+                    win.Reset()
+                    cparams[map]['layers'] = win.GetItems()
+            
+            elif name == 'TableSelect':
+                pDriver = self.task.get_param('dbdriver', element='prompt', raiseError=False)
+                driver = db = None
+                if pDriver:
+                    driver = pDriver['value']
+                pDb = self.task.get_param('dbname', element='prompt', raiseError=False)
+                if pDb:
+                    db = pDb['value']
+                
+                self.data[win.InsertTables] = { 'driver' : driver,
+                                                'database' : db }
+                
+            elif name == 'ColumnSelect':
+                pLayer = self.task.get_param('layer', element='element', raiseError=False)
+                if pLayer:
+                    if pLayer.get('value', '') != '':
+                        layer = pLayer.get('value', '')
+                    else:
+                        layer = pLayer.get('default', '')
+                else:
+                    layer = 1
+                
+                if map:
+                    if map in cparams:
+                        if not cparams[map]['dbInfo']:
+                            cparams[map]['dbInfo'] = gselect.VectorDBInfo(map)
+                        self.data[win.InsertColumns] = { 'vector' : map, 'layer' : layer,
+                                                         'dbInfo' : cparams[map]['dbInfo'] }
+                else: # table
+                    driver = db = None
+                    pDriver = self.task.get_param('dbdriver', element='prompt', raiseError=False)
+                    if pDriver:
+                        driver = pDriver.get('value', None)
+                    pDb = self.task.get_param('dbname', element='prompt', raiseError=False)
+                    if pDb:
+                        db = pDb.get('value', None)
+                    pTable = self.task.get_param('dbtable', element='element', raiseError=False)
+                    if pTable and \
+                            pTable.get('value', '') != '':
+                        if driver and db:
+                            self.data[win.InsertTableColumns] = { 'table' : pTable.get('value'),
+                                                                  'driver' : driver,
+                                                                  'database' : db }
+                        else:
+                            self.data[win.InsertTableColumns] = { 'table'  : pTable.get('value') }
+            
+            elif name == 'SubGroupSelect':
+                self.data[win.Insert] = { 'group' : p.get('value', '')}
+            
+            elif name == 'LocationSelect':
+                pDbase = self.task.get_param('dbase', element = 'element', raiseError = False)
+                if pDbase:
+                    self.data[win.UpdateItems] = { 'dbase' : pDbase.get('value', '')}
+
+            elif name == 'MapsetSelect':
+                pDbase = self.task.get_param('dbase', element = 'element', raiseError = False)
+                pLocation = self.task.get_param('location', element = 'element', raiseError = False)
+                if pDbase and pLocation:
+                    self.data[win.UpdateItems] = { 'dbase' : pDbase.get('value', ''),
+                                                   'location' : pLocation.get('value', '')}
+
+            elif name ==  'ProjSelect':
+                pDbase = self.task.get_param('dbase', element = 'element', raiseError = False)
+                pLocation = self.task.get_param('location', element = 'element', raiseError = False)
+                pMapset = self.task.get_param('mapset', element = 'element', raiseError = False)
+                if pDbase and pLocation and pMapset:
+                    self.data[win.UpdateItems] = { 'dbase' : pDbase.get('value', ''),
+                                                   'location' : pLocation.get('value', ''),
+                                                   'mapset' : pMapset.get('value', '')}
+            
+def UpdateDialog(parent, event, eventId, task):
+    return UpdateThread(parent, event, eventId, task)
+
+class UpdateQThread(Thread):
+    """!Update dialog widgets in the thread"""
+    requestId = 0
+    def __init__(self, parent, requestQ, resultQ, **kwds):
+        Thread.__init__(self, **kwds)
+        
+        self.parent = parent # CmdPanel
+        self.setDaemon(True)
+        
+        self.requestQ = requestQ
+        self.resultQ = resultQ
+        
+        self.start()
+        
+    def Update(self, callable, *args, **kwds):
+        UpdateQThread.requestId +=  1
+        
+        self.request = None
+        self.requestQ.put((UpdateQThread.requestId, callable, args, kwds))
+        
+        return UpdateQThread.requestId
+    
+    def run(self):
+        while True:
+            requestId, callable, args, kwds = self.requestQ.get()
+            
+            requestTime = time.time()
+            
+            self.request = callable(*args, **kwds)
+
+            self.resultQ.put((requestId, self.request.run()))
+           
+            if self.request:
+                event = wxUpdateDialog(data = self.request.data)
+                wx.PostEvent(self.parent, event)
+    
+class TaskFrame(wx.Frame):
+    """!This is the Frame containing the dialog for options input.
+
+    The dialog is organized in a notebook according to the guisections
+    defined by each GRASS command.
+
+    If run with a parent, it may Apply, Ok or Cancel; the latter two
+    close the dialog.  The former two trigger a callback.
+
+    If run standalone, it will allow execution of the command.
+
+    The command is checked and sent to the clipboard when clicking
+    'Copy'.
+    """
+    def __init__(self, parent, ID, task_description,
+                 get_dcmd = None, layer = None):
+        self.get_dcmd = get_dcmd
+        self.layer    = layer
+        self.task     = task_description
+        self.parent   = parent            # LayerTree | Modeler | None | ...
+        if parent and parent.GetName() ==  'Modeler':
+            self.modeler = self.parent
+        else:
+            self.modeler = None
+        
+        # module name + keywords
+        if self.task.name.split('.')[-1] in ('py', 'sh'):
+            title = str(self.task.name.rsplit('.',1)[0])
+        else:
+            title = self.task.name
+        try:
+            if self.task.keywords !=  ['']:
+                title +=   " [" + ', '.join(self.task.keywords) + "]"
+        except ValueError:
+            pass
+        
+        wx.Frame.__init__(self, parent = parent, id = ID, title = title,
+                          pos = wx.DefaultPosition, style = wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL,
+                          name = "MainFrame")
+        
+        self.locale = wx.Locale(language = wx.LANGUAGE_DEFAULT)
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        # statusbar
+        self.CreateStatusBar()
+        
+        # icon
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_dialog.ico'), wx.BITMAP_TYPE_ICO))
+        
+        guisizer = wx.BoxSizer(wx.VERTICAL)
+        
+        # set apropriate output window
+        if self.parent:
+            self.standalone = False
+        else:
+            self.standalone = True
+        
+        # logo + description
+        topsizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        # GRASS logo
+        self.logo = wx.StaticBitmap(parent = self.panel,
+                                    bitmap = wx.Bitmap(name = os.path.join(globalvar.ETCIMGDIR,
+                                                                           'grass_form.png'),
+                                                     type = wx.BITMAP_TYPE_PNG))
+        topsizer.Add(item = self.logo, proportion = 0, border = 3,
+                     flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL)
+        
+        # add module description
+        if self.task.label:
+            module_desc = self.task.label + ' ' + self.task.description
+        else:
+            module_desc = self.task.description
+        self.description = StaticWrapText(parent = self.panel,
+                                          label = module_desc)
+        topsizer.Add(item = self.description, proportion = 1, border = 5,
+                     flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
+        
+        guisizer.Add(item = topsizer, proportion = 0, flag = wx.EXPAND)
+        
+        self.panel.SetSizerAndFit(guisizer)
+        self.Layout()
+        
+        # notebooks
+        self.notebookpanel = CmdPanel(parent = self.panel, task = self.task,
+                                      frame = self)
+        self.goutput = self.notebookpanel.goutput
+        self.notebookpanel.OnUpdateValues = self.updateValuesHook
+        guisizer.Add(item = self.notebookpanel, proportion = 1, flag = wx.EXPAND)
+        
+        # status bar
+        status_text = _("Enter parameters for '") + self.task.name + "'"
+        try:
+            self.task.get_cmd()
+            self.updateValuesHook()
+        except ValueError:
+            self.SetStatusText(status_text)
+        
+        # buttons
+        btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
+        # cancel
+        self.btn_cancel = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
+        self.btn_cancel.SetToolTipString(_("Close this window without executing the command (Ctrl+Q)"))
+        btnsizer.Add(item = self.btn_cancel, proportion = 0, flag = wx.ALL | wx.ALIGN_CENTER, border = 10)
+        self.btn_cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+
+        if self.get_dcmd is not None: # A callback has been set up
+            btn_apply = wx.Button(parent = self.panel, id = wx.ID_APPLY)
+            btn_ok = wx.Button(parent = self.panel, id = wx.ID_OK)
+            btn_ok.SetDefault()
+
+            btnsizer.Add(item = btn_apply, proportion = 0,
+                         flag = wx.ALL | wx.ALIGN_CENTER,
+                         border = 10)
+            btnsizer.Add(item = btn_ok, proportion = 0,
+                         flag = wx.ALL | wx.ALIGN_CENTER,
+                         border = 10)
+            
+            btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)
+            btn_ok.Bind(wx.EVT_BUTTON, self.OnOK)
+        else: # We're standalone
+            # run
+            self.btn_run = wx.Button(parent = self.panel, id = wx.ID_OK, label =  _("&Run"))
+            self.btn_run.SetToolTipString(_("Run the command (Ctrl+R)"))
+            self.btn_run.SetDefault()
+            self.btn_run.SetForegroundColour(wx.Colour(35, 142, 35))
+            
+            # copy
+            self.btn_clipboard = wx.Button(parent = self.panel, id = wx.ID_COPY)
+            self.btn_clipboard.SetToolTipString(_("Copy the current command string to the clipboard (Ctrl+C)"))
+            
+            btnsizer.Add(item = self.btn_run, proportion = 0,
+                         flag = wx.ALL | wx.ALIGN_CENTER,
+                         border = 10)
+            
+            btnsizer.Add(item = self.btn_clipboard, proportion = 0,
+                         flag = wx.ALL | wx.ALIGN_CENTER,
+                         border = 10)
+            
+            self.btn_run.Bind(wx.EVT_BUTTON, self.OnRun)
+            self.btn_clipboard.Bind(wx.EVT_BUTTON, self.OnCopy)
+        # help
+        self.btn_help = wx.Button(parent = self.panel, id = wx.ID_HELP)
+        self.btn_help.SetToolTipString(_("Show manual page of the command (Ctrl+H)"))
+        self.btn_help.Bind(wx.EVT_BUTTON, self.OnHelp)
+        if self.notebookpanel.notebook.GetPageIndexByName('manual') < 0:
+            self.btn_help.Hide()
+        
+        # add help button
+        btnsizer.Add(item = self.btn_help, proportion = 0, flag = wx.ALL | wx.ALIGN_CENTER, border = 10)
+        
+        guisizer.Add(item = btnsizer, proportion = 0, flag = wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT,
+                     border = 30)
+        
+        if self.parent and not self.modeler:
+            addLayer = False
+            for p in self.task.params:
+                if p.get('age', 'old') ==  'new' and \
+                   p.get('prompt', '') in ('raster', 'vector', '3d-raster'):
+                    addLayer = True
+            
+            if addLayer:
+                # add newly created map into layer tree
+                self.addbox = wx.CheckBox(parent = self.panel,
+                                          label = _('Add created map(s) into layer tree'), style = wx.NO_BORDER)
+                self.addbox.SetValue(UserSettings.Get(group = 'cmd', key = 'addNewLayer', subkey = 'enabled'))
+                guisizer.Add(item = self.addbox, proportion = 0,
+                             flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                             border = 5)
+        
+        hasNew = False
+        for p in self.task.params:
+            if p.get('age', 'old') ==  'new':
+                hasNew = True
+                break
+        
+        if self.get_dcmd is None and hasNew:
+            # close dialog when command is terminated
+            self.closebox = wx.CheckBox(parent = self.panel,
+                                        label = _('Close dialog on finish'), style = wx.NO_BORDER)
+            self.closebox.SetValue(UserSettings.Get(group = 'cmd', key = 'closeDlg', subkey = 'enabled'))
+            self.closebox.SetToolTipString(_("Close dialog when command is successfully finished. "
+                                             "Change this settings in Preferences dialog ('Command' tab)."))
+            guisizer.Add(item = self.closebox, proportion = 0,
+                         flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                         border = 5)
+        
+        self.Bind(wx.EVT_CLOSE,  self.OnCancel)
+        self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
+        
+        # do layout
+        # called automatically by SetSizer()
+        self.panel.SetAutoLayout(True) 
+        self.panel.SetSizerAndFit(guisizer)
+        
+        sizeFrame = self.GetBestSize()
+        self.SetMinSize(sizeFrame)
+        self.SetSize(wx.Size(sizeFrame[0], sizeFrame[1] + 0.33 * max(self.notebookpanel.panelMinHeight,
+                                                                     self.notebookpanel.constrained_size[1])))
+        
+        # thread to update dialog
+        # create queues
+        self.requestQ = Queue.Queue()
+        self.resultQ = Queue.Queue()
+        self.updateThread = UpdateQThread(self.notebookpanel, self.requestQ, self.resultQ)
+        
+        self.Layout()
+        
+        # keep initial window size limited for small screens
+        width, height = self.GetSizeTuple()
+        self.SetSize(wx.Size(min(width, 650),
+                             min(height, 500)))
+        
+        # fix goutput's pane size (required for Mac OSX)
+        if self.goutput:  	 	 
+            self.goutput.SetSashPosition(int(self.GetSize()[1] * .75))
+        
+    def updateValuesHook(self, event = None):
+        """!Update status bar data"""
+        self.SetStatusText(' '.join(self.notebookpanel.createCmd(ignoreErrors = True)))
+        if event:
+            event.Skip()
+        
+    def OnKeyUp(self, event):
+        """!Key released (check hot-keys)"""
+        try:
+            kc = chr(event.GetKeyCode())
+        except ValueError:
+            event.Skip()
+            return
+        
+        if not event.ControlDown():
+            event.Skip()
+            return
+        
+        if kc ==  'Q':
+            self.OnCancel(None)
+        elif kc ==  'S':
+            self.OnAbort(None)
+        elif kc ==  'H':
+            self.OnHelp(None)
+        elif kc ==  'R':
+            self.OnRun(None)
+        elif kc ==  'C':
+            self.OnCopy(None)
+        
+        event.Skip()
+
+    def OnDone(self, cmd, returncode):
+        """!This function is launched from OnRun() when command is
+        finished
+
+        @param returncode command's return code (0 for success)
+        """
+        if not self.parent or returncode !=  0:
+            return
+        if self.parent.GetName() not in ('LayerTree', 'LayerManager'):
+            return
+        
+        if self.parent.GetName() ==  'LayerTree':
+            display = self.parent.GetMapDisplay()
+        else: # Layer Manager
+            display = self.parent.GetLayerTree().GetMapDisplay()
+            
+        if not display or not display.IsAutoRendered():
+            return
+        
+        mapLayers = map(lambda x: x.GetName(),
+                        display.GetMap().GetListOfLayers(l_type = 'raster') +
+                        display.GetMap().GetListOfLayers(l_type = 'vector'))
+        
+        task = GUI(show = None).ParseCommand(cmd)
+        for p in task.get_options()['params']:
+            if p.get('prompt', '') not in ('raster', 'vector'):
+                continue
+            mapName = p.get('value', '')
+            if '@' not in mapName:
+                mapName = mapName + '@' + grass.gisenv()['MAPSET']
+            if mapName in mapLayers:
+                display.GetWindow().UpdateMap(render = True)
+                return
+        
+    def OnOK(self, event):
+        """!OK button pressed"""
+        cmd = self.OnApply(event)
+        if cmd is not None and self.get_dcmd is not None:
+            self.OnCancel(event)
+
+    def OnApply(self, event):
+        """!Apply the command"""
+        if self.modeler:
+            cmd = self.createCmd(ignoreErrors = True, ignoreRequired = True)
+        else:
+            cmd = self.createCmd()
+        
+        if cmd is not None and self.get_dcmd is not None:
+            # return d.* command to layer tree for rendering
+            self.get_dcmd(cmd, self.layer, {"params": self.task.params, 
+                                            "flags" : self.task.flags},
+                          self)
+            # echo d.* command to output console
+            # self.parent.writeDCommand(cmd)
+
+        return cmd
+
+    def OnRun(self, event):
+        """!Run the command"""
+        cmd = self.createCmd()
+        
+        if not cmd or len(cmd) < 1:
+            return
+        
+        if self.standalone or cmd[0][0:2] !=  "d.":
+            # Send any non-display command to parent window (probably wxgui.py)
+            # put to parents switch to 'Command output'
+            self.notebookpanel.notebook.SetSelectionByName('output')
+            
+            try:
+                
+                self.goutput.RunCmd(cmd, onDone = self.OnDone)
+            except AttributeError, e:
+                print >> sys.stderr, "%s: Probably not running in wxgui.py session?" % (e)
+                print >> sys.stderr, "parent window is: %s" % (str(self.parent))
+        else:
+            gcmd.Command(cmd)
+        
+        # update buttons status
+        for btn in (self.btn_run,
+                    self.btn_cancel,
+                    self.btn_clipboard,
+                    self.btn_help):
+            btn.Enable(False)
+        
+    def OnAbort(self, event):
+        """!Abort running command"""
+        event = goutput.wxCmdAbort(aborted = True)
+        wx.PostEvent(self.goutput, event)
+
+    def OnCopy(self, event):
+        """!Copy the command"""
+        cmddata = wx.TextDataObject()
+        # list -> string
+        cmdstring = ' '.join(self.createCmd(ignoreErrors = True))
+        cmddata.SetText(cmdstring)
+        if wx.TheClipboard.Open():
+#            wx.TheClipboard.UsePrimarySelection(True)
+            wx.TheClipboard.SetData(cmddata)
+            wx.TheClipboard.Close()
+            self.SetStatusText(_("'%s' copied to clipboard") % \
+                                    (cmdstring))
+
+    def OnCancel(self, event):
+        """!Cancel button pressed"""
+        self.MakeModal(False)
+        
+        if self.get_dcmd and \
+                self.parent and \
+                self.parent.GetName() in ('LayerTree',
+                                          'MapWindow'):
+            # display decorations and 
+            # pressing OK or cancel after setting layer properties
+            if self.task.name in ['d.barscale','d.legend','d.histogram'] \
+                or len(self.parent.GetPyData(self.layer)[0]['cmd']) >=  1:
+                self.Hide()
+            # canceled layer with nothing set
+            elif len(self.parent.GetPyData(self.layer)[0]['cmd']) < 1:
+                self.parent.Delete(self.layer)
+                self.Destroy()
+        else:
+            # cancel for non-display commands
+            self.Destroy()
+
+    def OnHelp(self, event):
+        """!Show manual page (switch to the 'Manual' notebook page)"""
+        if self.notebookpanel.notebook.GetPageIndexByName('manual') > -1:
+            self.notebookpanel.notebook.SetSelectionByName('manual')
+            self.notebookpanel.OnPageChange(None)
+        
+        if event:    
+            event.Skip()
+        
+    def createCmd(self, ignoreErrors = False, ignoreRequired = False):
+        """!Create command string (python list)"""
+        return self.notebookpanel.createCmd(ignoreErrors = ignoreErrors,
+                                            ignoreRequired = ignoreRequired)
+
+class CmdPanel(wx.Panel):
+    """!A panel containing a notebook dividing in tabs the different
+    guisections of the GRASS cmd.
+    """
+    def __init__(self, parent, task, id = wx.ID_ANY, frame = None, *args, **kwargs):
+        if frame:
+            self.parent = frame
+        else:
+            self.parent = parent
+        self.task = task
+        
+        wx.Panel.__init__(self, parent, id = id, *args, **kwargs)
+        
+        # Determine tab layout
+        sections = []
+        is_section = {}
+        not_hidden = [ p for p in self.task.params + self.task.flags if not p.get('hidden', False) ==  True ]
+
+        self.label_id = [] # wrap titles on resize
+
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+        
+        for task in not_hidden:
+            if task.get('required', False):
+                # All required go into Main, even if they had defined another guisection
+                task['guisection'] = _('Required')
+            if task.get('guisection','') ==  '':
+                # Undefined guisections end up into Options
+                task['guisection'] = _('Optional')
+            if task['guisection'] not in is_section:
+                # We do it like this to keep the original order, except for Main which goes first
+                is_section[task['guisection']] = 1
+                sections.append(task['guisection'])
+            else:
+                is_section[ task['guisection'] ] +=  1
+        del is_section
+
+        # 'Required' tab goes first, 'Optional' as the last one
+        for (newidx,content) in [ (0,_('Required')), (len(sections)-1,_('Optional')) ]:
+            if content in sections:
+                idx = sections.index(content)
+                sections[idx:idx+1] = []
+                sections[newidx:newidx] =  [content]
+
+        panelsizer = wx.BoxSizer(orient = wx.VERTICAL)
+
+        # Build notebook
+        self.notebook = GNotebook(self, style = globalvar.FNPageStyle)
+        self.notebook.SetTabAreaColour(globalvar.FNPageColor)
+        self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChange)
+
+        tab = {}
+        tabsizer = {}
+        for section in sections:
+            tab[section] = scrolled.ScrolledPanel(parent = self.notebook)
+            tab[section].SetScrollRate(10, 10)
+            tabsizer[section] = wx.BoxSizer(orient = wx.VERTICAL)
+            self.notebook.AddPage(page = tab[section], text = section)
+        
+        # are we running from command line?
+        ### add 'command output' tab regardless standalone dialog
+        if self.parent.GetName() ==  "MainFrame" and self.parent.get_dcmd is None:
+            from gui_core.goutput import GMConsole
+            self.goutput = GMConsole(parent = self, margin = False)
+            self.outpage = self.notebook.AddPage(page = self.goutput, text = _("Command output"), name = 'output')
+        else:
+            self.goutput = None
+        
+        self.manual_tab = HelpPanel(parent = self, grass_command = self.task.name)
+        if not self.manual_tab.IsFile():
+            self.manual_tab.Hide()
+        else:
+            self.notebook.AddPage(page = self.manual_tab, text = _("Manual"), name = 'manual')
+        
+        self.notebook.SetSelection(0)
+
+        panelsizer.Add(item = self.notebook, proportion = 1, flag = wx.EXPAND)
+
+        #
+        # flags
+        #
+        text_style = wx.FONTWEIGHT_NORMAL
+        visible_flags = [ f for f in self.task.flags if not f.get('hidden', False) ==  True ]
+        for f in visible_flags:
+            which_sizer = tabsizer[ f['guisection'] ]
+            which_panel = tab[ f['guisection'] ]
+            # if label is given: description -> tooltip
+            if f.get('label','') !=  '':
+                title = text_beautify(f['label'])
+                tooltip = text_beautify(f['description'], width = -1)
+            else:
+                title = text_beautify(f['description'])
+                tooltip = None
+            title_sizer = wx.BoxSizer(wx.HORIZONTAL)
+            rtitle_txt = wx.StaticText(parent = which_panel,
+                                       label = '(' + f['name'] + ')')
+            chk = wx.CheckBox(parent = which_panel, label = title, style = wx.NO_BORDER)
+            self.label_id.append(chk.GetId())
+            if tooltip:
+                chk.SetToolTipString(tooltip)
+            chk.SetValue(f.get('value', False))
+            title_sizer.Add(item = chk, proportion = 1,
+                            flag = wx.EXPAND)
+            title_sizer.Add(item = rtitle_txt, proportion = 0,
+                            flag = wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
+            which_sizer.Add(item = title_sizer, proportion = 0,
+                            flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 5)
+            f['wxId'] = [ chk.GetId(), ]
+            chk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
+            
+            if self.parent.GetName() ==  'MainFrame' and self.parent.modeler:
+                parChk = wx.CheckBox(parent = which_panel, id = wx.ID_ANY,
+                                     label = _("Parameterized in model"))
+                parChk.SetName('ModelParam')
+                parChk.SetValue(f.get('parameterized', False))
+                if 'wxId' in f:
+                    f['wxId'].append(parChk.GetId())
+                else:
+                    f['wxId'] = [ parChk.GetId() ]
+                parChk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
+                which_sizer.Add(item = parChk, proportion = 0,
+                                flag = wx.LEFT, border = 20)
+            
+            if f['name'] in ('verbose', 'quiet'):
+                chk.Bind(wx.EVT_CHECKBOX, self.OnVerbosity)
+                vq = UserSettings.Get(group = 'cmd', key = 'verbosity', subkey = 'selection')
+                if f['name'] ==  vq:
+                    chk.SetValue(True)
+                    f['value'] = True
+            elif f['name'] ==  'overwrite' and 'value' not in f:
+                chk.SetValue(UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'))
+                f['value'] = UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled')
+                
+        #
+        # parameters
+        #
+        visible_params = [ p for p in self.task.params if not p.get('hidden', False) ==  True ]
+        
+        try:
+            first_param = visible_params[0]
+        except IndexError:
+            first_param = None
+        
+        for p in visible_params:
+            which_sizer = tabsizer[ p['guisection'] ]
+            which_panel = tab[ p['guisection'] ]
+            # if label is given -> label and description -> tooltip
+            # otherwise description -> lavel
+            if p.get('label','') !=  '':
+                title = text_beautify(p['label'])
+                tooltip = text_beautify(p['description'], width = -1)
+            else:
+                title = text_beautify(p['description'])
+                tooltip = None
+            txt = None
+
+            # text style (required -> bold)
+            if not p.get('required', False):
+                text_style = wx.FONTWEIGHT_NORMAL
+            else:
+                text_style = wx.FONTWEIGHT_BOLD
+
+            # title sizer (description, name, type)
+            if (len(p.get('values', [])) > 0) and \
+                    p.get('multiple', False) and \
+                    p.get('gisprompt',False) ==  False and \
+                    p.get('type', '') ==  'string':
+                title_txt = wx.StaticBox(parent = which_panel, id = wx.ID_ANY)
+            else:
+                title_sizer = wx.BoxSizer(wx.HORIZONTAL)
+                title_txt = wx.StaticText(parent = which_panel)
+                if p['key_desc']:
+                    ltype = ','.join(p['key_desc'])
+                else:
+                    ltype = p['type']
+                rtitle_txt = wx.StaticText(parent = which_panel,
+                                           label = '(' + p['name'] + '=' + ltype + ')')
+                title_sizer.Add(item = title_txt, proportion = 1,
+                                flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
+                title_sizer.Add(item = rtitle_txt, proportion = 0,
+                                flag = wx.ALIGN_RIGHT | wx.RIGHT | wx.TOP, border = 5)
+                which_sizer.Add(item = title_sizer, proportion = 0,
+                                flag = wx.EXPAND)
+            self.label_id.append(title_txt.GetId())
+
+            # title expansion
+            if p.get('multiple', False) and len(p.get('values','')) ==  0:
+                title = _("[multiple]") + " " + title
+                if p.get('value','') ==   '' :
+                    p['value'] = p.get('default','')
+
+            if (len(p.get('values', [])) > 0):
+                valuelist      = map(str, p.get('values',[]))
+                valuelist_desc = map(unicode, p.get('values_desc',[]))
+
+                if p.get('multiple', False) and \
+                        p.get('gisprompt',False) ==  False and \
+                        p.get('type', '') ==  'string':
+                    title_txt.SetLabel(" %s: (%s, %s) " % (title, p['name'], p['type']))
+                    if valuelist_desc:
+                        hSizer = wx.StaticBoxSizer(box = title_txt, orient = wx.VERTICAL)
+                    else:
+                        hSizer = wx.StaticBoxSizer(box = title_txt, orient = wx.HORIZONTAL)
+                    isEnabled = {}
+                    # copy default values
+                    if p['value'] ==  '':
+                        p['value'] = p.get('default', '')
+                        
+                    for defval in p.get('value', '').split(','):
+                        isEnabled[ defval ] = 'yes'
+                        # for multi checkboxes, this is an array of all wx IDs
+                        # for each individual checkbox
+                        p['wxId'] = list()
+                    idx = 0
+                    for val in valuelist:
+                        try:
+                            label = valuelist_desc[idx]
+                        except IndexError:
+                            label = val
+                        
+                        chkbox = wx.CheckBox(parent = which_panel,
+                                             label = text_beautify(label))
+                        p[ 'wxId' ].append(chkbox.GetId())
+                        if val in isEnabled:
+                            chkbox.SetValue(True)
+                        hSizer.Add(item = chkbox, proportion = 0,
+                                    flag = wx.ADJUST_MINSIZE | wx.ALL, border = 1)
+                        chkbox.Bind(wx.EVT_CHECKBOX, self.OnCheckBoxMulti)
+                        idx +=  1
+                        
+                    which_sizer.Add(item = hSizer, proportion = 0,
+                                    flag = wx.EXPAND | wx.TOP | wx.RIGHT | wx.LEFT, border = 5)
+                elif p.get('gisprompt', False) ==  False:
+                    if len(valuelist) ==  1: # -> textctrl
+                        title_txt.SetLabel("%s (%s %s):" % (title, _('valid range'),
+                                                            str(valuelist[0])))
+                        
+                        if p.get('type', '') ==  'integer' and \
+                                not p.get('multiple', False):
+
+                            # for multiple integers use textctrl instead of spinsctrl
+                            try:
+                                minValue, maxValue = map(int, valuelist[0].split('-'))
+                            except ValueError:
+                                minValue = -1e6
+                                maxValue = 1e6
+                            txt2 = wx.SpinCtrl(parent = which_panel, id = wx.ID_ANY, size = globalvar.DIALOG_SPIN_SIZE,
+                                               min = minValue, max = maxValue)
+                            txt2.SetName("SpinCtrl")
+                            style = wx.BOTTOM | wx.LEFT
+                        else:
+                            txt2 = wx.TextCtrl(parent = which_panel, value = p.get('default',''))
+                            txt2.SetName("TextCtrl")
+                            style = wx.EXPAND | wx.BOTTOM | wx.LEFT
+                        
+                        value = self._getValue(p)
+                        # parameter previously set
+                        if value:
+                            if txt2.GetName() ==  "SpinCtrl":
+                                txt2.SetValue(int(value))
+                            else:
+                                txt2.SetValue(value)
+                        
+                        which_sizer.Add(item = txt2, proportion = 0,
+                                        flag = style, border = 5)
+
+                        p['wxId'] = [ txt2.GetId(), ]
+                        txt2.Bind(wx.EVT_TEXT, self.OnSetValue)
+                    else:
+                        title_txt.SetLabel(title + ':')
+                        value = self._getValue(p)
+                        
+                        if p['name'] == 'icon': # symbols
+                            bitmap = wx.Bitmap(os.path.join(globalvar.ETCSYMBOLDIR, value) + '.png')
+                            bb = wx.BitmapButton(parent = which_panel, id = wx.ID_ANY,
+                                                 bitmap = bitmap)
+                            iconLabel = wx.StaticText(parent = which_panel, id = wx.ID_ANY)
+                            iconLabel.SetLabel(value)
+                            p['value'] = value
+                            p['wxId'] = [bb.GetId(), iconLabel.GetId()]
+                            bb.Bind(wx.EVT_BUTTON, self.OnSetSymbol)
+                            this_sizer = wx.BoxSizer(wx.HORIZONTAL)
+                            this_sizer.Add(item = bb, proportion = 0,
+                                            flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT, border = 5)
+                            this_sizer.Add(item = iconLabel, proportion = 0,
+                                            flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT | wx.ALIGN_CENTER_VERTICAL, border = 5)
+                            which_sizer.Add(item = this_sizer, proportion = 0,
+                                            flag = wx.ADJUST_MINSIZE, border = 0)
+                        else:
+                            # list of values (combo)
+                            cb = wx.ComboBox(parent = which_panel, id = wx.ID_ANY, value = p.get('default',''),
+                                             size = globalvar.DIALOG_COMBOBOX_SIZE,
+                                             choices = valuelist, style = wx.CB_DROPDOWN)
+                            if value:
+                                cb.SetValue(value) # parameter previously set
+                            which_sizer.Add(item = cb, proportion = 0,
+                                            flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT, border = 5)
+                            p['wxId'] = [cb.GetId(),]
+                            cb.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
+                            cb.Bind(wx.EVT_TEXT, self.OnSetValue)
+            
+            # text entry
+            if (p.get('type','string') in ('string','integer','float')
+                and len(p.get('values',[])) ==  0
+                and p.get('gisprompt',False) ==  False
+                and p.get('prompt','') !=  'color'):
+
+                title_txt.SetLabel(title + ':')
+                if p.get('multiple', False) or \
+                        p.get('type', 'string') ==  'string' or \
+                        len(p.get('key_desc', [])) > 1:
+                    txt3 = wx.TextCtrl(parent = which_panel, value = p.get('default',''))
+                    
+                    value = self._getValue(p)
+                    if value:
+                        # parameter previously set
+                        txt3.SetValue(str(value))
+                    
+                    txt3.Bind(wx.EVT_TEXT, self.OnSetValue)
+                    style = wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT
+                else:
+                    minValue = -1e9
+                    maxValue = 1e9
+                    if p.get('type', '') ==  'integer':
+                        txt3 = wx.SpinCtrl(parent = which_panel, value = p.get('default',''),
+                                           size = globalvar.DIALOG_SPIN_SIZE,
+                                           min = minValue, max = maxValue)
+                        style = wx.BOTTOM | wx.LEFT | wx.RIGHT
+                        
+                        value = self._getValue(p)
+                        if value:
+                            txt3.SetValue(int(value)) # parameter previously set
+                        
+                        txt3.Bind(wx.EVT_SPINCTRL, self.OnSetValue)
+                    else:
+                        txt3 = wx.TextCtrl(parent = which_panel, value = p.get('default',''),
+                                           validator = FloatValidator())
+                        style = wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT
+                        
+                        value = self._getValue(p)
+                        if value:
+                            txt3.SetValue(str(value)) # parameter previously set
+                    
+                txt3.Bind(wx.EVT_TEXT, self.OnSetValue)
+                
+                which_sizer.Add(item = txt3, proportion = 0,
+                                flag = style, border = 5)
+                p['wxId'] = [ txt3.GetId(), ]
+
+            #
+            # element selection tree combobox (maps, icons, regions, etc.)
+            #
+            if p.get('gisprompt', False) ==  True:
+                title_txt.SetLabel(title + ':')
+                # GIS element entry
+                if p.get('prompt','') not in ('color',
+                                              'color_none',
+                                              'subgroup',
+                                              'dbdriver',
+                                              'dbname',
+                                              'dbtable',
+                                              'dbcolumn',
+                                              'layer',
+                                              'layer_all',
+                                              'layer_zero',
+                                              'location',
+                                              'mapset',
+                                              'dbase') and \
+                       p.get('element', '') !=  'file':
+                    multiple = p.get('multiple', False)
+                    if p.get('age', '') ==  'new':
+                        mapsets = [grass.gisenv()['MAPSET'],]
+                    else:
+                        mapsets = None
+                    if self.task.name in ('r.proj', 'v.proj') \
+                            and p.get('name', '') ==  'input':
+                        if self.task.name ==  'r.proj':
+                            isRaster = True
+                        else:
+                            isRaster = False
+                        selection = gselect.ProjSelect(parent = which_panel,
+                                                       isRaster = isRaster)
+                        p['wxId'] = [ selection.GetId(), ]
+                        selection.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
+                        selection.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
+                    else:
+                        selection = gselect.Select(parent = which_panel, id = wx.ID_ANY,
+                                                   size = globalvar.DIALOG_GSELECT_SIZE,
+                                                   type = p.get('element', ''),
+                                                   multiple = multiple, mapsets = mapsets,
+                                                   fullyQualified = p.get('age', 'old') == 'old')
+                        
+                        
+                        # A select.Select is a combobox with two children: a textctl and a popupwindow;
+                        # we target the textctl here
+                        textWin = selection.GetTextCtrl()
+                        p['wxId'] = [ textWin.GetId(), ]
+                        textWin.Bind(wx.EVT_TEXT, self.OnSetValue)
+                    
+                    value = self._getValue(p)
+                    if value:
+                        selection.SetValue(value) # parameter previously set
+
+                    which_sizer.Add(item=selection, proportion=0,
+                                    flag=wx.ADJUST_MINSIZE| wx.BOTTOM | wx.LEFT | wx.RIGHT, border=5)
+                    
+                    if p.get('prompt', '') in ('vector', 'group'):
+                        selection.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
+                # subgroup
+                elif p.get('prompt', '') ==  'subgroup':
+                    selection = gselect.SubGroupSelect(parent = which_panel)
+                    p['wxId'] = [ selection.GetId() ]
+                    selection.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
+                    selection.Bind(wx.EVT_TEXT,     self.OnSetValue)
+                    which_sizer.Add(item = selection, proportion = 0,
+                                    flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.TOP | wx.ALIGN_CENTER_VERTICAL,
+                                    border = 5)
+                
+                # layer, dbdriver, dbname, dbcolumn, dbtable entry
+                elif p.get('prompt', '') in ('dbdriver',
+                                             'dbname',
+                                             'dbtable',
+                                             'dbcolumn',
+                                             'layer',
+                                             'layer_all',
+                                             'layer_zero',
+                                             'location',
+                                             'mapset',
+                                             'dbase'):
+                    if p.get('multiple', 'no') ==  'yes':
+                        win = wx.TextCtrl(parent = which_panel, value = p.get('default',''),
+                                          size = globalvar.DIALOG_TEXTCTRL_SIZE)
+                        win.Bind(wx.EVT_TEXT, self.OnSetValue)
+                    else:
+                        value = self._getValue(p)
+                        
+                        if p.get('prompt', '') in ('layer',
+                                                   'layer_all',
+                                                   'layer_zero'):
+                            
+                            if p.get('age', 'old_layer') == 'old_layer':
+                                initial = list()
+                                if p.get('prompt', '') ==  'layer_all':
+                                    initial.insert(0, '-1')
+                                elif p.get('prompt', '') == 'layer_zero':
+                                    initial.insert(0, '0')
+                                lyrvalue = p.get('default')
+                                if lyrvalue != '':
+                                    if lyrvalue not in initial:
+                                        initial.append(str(lyrvalue))
+                                lyrvalue = p.get('value')
+                                if lyrvalue != '':
+                                    if lyrvalue not in initial:
+                                        initial.append(str(lyrvalue))
+
+                                win = gselect.LayerSelect(parent = which_panel,
+                                                          initial = initial,
+                                                          default = p['default'])
+                                p['wxGetValue'] = win.GetStringSelection
+                                win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
+                                win.Bind(wx.EVT_TEXT, self.OnSetValue)
+                                win.SetValue(str(value))    # default or previously set value
+                            else:
+                                win = wx.SpinCtrl(parent = which_panel, id = wx.ID_ANY,
+                                                  min = 1, max = 100, initial = int(p['default']))
+                                win.Bind(wx.EVT_SPINCTRL, self.OnSetValue)
+                                win.SetValue(int(value))    # default or previously set value
+
+                        elif p.get('prompt', '') ==  'dbdriver':
+                            win = gselect.DriverSelect(parent = which_panel,
+                                                       choices = p.get('values', []),
+                                                       value = value)
+                            p['wxGetValue'] = win.GetStringSelection
+                            win.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
+                            win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
+                        elif p.get('prompt', '') ==  'dbname':
+                            win = gselect.DatabaseSelect(parent = which_panel,
+                                                         value = value)
+                            win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
+                            win.Bind(wx.EVT_TEXT, self.OnSetValue)
+                        
+                        elif p.get('prompt', '') == 'dbtable':
+                            if p.get('age', 'old_dbtable') == 'old_dbtable':
+                                win = gselect.TableSelect(parent=which_panel)
+                                
+                                p['wxGetValue'] = win.GetStringSelection
+                                win.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
+                                win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
+                            else:
+                                win = wx.TextCtrl(parent = which_panel, value = p.get('default',''),
+                                                  size = globalvar.DIALOG_TEXTCTRL_SIZE)
+                                win.Bind(wx.EVT_TEXT, self.OnSetValue)
+                        elif p.get('prompt', '') ==  'dbcolumn':
+                            win = gselect.ColumnSelect(parent = which_panel,
+                                                       value = value,
+                                                       param = p)
+                            win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
+                            win.Bind(wx.EVT_TEXT,     self.OnSetValue)
+
+                        elif p.get('prompt', '') ==  'location':
+                            win = gselect.LocationSelect(parent = which_panel,
+                                                         value = value)
+                            win.Bind(wx.EVT_COMBOBOX,     self.OnUpdateSelection)
+                            win.Bind(wx.EVT_COMBOBOX,     self.OnSetValue)
+                        
+                        elif p.get('prompt', '') ==  'mapset':
+                            win = gselect.MapsetSelect(parent = which_panel,
+                                                       value = value)
+                            win.Bind(wx.EVT_COMBOBOX,     self.OnUpdateSelection)
+                            win.Bind(wx.EVT_COMBOBOX,     self.OnSetValue)
+                            
+                        elif p.get('prompt', '') ==  'dbase':
+                            win = gselect.DbaseSelect(parent = which_panel,
+                                                      changeCallback = self.OnSetValue)
+                            win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
+                            p['wxId'] = [ win.GetChildren()[1].GetId() ]
+                            
+                    if 'wxId' not in p:
+                        try:
+                            p['wxId'] = [ win.GetId(), ]
+                        except AttributeError:
+                            pass
+                    
+                    which_sizer.Add(item = win, proportion = 0,
+                                    flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT, border = 5)
+                # color entry
+                elif p.get('prompt', '') in ('color',
+                                             'color_none'):
+                    default_color = (200,200,200)
+                    label_color = _("Select Color")
+                    if p.get('default','') !=  '':
+                        default_color, label_color = color_resolve(p['default'])
+                    if p.get('value','') !=  '': # parameter previously set
+                        default_color, label_color = color_resolve(p['value'])
+                    if p.get('prompt', '') ==  'color_none':
+                        this_sizer = wx.BoxSizer(orient = wx.HORIZONTAL)
+                    else:
+                        this_sizer = which_sizer
+                    btn_colour = csel.ColourSelect(parent = which_panel, id = wx.ID_ANY,
+                                                   label = label_color, colour = default_color,
+                                                   pos = wx.DefaultPosition, size = (150,-1))
+                    this_sizer.Add(item = btn_colour, proportion = 0,
+                                   flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT, border = 5)
+                    # For color selectors, this is a two-member array, holding the IDs of
+                    # the selector proper and either a "transparent" button or None
+                    p['wxId'] = [btn_colour.GetId(),]
+                    btn_colour.Bind(csel.EVT_COLOURSELECT,  self.OnColorChange)
+                    if p.get('prompt', '') ==  'color_none':
+                        none_check = wx.CheckBox(which_panel, wx.ID_ANY, _("Transparent"))
+                        if p.get('value','') !=  '' and p.get('value',[''])[0] ==  "none":
+                            none_check.SetValue(True)
+                        else:
+                            none_check.SetValue(False)
+                        this_sizer.Add(item = none_check, proportion = 0,
+                                       flag = wx.ADJUST_MINSIZE | wx.LEFT | wx.RIGHT | wx.TOP, border = 5)
+                        which_sizer.Add(this_sizer)
+                        none_check.Bind(wx.EVT_CHECKBOX, self.OnColorChange)
+                        p['wxId'].append(none_check.GetId())
+                    else:
+                        p['wxId'].append(None)
+                # file selector
+                elif p.get('prompt','') !=  'color' and p.get('element', '') ==  'file':
+                    if p.get('age', 'new_file') == 'new_file':
+                        fmode = wx.SAVE
+                    else:
+                        fmode = wx.OPEN
+                    fbb = filebrowse.FileBrowseButton(parent = which_panel, id = wx.ID_ANY, fileMask = '*',
+                                                      size = globalvar.DIALOG_GSELECT_SIZE, labelText = '',
+                                                      dialogTitle = _('Choose %s') % \
+                                                          p.get('description',_('File')),
+                                                      buttonText = _('Browse'),
+                                                      startDirectory = os.getcwd(), fileMode = fmode,
+                                                      changeCallback = self.OnSetValue)
+                    value = self._getValue(p)
+                    if value:
+                        fbb.SetValue(value) # parameter previously set
+                    which_sizer.Add(item = fbb, proportion = 0,
+                                    flag = wx.EXPAND | wx.RIGHT, border = 5)
+                    
+                    # A file browse button is a combobox with two children:
+                    # a textctl and a button;
+                    # we have to target the button here
+                    p['wxId'] = [ fbb.GetChildren()[1].GetId() ]
+                    if p.get('age', 'new_file') == 'old_file' and \
+                            UserSettings.Get(group='cmd', key='interactiveInput', subkey='enabled'):
+                        # widget for interactive input
+                        ifbb = wx.TextCtrl(parent = which_panel, id = wx.ID_ANY,
+                                           style = wx.TE_MULTILINE,
+                                           size = (-1, 75))
+                        if p.get('value', '') and os.path.isfile(p['value']):
+                            f = open(p['value'])
+                            ifbb.SetValue(''.join(f.readlines()))
+                            f.close()
+                        
+                        ifbb.Bind(wx.EVT_TEXT, self.OnFileText)
+                        
+                        btnLoad = wx.Button(parent = which_panel, id = wx.ID_ANY, label = _("&Load"))
+                        btnLoad.Bind(wx.EVT_BUTTON, self.OnFileLoad)
+                        btnSave = wx.Button(parent = which_panel, id = wx.ID_SAVEAS)
+                        btnSave.Bind(wx.EVT_BUTTON, self.OnFileSave)
+                        
+                        which_sizer.Add(item = wx.StaticText(parent = which_panel, id = wx.ID_ANY,
+                                                             label = _('or enter values interactively')),
+                                        proportion = 0,
+                                        flag = wx.EXPAND | wx.RIGHT | wx.LEFT | wx.BOTTOM, border = 5)
+                        which_sizer.Add(item = ifbb, proportion = 1,
+                                        flag = wx.EXPAND | wx.RIGHT | wx.LEFT, border = 5)
+                        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+                        btnSizer.Add(item = btnLoad, proportion = 0,
+                                     flag = wx.ALIGN_RIGHT | wx.RIGHT, border = 10)
+                        btnSizer.Add(item = btnSave, proportion = 0,
+                                     flag = wx.ALIGN_RIGHT)
+                        which_sizer.Add(item = btnSizer, proportion = 0,
+                                        flag = wx.ALIGN_RIGHT | wx.RIGHT | wx.TOP, border = 5)
+                        
+                        p['wxId'].append(ifbb.GetId())
+                        p['wxId'].append(btnLoad.GetId())
+                        p['wxId'].append(btnSave.GetId())
+                
+            if self.parent.GetName() == 'MainFrame' and self.parent.modeler:
+                parChk = wx.CheckBox(parent = which_panel, id = wx.ID_ANY,
+                                     label = _("Parameterized in model"))
+                parChk.SetName('ModelParam')
+                parChk.SetValue(p.get('parameterized', False))
+                if 'wxId' in p:
+                    p['wxId'].append(parChk.GetId())
+                else:
+                    p['wxId'] = [ parChk.GetId() ]
+                parChk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
+                which_sizer.Add(item = parChk, proportion = 0,
+                                flag = wx.LEFT, border = 20)
+                
+            if title_txt is not None:
+                # create tooltip if given
+                if len(p['values_desc']) > 0:
+                    if tooltip:
+                        tooltip +=  2 * os.linesep
+                    else:
+                        tooltip = ''
+                    if len(p['values']) ==  len(p['values_desc']):
+                        for i in range(len(p['values'])):
+                            tooltip +=  p['values'][i] + ': ' + p['values_desc'][i] + os.linesep
+                    tooltip.strip(os.linesep)
+                if tooltip:
+                    title_txt.SetToolTipString(tooltip)
+
+            if p ==  first_param:
+                if 'wxId' in p and len(p['wxId']) > 0:
+                    win = self.FindWindowById(p['wxId'][0])
+                    win.SetFocus()
+        
+        #
+        # set widget relations for OnUpdateSelection
+        #
+        pMap = None
+        pLayer = []
+        pDriver = None
+        pDatabase = None
+        pTable = None
+        pColumn = []
+        pGroup = None
+        pSubGroup = None
+        pDbase = None
+        pLocation = None
+        pMapset = None
+        for p in self.task.params:
+            if p.get('gisprompt', False) ==  False:
+                continue
+            
+            prompt = p.get('element', '')
+            if prompt in ('cell', 'vector'):
+                name = p.get('name', '')
+                if name in ('map', 'input'):
+                    pMap = p
+            elif prompt ==  'layer':
+                pLayer.append(p)
+            elif prompt ==  'dbcolumn':
+                pColumn.append(p)
+            elif prompt ==  'dbdriver':
+                pDriver = p
+            elif prompt ==  'dbname':
+                pDatabase = p
+            elif prompt ==  'dbtable':
+                pTable = p
+            elif prompt ==  'group':
+                pGroup = p
+            elif prompt ==  'subgroup':
+                pSubGroup = p
+            elif prompt ==  'dbase':
+                pDbase = p
+            elif prompt ==  'location':
+                pLocation = p
+            elif prompt ==  'mapset':
+                pMapset = p
+        
+        pColumnIds = []
+        for p in pColumn:
+            pColumnIds +=  p['wxId']
+        pLayerIds = []
+        for p in pLayer:
+            pLayerIds +=  p['wxId']
+        
+        if pMap:
+            pMap['wxId-bind'] = copy.copy(pColumnIds)
+            if pLayer:
+                pMap['wxId-bind'] +=  pLayerIds
+        if pLayer:
+            for p in pLayer:
+                p['wxId-bind'] = copy.copy(pColumnIds)
+        
+        if pDriver and pTable:
+            pDriver['wxId-bind'] = pTable['wxId']
+
+        if pDatabase and pTable:
+            pDatabase['wxId-bind'] = pTable['wxId']
+
+        if pTable and pColumnIds:
+            pTable['wxId-bind'] = pColumnIds
+        
+        if pGroup and pSubGroup:
+            pGroup['wxId-bind'] = pSubGroup['wxId']
+
+        if pDbase and pLocation:
+            pDbase['wxId-bind'] = pLocation['wxId']
+
+        if pLocation and pMapset:
+            pLocation['wxId-bind'] = pMapset['wxId']
+        
+        if pLocation and pMapset and pMap:
+            pLocation['wxId-bind'] +=  pMap['wxId']
+            pMapset['wxId-bind'] = pMap['wxId']
+        
+	#
+	# determine panel size
+	#
+        maxsizes = (0, 0)
+        for section in sections:
+            tab[section].SetSizer(tabsizer[section])
+            tabsizer[section].Fit(tab[section])
+            tab[section].Layout()
+            minsecsizes = tabsizer[section].GetSize()
+            maxsizes = map(lambda x: max(maxsizes[x], minsecsizes[x]), (0, 1))
+
+        # TODO: be less arbitrary with these 600
+        self.panelMinHeight = 100
+        self.constrained_size = (min(600, maxsizes[0]) + 25, min(400, maxsizes[1]) + 25)
+        for section in sections:
+            tab[section].SetMinSize((self.constrained_size[0], self.panelMinHeight))
+        
+        if self.manual_tab.IsLoaded():
+            self.manual_tab.SetMinSize((self.constrained_size[0], self.panelMinHeight))
+        
+        self.SetSizer(panelsizer)
+        panelsizer.Fit(self.notebook)
+        
+        self.Bind(EVT_DIALOG_UPDATE, self.OnUpdateDialog)
+
+    def _getValue(self, p):
+        """!Get value or default value of given parameter
+
+        @param p parameter directory
+        """
+        if p.get('value', '') !=  '':
+            return p['value']
+        return p.get('default', '')
+        
+    def OnFileLoad(self, event):
+        """!Load file to interactive input"""
+        me = event.GetId()
+        win = dict()
+        for p in self.task.params:
+            if 'wxId' in p and me in p['wxId']:
+                win['file'] = self.FindWindowById(p['wxId'][0])
+                win['text'] = self.FindWindowById(p['wxId'][1])
+                break
+        
+        if not win:
+            return
+        
+        path = win['file'].GetValue()
+        if not path:
+            gcmd.GMessage(parent = self,
+                          message = _("Nothing to load."))
+            return
+        
+        data = ''
+        f = open(path, "r")
+        try:
+            data = f.read()
+        finally:
+            f.close()
+        
+        win['text'].SetValue(data)
+        
+    def OnFileSave(self, event):
+        """!Save interactive input to the file"""
+        wId = event.GetId()
+        win = {}
+        for p in self.task.params:
+            if wId in p.get('wxId', []):
+                win['file'] = self.FindWindowById(p['wxId'][0])
+                win['text'] = self.FindWindowById(p['wxId'][1])
+                break
+        
+        if not win:
+            return
+
+        text = win['text'].GetValue()
+        if not text:
+            gcmd.GMessage(parent = self,
+                          message = _("Nothing to save."))
+            return
+        
+        dlg = wx.FileDialog(parent = self,
+                            message = _("Save input as..."),
+                            defaultDir = os.getcwd(),
+                            style = wx.SAVE | wx.FD_OVERWRITE_PROMPT)
+
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            f = open(path, "w")
+            try:
+                f.write(text + os.linesep)
+            finally:
+                f.close()
+            
+            win['file'].SetValue(path)
+        
+        dlg.Destroy()
+        
+    def OnFileText(self, event):
+        """File input interactively entered"""
+        text = event.GetString()
+        p = self.task.get_param(value = event.GetId(), element = 'wxId', raiseError = False)
+        if not p:
+            return # should not happen
+        win = self.FindWindowById(p['wxId'][0])
+        if text:
+            filename = win.GetValue()
+            if not filename:
+                # outFile = tempfile.NamedTemporaryFile(mode = 'w+b')
+                filename = grass.tempfile()
+                win.SetValue(filename)
+                
+            f = open(filename, "w")
+            try:
+                f.write(text)
+                if text[-1] != os.linesep:
+                    f.write(os.linesep)
+            finally:
+                f.close()
+        else:
+            win.SetValue('')
+        
+    def OnUpdateDialog(self, event):
+        for fn, kwargs in event.data.iteritems():
+            fn(**kwargs)
+        
+        self.parent.updateValuesHook()
+        
+    def OnVerbosity(self, event):
+        """!Verbosity level changed"""
+        verbose = self.FindWindowById(self.task.get_flag('verbose')['wxId'][0])
+        quiet = self.FindWindowById(self.task.get_flag('quiet')['wxId'][0])
+        if event.IsChecked():
+            if event.GetId() ==  verbose.GetId():
+                if quiet.IsChecked():
+                    quiet.SetValue(False)
+                    self.task.get_flag('quiet')['value'] = False
+            else:
+                if verbose.IsChecked():
+                    verbose.SetValue(False)
+                    self.task.get_flag('verbose')['value'] = False
+
+        event.Skip()
+
+    def OnPageChange(self, event):
+        if not event:
+            sel = self.notebook.GetSelection()
+        else:
+            sel = event.GetSelection()
+        
+        idx = self.notebook.GetPageIndexByName('manual')
+        if idx > -1 and sel ==  idx:
+            # calling LoadPage() is strangely time-consuming (only first call)
+            # FIXME: move to helpPage.__init__()
+            if not self.manual_tab.IsLoaded():
+                wx.Yield()
+                self.manual_tab.LoadPage()
+
+        self.Layout()
+
+    def OnColorChange(self, event):
+        myId = event.GetId()
+        for p in self.task.params:
+            if 'wxId' in p and myId in p['wxId']:
+                has_button = p['wxId'][1] is not None
+                if has_button and wx.FindWindowById(p['wxId'][1]).GetValue() ==  True:
+                    p[ 'value' ] = 'none'
+                else:
+                    colorchooser = wx.FindWindowById(p['wxId'][0])
+                    new_color = colorchooser.GetValue()[:]
+                    # This is weird: new_color is a 4-tuple and new_color[:] is a 3-tuple
+                    # under wx2.8.1
+                    new_label = rgb2str.get(new_color, ':'.join(map(str,new_color)))
+                    colorchooser.SetLabel(new_label)
+                    colorchooser.SetColour(new_color)
+                    colorchooser.Refresh()
+                    p[ 'value' ] = colorchooser.GetLabel()
+        self.OnUpdateValues()
+
+    def OnUpdateValues(self, event = None):
+        """!If we were part of a richer interface, report back the
+        current command being built.
+
+        This method should be set by the parent of this panel if
+        needed. It's a hook, actually.  Beware of what is 'self' in
+        the method def, though. It will be called with no arguments.
+        """
+        pass
+
+    def OnCheckBoxMulti(self, event):
+        """!Fill the values as a ','-separated string according to
+        current status of the checkboxes.
+        """
+        me = event.GetId()
+        theParam = None
+        for p in self.task.params:
+            if 'wxId' in p and me in p['wxId']:
+                theParam = p
+                myIndex = p['wxId'].index(me)
+
+        # Unpack current value list
+        currentValues = {}
+        for isThere in theParam.get('value', '').split(','):
+            currentValues[isThere] = 1
+        theValue = theParam['values'][myIndex]
+
+        if event.Checked():
+            currentValues[ theValue ] = 1
+        else:
+            del currentValues[ theValue ]
+
+        # Keep the original order, so that some defaults may be recovered
+        currentValueList = [] 
+        for v in theParam['values']:
+            if v in currentValues:
+                currentValueList.append(v)
+
+        # Pack it back
+        theParam['value'] = ','.join(currentValueList)
+
+        self.OnUpdateValues()
+
+    def OnSetValue(self, event):
+        """!Retrieve the widget value and set the task value field
+        accordingly.
+        
+        Use for widgets that have a proper GetValue() method, i.e. not
+        for selectors.
+        """
+        myId = event.GetId()
+        me = wx.FindWindowById(myId)
+        name = me.GetName()
+        
+        found = False
+        for porf in self.task.params + self.task.flags:
+            if 'wxId' not in porf:
+                continue
+            if myId in porf['wxId']:
+                found = True
+                break
+        
+        if not found:
+            return
+        
+        if name in ('DriverSelect', 'TableSelect',
+                    'LocationSelect', 'MapsetSelect', 'ProjSelect'):
+            porf['value'] = me.GetStringSelection()
+        elif name ==  'GdalSelect':
+            porf['value'] = event.dsn
+        elif name ==  'ModelParam':
+            porf['parameterized'] = me.IsChecked()
+        elif name ==  'LayerSelect':
+            porf['value'] = me.GetValue()
+        else:
+            porf['value'] = me.GetValue()
+        
+        self.OnUpdateValues(event)
+        
+        event.Skip()
+        
+    def OnSetSymbol(self, event):
+        """!Shows dialog for symbol selection"""
+        myId = event.GetId()
+        
+        for p in self.task.params:
+            if 'wxId' in p and myId in p['wxId']:
+                from gui_core.dialogs import SymbolDialog
+                dlg = SymbolDialog(self, symbolPath = globalvar.ETCSYMBOLDIR,
+                                   currentSymbol = p['value'])
+                if dlg.ShowModal() == wx.ID_OK:
+                    img = dlg.GetSelectedSymbol(fullPath = True)
+                    p['value'] = dlg.GetSelectedSymbol(fullPath = False)
+                    
+                    bitmapButton = wx.FindWindowById(p['wxId'][0])
+                    label = wx.FindWindowById(p['wxId'][1])
+                    
+                    bitmapButton.SetBitmapLabel(wx.Bitmap(img + '.png'))
+                    label.SetLabel(p['value'])
+                    
+                    self.OnUpdateValues(event)
+                    
+                dlg.Destroy()
+ 
+    def OnUpdateSelection(self, event):
+        """!Update dialog (layers, tables, columns, etc.)
+        """
+        if not hasattr(self.parent, "updateThread"):
+            if event:
+                event.Skip()
+            return
+        if event:
+            self.parent.updateThread.Update(UpdateDialog,
+                                            self,
+                                            event,
+                                            event.GetId(),
+                                            self.task)
+        else:
+            self.parent.updateThread.Update(UpdateDialog,
+                                            self,
+                                            None,
+                                            None,
+                                            self.task)
+            
+    def createCmd(self, ignoreErrors = False, ignoreRequired = False):
+        """!Produce a command line string (list) or feeding into GRASS.
+
+        @param ignoreErrors True then it will return whatever has been
+        built so far, even though it would not be a correct command
+        for GRASS
+        """
+        try:
+            cmd = self.task.get_cmd(ignoreErrors = ignoreErrors,
+                                   ignoreRequired = ignoreRequired)
+        except ValueError, err:
+            dlg = wx.MessageDialog(parent = self,
+                                   message = unicode(err),
+                                   caption = _("Error in %s") % self.task.name,
+                                   style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            dlg.ShowModal()
+            dlg.Destroy()
+            cmd = None
+        
+        return cmd
+    
+    def OnSize(self, event):
+        width = event.GetSize()[0]
+        fontsize = self.GetFont().GetPointSize()
+        text_width = max(width / (fontsize - 3), 70)
+        
+        for id in self.label_id:
+            win = self.FindWindowById(id)
+            label = win.GetLabel()
+            label_new = '\n'.join(textwrap.wrap(label, text_width))
+            win.SetLabel(label_new)
+            
+        event.Skip()
+        
+class GUI:
+    def __init__(self, parent = None, show = True, modal = False,
+                 centreOnParent = False, checkError = False):
+        """!Parses GRASS commands when module is imported and used from
+        Layer Manager.
+        """
+        self.parent = parent
+        self.show   = show
+        self.modal  = modal
+        self.centreOnParent = centreOnParent
+        self.checkError     = checkError
+        
+        self.grass_task = None
+        self.cmd = list()
+        
+        global _blackList
+        if self.parent:
+            _blackList['enabled'] = True
+        else:
+            _blackList['enabled'] = False
+        
+    def GetCmd(self):
+        """!Get validated command"""
+        return self.cmd
+    
+    def ParseCommand(self, cmd, gmpath = None, completed = None):
+        """!Parse command
+        
+        Note: cmd is given as list
+        
+        If command is given with options, return validated cmd list:
+         - add key name for first parameter if not given
+         - change mapname to mapname at mapset
+        """
+        start = time.time()
+        dcmd_params = {}
+        if completed == None:
+            get_dcmd = None
+            layer = None
+            dcmd_params = None
+        else:
+            get_dcmd = completed[0]
+            layer = completed[1]
+            if completed[2]:
+                dcmd_params.update(completed[2])
+        
+        # parse the interface decription
+        try:
+            global _blackList
+            self.grass_task = gtask.parse_interface(gcmd.GetRealCmd(cmd[0]),
+                                                    blackList = _blackList)
+        except (grass.ScriptError, ValueError), e:
+            raise gcmd.GException(e)
+        
+        # if layer parameters previously set, re-insert them into dialog
+        if completed is not None:
+            if 'params' in dcmd_params:
+                self.grass_task.params = dcmd_params['params']
+            if 'flags' in dcmd_params:
+                self.grass_task.flags = dcmd_params['flags']
+        
+        err = list()
+        # update parameters if needed && validate command
+        if len(cmd) > 1:
+            i = 0
+            cmd_validated = [cmd[0]]
+            for option in cmd[1:]:
+                if option[0] ==  '-': # flag
+                    if option[1] ==  '-':
+                        self.grass_task.set_flag(option[2:], True)
+                    else:
+                        self.grass_task.set_flag(option[1], True)
+                    cmd_validated.append(option)
+                else: # parameter
+                    try:
+                        key, value = option.split('=', 1)
+                    except:
+                        params = self.grass_task.get_options()['params']
+                        if params:
+                            if i == 0: # add key name of first parameter if not given
+                                key = params[0]['name']
+                                value = option
+                            else:
+                                raise gcmd.GException, _("Unable to parse command '%s'") % ' '.join(cmd)
+                        else:
+                            continue
+                    
+                    element = self.grass_task.get_param(key, raiseError = False)
+                    if not element:
+                        err.append(_("%(cmd)s: parameter '%(key)s' not available") % \
+                                       { 'cmd' : cmd[0],
+                                         'key' : key })
+                        continue
+                    element = element['element']
+                    
+                    if element in ['cell', 'vector']:
+                        # mapname -> mapname at mapset
+                        if '@' not in value:
+                            mapset = grass.find_file(value, element)['mapset']
+                            curr_mapset = grass.gisenv()['MAPSET']
+                            if mapset and mapset !=  curr_mapset:
+                                value = value + '@' + mapset
+                    
+                    self.grass_task.set_param(key, value)
+                    cmd_validated.append(key + '=' + value)
+                    i += 1
+            
+            # update original command list
+            cmd = cmd_validated
+        
+        if self.show is not None:
+            self.mf = TaskFrame(parent = self.parent, ID = wx.ID_ANY,
+                                task_description = self.grass_task,
+                                get_dcmd = get_dcmd, layer = layer)
+        else:
+            self.mf = None
+        
+        if get_dcmd is not None:
+            # update only propwin reference
+            get_dcmd(dcmd = None, layer = layer, params = None,
+                     propwin = self.mf)
+        
+        if self.show is not None:
+            self.mf.notebookpanel.OnUpdateSelection(None)
+            if self.show is True:
+                if self.parent and self.centreOnParent:
+                    self.mf.CentreOnParent()
+                else:
+                    self.mf.CenterOnScreen()
+                self.mf.Show(self.show)
+                self.mf.MakeModal(self.modal)
+            else:
+                self.mf.OnApply(None)
+        
+        self.cmd = cmd
+        
+        if self.checkError:
+            return self.grass_task, err
+        else:
+            return self.grass_task
+    
+    def GetCommandInputMapParamKey(self, cmd):
+        """!Get parameter key for input raster/vector map
+        
+        @param cmd module name
+        
+        @return parameter key
+        @return None on failure
+        """
+        # parse the interface decription
+        if not self.grass_task:
+            enc = locale.getdefaultlocale()[1]
+            if enc and enc.lower() == "cp932":
+                p = re.compile('encoding="' + enc + '"', re.IGNORECASE)
+                tree = etree.fromstring(p.sub('encoding="utf-8"',
+                                              gtask.get_interface_description(cmd).decode(enc).encode('utf-8')))
+            else:
+                tree = etree.fromstring(gtask.get_interface_description(cmd))
+            self.grass_task = gtask.processTask(tree).get_task()
+            
+            for p in self.grass_task.params:
+                if p.get('name', '') in ('input', 'map'):
+                    age = p.get('age', '')
+                    prompt = p.get('prompt', '')
+                    element = p.get('element', '') 
+                    if age ==  'old' and \
+                            element in ('cell', 'grid3', 'vector') and \
+                            prompt in ('raster', '3d-raster', 'vector'):
+                        return p.get('name', None)
+        return None
+
+class GrassGUIApp(wx.App):
+    """!Stand-alone GRASS command GUI
+    """
+    def __init__(self, grass_task):
+        self.grass_task = grass_task
+        wx.App.__init__(self, False)
+        
+    def OnInit(self):
+        msg = self.grass_task.get_error_msg()
+        if msg:
+            gcmd.GError(msg + '\n\nTry to set up GRASS_ADDON_PATH variable.')
+            return True
+        
+        self.mf = TaskFrame(parent = None, ID = wx.ID_ANY, task_description = self.grass_task)
+        self.mf.CentreOnScreen()
+        self.mf.Show(True)
+        self.SetTopWindow(self.mf)
+        
+        return True
+
+if __name__ ==  "__main__":
+    import gettext
+    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+    
+    if len(sys.argv) ==  1:
+        sys.exit(_("usage: %s <grass command>") % sys.argv[0])
+    
+    if sys.argv[1] !=  'test':
+        q = wx.LogNull()
+        cmd = utils.split(sys.argv[1])
+        task = gtask.grassTask(gcmd.GetRealCmd(cmd[0]))
+        task.set_options(cmd[1:])
+        app = GrassGUIApp(task)
+        app.MainLoop()
+    else: #Test
+        # Test grassTask from within a GRASS session
+        if os.getenv("GISBASE") is not None:
+            task = gtask.grassTask("d.vect")
+            task.get_param('map')['value'] = "map_name"
+            task.get_flag('v')['value'] = True
+            task.get_param('layer')['value'] = 1
+            task.get_param('bcolor')['value'] = "red"
+            assert ' '.join(task.get_cmd()) ==  "d.vect -v map = map_name layer = 1 bcolor = red"
+        # Test interface building with handmade grassTask,
+        # possibly outside of a GRASS session.
+        task = gtask.grassTask()
+        task.name = "TestTask"
+        task.description = "This is an artificial grassTask() object intended for testing purposes."
+        task.keywords = ["grass","test","task"]
+        task.params = [
+            {
+            "name" : "text",
+            "description" : "Descriptions go into tooltips if labels are present, like this one",
+            "label" : "Enter some text",
+            },{
+            "name" : "hidden_text",
+            "description" : "This text should not appear in the form",
+            "hidden" : True
+            },{
+            "name" : "text_default",
+            "description" : "Enter text to override the default",
+            "default" : "default text"
+            },{
+            "name" : "text_prefilled",
+            "description" : "You should see a friendly welcome message here",
+            "value" : "hello, world"
+            },{
+            "name" : "plain_color",
+            "description" : "This is a plain color, and it is a compulsory parameter",
+            "required" : False,
+            "gisprompt" : True,
+            "prompt" : "color"
+            },{
+            "name" : "transparent_color",
+            "description" : "This color becomes transparent when set to none",
+            "guisection" : "tab",
+            "gisprompt" : True,
+            "prompt" : "color"
+            },{
+            "name" : "multi",
+            "description" : "A multiple selection",
+            'default': u'red,green,blue',
+            'gisprompt': False,
+            'guisection': 'tab',
+            'multiple': u'yes',
+            'type': u'string',
+            'value': '',
+            'values': ['red', 'green', u'yellow', u'blue', u'purple', u'other']
+            },{
+            "name" : "single",
+            "description" : "A single multiple-choice selection",
+            'values': ['red', 'green', u'yellow', u'blue', u'purple', u'other'],
+            "guisection" : "tab"
+            },{
+            "name" : "large_multi",
+            "description" : "A large multiple selection",
+            "gisprompt" : False,
+            "multiple" : "yes",
+            # values must be an array of strings
+            "values" : str2rgb.keys() + map(str, str2rgb.values())
+            },{
+            "name" : "a_file",
+            "description" : "A file selector",
+            "gisprompt" : True,
+            "element" : "file"
+            }
+            ]
+        task.flags = [
+            {
+            "name" : "a",
+            "description" : "Some flag, will appear in Main since it is required",
+            "required" : True
+            },{
+            "name" : "b",
+            "description" : "pre-filled flag, will appear in options since it is not required",
+            "value" : True
+            },{
+            "name" : "hidden_flag",
+            "description" : "hidden flag, should not be changeable",
+            "hidden" : "yes",
+            "value" : True
+            }
+            ]
+        q = wx.LogNull()
+        GrassGUIApp(task).MainLoop()
+


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_core/ghelp.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_core/ghelp.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_core/ghelp.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,908 @@
+"""!
+ at package gui_core.ghelp
+
+ at brief Help window
+
+Classes:
+ - ghelp::SearchModuleWindow
+ - ghelp::MenuTreeWindow
+ - ghelp::MenuTree
+ - ghelp::AboutWindow
+ - ghelp::HelpFrame
+ - ghelp::HelpWindow
+ - ghelp::HelpPanel
+
+(C) 2008-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+import codecs
+import platform
+
+import wx
+from wx.html import HtmlWindow
+try:
+    import wx.lib.agw.customtreectrl as CT
+    from wx.lib.agw.hyperlink import HyperLinkCtrl
+except ImportError:
+    import wx.lib.customtreectrl as CT
+    from wx.lib.hyperlink import HyperLinkCtrl
+import wx.lib.flatnotebook as FN
+
+import grass.script as grass
+
+from core             import globalvar
+from core             import utils
+from lmgr.menudata    import ManagerData
+from core.gcmd        import GError, DecodeString
+from gui_core.widgets import GNotebook, StaticWrapText, ItemTree, ScrolledPanel
+
+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 = 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 text in ','.join(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 group not in modules:
+                    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('')
+        if self.showTip:
+            self.searchTip.SetLabel('')
+        
+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 = 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 'command' not in data:
+            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 'command' not in data:
+            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 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):
+    """!Create custom About Window
+
+    @todo improve styling
+    """
+    def __init__(self, parent, size = (750, 450), 
+                 title = _('About GRASS GIS'), **kwargs):
+        wx.Frame.__init__(self, parent = parent, id = wx.ID_ANY, title = title, size = size, **kwargs)
+        
+        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
+        vInfo = grass.version()
+        
+        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-64x64.png")
+        logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY,
+                                     bitmap = wx.Bitmap(name = logo,
+                                                        type = wx.BITMAP_TYPE_PNG))
+        infoSizer.Add(item = logoBitmap, proportion = 0,
+                      flag = wx.ALL | wx.ALIGN_CENTER, border = 25)
+        
+        info = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                             label = 'GRASS GIS ' + vInfo['version'] + '\n\n')
+        info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
+        info.SetForegroundColour(wx.Colour(35, 142, 35))
+        infoSizer.Add(item = info, proportion = 0,
+                      flag = wx.BOTTOM | wx.ALIGN_CENTER, border = 15)
+        
+        row = 0
+        infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                                               label = _('Official GRASS site:')),
+                          pos = (row, 0),
+                          flag = wx.ALIGN_RIGHT)
+
+        infoGridSizer.Add(item = HyperLinkCtrl(parent = infoTxt, id = wx.ID_ANY,
+                                               label = 'http://grass.osgeo.org'),
+                          pos = (row, 1),
+                          flag = wx.ALIGN_LEFT)
+
+        row += 2
+        infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                                               label = _('SVN Revision:')),
+                          pos = (row, 0),
+                          flag = wx.ALIGN_RIGHT)
+        
+        infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                                               label = vInfo['revision']),
+                          pos = (row, 1),
+                          flag = wx.ALIGN_LEFT)
+        
+        row += 1
+        infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                                               label = _('GIS Library Revision:')),
+                          pos = (row, 0),
+                          flag = wx.ALIGN_RIGHT)
+        
+        infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                                               label = vInfo['libgis_revision'] + ' (' +
+                                               vInfo['libgis_date'].split(' ')[0] + ')'),
+                          pos = (row, 1),
+                          flag = wx.ALIGN_LEFT)
+
+        row += 2
+        infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                                               label = _('Python:')),
+                          pos = (row, 0),
+                          flag = wx.ALIGN_RIGHT)
+        
+        infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                                               label = platform.python_version()),
+                          pos = (row, 1),
+                          flag = wx.ALIGN_LEFT)
+
+        row += 1
+        infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                                               label = _('wxPython:')),
+                          pos = (row, 0),
+                          flag = wx.ALIGN_RIGHT)
+        
+        infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
+                                               label = wx.__version__),
+                          pos = (row, 1),
+                          flag = wx.ALIGN_LEFT)
+        
+        infoSizer.Add(item = infoGridSizer,
+                      proportion = 1,
+                      flag = wx.EXPAND | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL)
+        
+        # create a flat notebook for displaying information about GRASS
+        aboutNotebook = GNotebook(panel, style = globalvar.FNPageStyle | FN.FNB_NO_X_BUTTON) 
+        aboutNotebook.SetTabAreaColour(globalvar.FNPageColor)
+        
+        for title, win in ((_("Info"), infoTxt),
+                           (_("Copyright"), self._pageCopyright()),
+                           (_("License"), self._pageLicense()),
+                           (_("Authors"), self._pageCredit()),
+                           (_("Contributors"), self._pageContributors()),
+                           (_("Extra contributors"), self._pageContributors(extra = True)),
+                           (_("Translators"), self._pageTranslators())):
+            aboutNotebook.AddPage(page = win, text = title)
+        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
+        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()
+        self.SetMinSize((500, 400))
+        
+    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 = ScrolledPanel(self)
+                                     
+        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 = ScrolledPanel(self)
+        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 = ScrolledPanel(self)
+        authortxt = wx.StaticText(authorwin, id = wx.ID_ANY, label = authors)
+        authorwin.SetAutoLayout(True)
+        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, extra = False):
+        """Contributors info"""
+        if extra:
+            contribfile = os.path.join(os.getenv("GISBASE"), "contributors_extra.csv")
+        else:
+            contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv")
+        if os.path.exists(contribfile):
+            contribFile = codecs.open(contribfile, encoding = 'utf-8', mode = 'r')
+            contribs = list()
+            errLines = list()
+            for line in contribFile.readlines()[1:]:
+                line = line.rstrip('\n')
+                try:
+                    if extra:
+                        name, email, rfc2_agreed = line.split(',')
+                    else:
+                        cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',')
+                except ValueError:
+                    errLines.append(line)
+                    continue
+                if extra:
+                    contribs.append((name, email))
+                else:
+                    contribs.append((name, email, country, osgeo_id))
+            
+            contribFile.close()
+            
+            if errLines:
+                GError(parent = self,
+                       message = _("Error when reading file '%s'.") % contribfile + \
+                           "\n\n" + _("Lines:") + " %s" % \
+                           os.linesep.join(map(DecodeString, errLines)))
+        else:
+            contribs = None
+        
+        contribwin = ScrolledPanel(self)
+        contribwin.SetAutoLayout(True)
+        contribwin.SetupScrolling()
+        contribwin.sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        if not contribs:
+            contribtxt = wx.StaticText(contribwin, id = wx.ID_ANY,
+                                       label = _('%s file missing') % contribfile)
+            contribwin.sizer.Add(item = contribtxt, proportion = 1,
+                                 flag = wx.EXPAND | wx.ALL, border = 3)
+        else:
+            if extra:
+                items = (_('Name'), _('E-mail'))
+            else:
+                items = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID'))
+            contribBox = wx.FlexGridSizer(cols = len(items), vgap = 5, hgap = 5)
+            for item in items:
+                contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
+                                                    label = item))
+            for vals in sorted(contribs, key = lambda x: x[0]):
+                for item in vals:
+                    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()
+            errLines = list()
+            for line in translatorsFile.readlines()[1:]:
+                line = line.rstrip('\n')
+                try:
+                    name, email, languages = line.split(',')
+                except ValueError:
+                    errLines.append(line)
+                    continue
+                for language in languages.split(' '):
+                    if language not in translators:
+                        translators[language] = list()
+                    translators[language].append((name, email))
+            translatorsFile.close()
+            
+            if errLines:
+                GError(parent = self,
+                       message = _("Error when reading file '%s'.") % translatorsfile + \
+                           "\n\n" + _("Lines:") + " %s" % \
+                           os.linesep.join(map(DecodeString, errLines)))
+        else:
+            translators = None
+        
+        translatorswin = ScrolledPanel(self)
+        translatorswin.SetAutoLayout(True)
+        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 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 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_core/ghelp.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_core/goutput.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_core/goutput.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_core/goutput.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1151 @@
+"""!
+ at package gui_core.goutput
+
+ at brief Command output widgets
+
+Classes:
+ - goutput::CmdThread
+ - goutput::GMConsole
+ - goutput::GMStdout
+ - goutput::GMStderr
+ - goutput::GMStc
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton (Arizona State University)
+ at author Martin Landa <landa.martin gmail.com>
+ at author Vaclav Petras <wenzeslaus gmail.com> (copy&paste customization)
+"""
+
+import os
+import sys
+import textwrap
+import time
+import threading
+import Queue
+import codecs
+import locale
+
+import wx
+import wx.stc
+from wx.lib.newevent import NewEvent
+
+import grass.script as grass
+from   grass.script import task as gtask
+
+from core            import globalvar
+from core            import utils
+from core.gcmd       import CommandThread, GMessage, GError, GException, EncodeString
+from gui_core.forms  import GUI
+from gui_core.prompt import GPromptSTC
+from core.debug      import Debug
+from core.settings   import UserSettings, Settings
+from gui_core.ghelp  import SearchModuleWindow
+
+wxCmdOutput,   EVT_CMD_OUTPUT   = NewEvent()
+wxCmdProgress, EVT_CMD_PROGRESS = NewEvent()
+wxCmdRun,      EVT_CMD_RUN      = NewEvent()
+wxCmdDone,     EVT_CMD_DONE     = NewEvent()
+wxCmdAbort,    EVT_CMD_ABORT    = NewEvent()
+wxCmdPrepare,  EVT_CMD_PREPARE  = NewEvent()
+
+def GrassCmd(cmd, stdout = None, stderr = None):
+    """!Return GRASS command thread"""
+    return CommandThread(cmd,
+                         stdout = stdout, stderr = stderr)
+
+class CmdThread(threading.Thread):
+    """!Thread for GRASS commands"""
+    requestId = 0
+    def __init__(self, parent, requestQ, resultQ, **kwds):
+        threading.Thread.__init__(self, **kwds)
+
+        self.setDaemon(True)
+
+        self.parent = parent # GMConsole
+        self._want_abort_all = False
+        
+        self.requestQ = requestQ
+        self.resultQ = resultQ
+
+        self.start()
+
+    def RunCmd(self, *args, **kwds):
+        CmdThread.requestId += 1
+
+        self.requestCmd = None
+        self.requestQ.put((CmdThread.requestId, args, kwds))
+        
+        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, args, kwds = self.requestQ.get()
+            for key in ('callable', 'onDone', 'onPrepare', 'userData'):
+                if key in kwds:
+                    vars()[key] = kwds[key]
+                    del kwds[key]
+                else:
+                    vars()[key] = None
+            
+            if not vars()['callable']:
+                vars()['callable'] = GrassCmd
+            
+            requestTime = time.time()
+            
+            # prepare
+            event = wxCmdPrepare(cmd = args[0],
+                                 time = requestTime,
+                                 pid = requestId,
+                                 onPrepare = vars()['onPrepare'],
+                                 userData = vars()['userData'])
+            wx.PostEvent(self.parent, event)
+            
+            # run command
+            event = wxCmdRun(cmd = args[0],
+                             pid = requestId)
+            wx.PostEvent(self.parent, event)
+            
+            time.sleep(.1)
+            self.requestCmd = vars()['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:
+                returncode = self.requestCmd.module.returncode
+            except AttributeError:
+                returncode = 0 # being optimistic
+            
+            try:
+                aborted = self.requestCmd.aborted
+            except AttributeError:
+                aborted = False
+            
+            time.sleep(.1)
+
+            # set default color table for raster data
+            if UserSettings.Get(group = 'cmd', key = 'rasterColorTable', subkey = 'enabled') and \
+                    args[0][0][:2] == 'r.':
+                colorTable = UserSettings.Get(group = 'cmd', key = 'rasterColorTable', subkey = 'selection')
+                mapName = None
+                if args[0][0] == 'r.mapcalc':
+                    try:
+                        mapName = args[0][1].split('=', 1)[0].strip()
+                    except KeyError:
+                        pass
+                else:
+                    moduleInterface = GUI(show = None).ParseCommand(args[0])
+                    outputParam = moduleInterface.get_param(value = 'output', raiseError = False)
+                    if outputParam and outputParam['prompt'] == 'raster':
+                        mapName = outputParam['value']
+                
+                if mapName:
+                    argsColor = list(args)
+                    argsColor[0] = [ 'r.colors',
+                                     'map=%s' % mapName,
+                                     'color=%s' % colorTable ]
+                    self.requestCmdColor = vars()['callable'](*argsColor, **kwds)
+                    self.resultQ.put((requestId, self.requestCmdColor.run()))
+            
+            event = wxCmdDone(cmd = args[0],
+                              aborted = aborted,
+                              returncode = returncode,
+                              time = requestTime,
+                              pid = requestId,
+                              onDone = vars()['onDone'],
+                              userData = vars()['userData'])
+            
+            # send event
+            wx.PostEvent(self.parent, event)
+            
+    def abort(self, abortall = True):
+        """!Abort command(s)"""
+        if abortall:
+            self._want_abort_all = True
+        self.requestCmd.abort()
+        if self.requestQ.empty():
+            self._want_abort_all = False
+        
+class GMConsole(wx.SplitterWindow):
+    """!Create and manage output console for commands run by GUI.
+    """
+    def __init__(self, parent, id = wx.ID_ANY, margin = False,
+                 notebook = None,
+                 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.parent          = parent # GMFrame | CmdPanel | ?
+        if notebook:
+            self._notebook = notebook
+        else:
+            self._notebook = self.parent.notebook
+        self.lineWidth       = 80
+        
+        # remember position of line begining (used for '\r')
+        self.linePos         = -1
+        
+        # create queues
+        self.requestQ = Queue.Queue()
+        self.resultQ = Queue.Queue()
+        
+        # progress bar
+        self.progressbar = wx.Gauge(parent = self.panelOutput, id = wx.ID_ANY,
+                                    range = 100, pos = (110, 50), size = (-1, 25),
+                                    style = wx.GA_HORIZONTAL)
+        self.progressbar.Bind(EVT_CMD_PROGRESS, self.OnCmdProgress)
+        
+        # text control for command output
+        self.cmdOutput = GMStc(parent = self.panelOutput, id = wx.ID_ANY, margin = margin,
+                               wrap = None) 
+        self.cmdOutputTimer = wx.Timer(self.cmdOutput, id = wx.ID_ANY)
+        self.cmdOutput.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput)
+        self.cmdOutput.Bind(wx.EVT_TIMER, self.OnProcessPendingOutputWindowEvents)
+        self.Bind(EVT_CMD_RUN,     self.OnCmdRun)
+        self.Bind(EVT_CMD_DONE,    self.OnCmdDone)
+        self.Bind(EVT_CMD_PREPARE, self.OnCmdPrepare)
+        
+        # search & command prompt
+        self.cmdPrompt = GPromptSTC(parent = self)
+        
+        if self.parent.GetName() != 'LayerManager':
+            self.search = None
+            self.cmdPrompt.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.cmdStdOut = GMStdout(self)
+        self.cmdStdErr = GMStderr(self)
+        
+        # thread
+        self.cmdThread = CmdThread(self, self.requestQ, self.resultQ)
+        
+        self.outputBox = wx.StaticBox(parent = self.panelOutput, id = wx.ID_ANY,
+                                      label = " %s " % _("Output window"))
+        self.cmdBox = wx.StaticBox(parent = self.panelOutput, id = wx.ID_ANY,
+                                   label = " %s " % _("Command prompt"))
+        
+        # buttons
+        self.btnOutputClear = wx.Button(parent = self.panelOutput, id = wx.ID_CLEAR)
+        self.btnOutputClear.SetToolTipString(_("Clear output window content"))
+        self.btnCmdClear = wx.Button(parent = self.panelOutput, id = wx.ID_CLEAR)
+        self.btnCmdClear.SetToolTipString(_("Clear command prompt content"))
+        if self.parent.GetName() != 'LayerManager':
+            self.btnCmdClear.Hide()
+        self.btnOutputSave  = wx.Button(parent = self.panelOutput, id = wx.ID_SAVE)
+        self.btnOutputSave.SetToolTipString(_("Save output window content to the file"))
+        # abort
+        self.btnCmdAbort = wx.Button(parent = self.panelOutput, id = wx.ID_STOP)
+        self.btnCmdAbort.SetToolTipString(_("Abort running command"))
+        self.btnCmdAbort.Enable(False)
+        
+        self.btnCmdClear.Bind(wx.EVT_BUTTON,     self.cmdPrompt.OnCmdErase)
+        self.btnOutputClear.Bind(wx.EVT_BUTTON,  self.ClearHistory)
+        self.btnOutputSave.Bind(wx.EVT_BUTTON,   self.SaveHistory)
+        self.btnCmdAbort.Bind(wx.EVT_BUTTON,     self.OnCmdAbort)
+        self.btnCmdAbort.Bind(EVT_CMD_ABORT,     self.OnCmdAbort)
+        
+        self._layout()
+        
+    def _layout(self):
+        """!Do layout"""
+        outputSizer = wx.BoxSizer(wx.VERTICAL)
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        outBtnSizer = wx.StaticBoxSizer(self.outputBox, wx.HORIZONTAL)
+        cmdBtnSizer = wx.StaticBoxSizer(self.cmdBox, wx.HORIZONTAL)
+        
+        if self.cmdPrompt.IsShown():
+            promptSizer = wx.BoxSizer(wx.VERTICAL)
+            promptSizer.Add(item = self.cmdPrompt, proportion = 1,
+                        flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border = 3)
+        
+        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.cmdOutput, proportion = 1,
+                        flag = wx.EXPAND | wx.ALL, border = 3)
+        outputSizer.Add(item = self.progressbar, proportion = 0,
+                        flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 3)
+        outBtnSizer.Add(item = self.btnOutputClear, proportion = 1,
+                        flag = wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT, border = 5)
+        outBtnSizer.Add(item = self.btnOutputSave, proportion = 1,
+                        flag = wx.ALIGN_RIGHT | wx.RIGHT, border = 5)
+        
+        cmdBtnSizer.Add(item = self.btnCmdClear, proportion = 1,
+                        flag = wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT, border = 5)
+        cmdBtnSizer.Add(item = self.btnCmdAbort, proportion = 1,
+                        flag = wx.ALIGN_CENTER | wx.RIGHT, border = 5)
+        
+        btnSizer.Add(item = outBtnSizer, proportion = 1,
+                     flag = wx.ALL | wx.ALIGN_CENTER, border = 5)
+        btnSizer.Add(item = cmdBtnSizer, proportion = 1,
+                     flag = wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM | wx.RIGHT, border = 5)
+        outputSizer.Add(item = btnSizer, proportion = 0,
+                        flag = wx.EXPAND)
+        
+        outputSizer.Fit(self)
+        outputSizer.SetSizeHints(self)
+        self.panelOutput.SetSizer(outputSizer)
+        
+        if self.cmdPrompt.IsShown():
+            promptSizer.Fit(self)
+            promptSizer.SetSizeHints(self)
+            self.panelPrompt.SetSizer(promptSizer)
+        
+        # split window
+        if self.cmdPrompt.IsShown():
+            self.SplitHorizontally(self.panelOutput, self.panelPrompt, -50)
+        else:
+            self.SplitHorizontally(self.panelOutput, self.panelPrompt, -45)
+            self.Unsplit()
+        self.SetMinimumPaneSize(self.btnCmdClear.GetSize()[1] + 25)
+        
+        self.SetSashGravity(1.0)
+        
+        # layout
+        self.SetAutoLayout(True)
+        self.Layout()
+
+    def MakeSearchPaneContent(self, pane):
+        """!Create search pane"""
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        self.search = SearchModuleWindow(parent = pane, cmdPrompt = self.cmdPrompt)
+        
+        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
+
+        @param prompt get prompt / output panel
+
+        @return wx.Panel reference
+        """
+        if prompt:
+            return self.panelPrompt
+
+        return self.panelOutput
+    
+    def Redirect(self):
+        """!Redirect stdout/stderr
+        """
+        if Debug.GetLevel() == 0 and int(grass.gisenv().get('DEBUG', 0)) == 0:
+            # don't redirect when debugging is enabled
+            sys.stdout = self.cmdStdOut
+            sys.stderr = self.cmdStdErr
+        else:
+            enc = locale.getdefaultlocale()[1]
+            if enc:
+                sys.stdout = codecs.getwriter(enc)(sys.__stdout__)
+                sys.stderr = codecs.getwriter(enc)(sys.__stderr__)
+            else:
+                sys.stdout = sys.__stdout__
+                sys.stderr = sys.__stderr__
+        
+    def WriteLog(self, text, style = None, wrap = None,
+                 switchPage = False):
+        """!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.cmdOutput.SetStyle()
+
+        if switchPage:
+            self._notebook.SetSelectionByName('output')
+        
+        if not style:
+            style = self.cmdOutput.StyleDefault
+        
+        # p1 = self.cmdOutput.GetCurrentPos()
+        p1 = self.cmdOutput.GetEndStyled()
+        # self.cmdOutput.GotoPos(p1)
+        self.cmdOutput.DocumentEnd()
+        
+        for line in text.splitlines():
+            # fill space
+            if len(line) < self.lineWidth:
+                diff = self.lineWidth - len(line) 
+                line += diff * ' '
+            
+            self.cmdOutput.AddTextWrapped(line, wrap = wrap) # adds '\n'
+            
+            p2 = self.cmdOutput.GetCurrentPos()
+            
+            self.cmdOutput.StartStyling(p1, 0xff)
+            self.cmdOutput.SetStyling(p2 - p1, style)
+        
+        self.cmdOutput.EnsureCaretVisible()
+        
+    def WriteCmdLog(self, line, pid = None, switchPage = True):
+        """!Write message in selected style"""
+        if pid:
+            line = '(' + str(pid) + ') ' + line
+        
+        self.WriteLog(line, style = self.cmdOutput.StyleCommand, switchPage = switchPage)
+        
+    def WriteWarning(self, line):
+        """!Write message in warning style"""
+        self.WriteLog(line, style = self.cmdOutput.StyleWarning, switchPage = True)
+
+    def WriteError(self, line):
+        """!Write message in error style"""
+        self.WriteLog(line, style = self.cmdOutput.StyleError, switchPage = True)
+
+    def RunCmd(self, command, compReg = True, switchPage = False,
+               onDone = None, onPrepare = None, userData = None):
+        """!Run command typed into console command prompt (GPrompt).
+        
+        @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 given as a list (produced e.g. by utils.split())
+        @param compReg True use computation region
+        @param switchPage switch to output page
+        @param onDone function to be called when command is finished
+        @param onPrepare function to be called before command is launched
+        @param userData data defined for the command
+        """
+        if len(command) == 0:
+            Debug.msg(2, "GPrompt:RunCmd(): empty command")
+            return
+        
+        # update history file
+        env = grass.gisenv()
+        try:
+            fileHistory = codecs.open(os.path.join(env['GISDBASE'],
+                                                   env['LOCATION_NAME'],
+                                                   env['MAPSET'],
+                                                   '.bash_history'),
+                                      encoding = 'utf-8', mode = 'a')
+        except IOError, e:
+            self.WriteError(e)
+            fileHistory = None
+        
+        if fileHistory:
+            try:
+                fileHistory.write(' '.join(command) + os.linesep)
+            finally:
+                fileHistory.close()
+        
+        if command[0] in globalvar.grassCmd:
+            # send GRASS command without arguments to GUI command interface
+            # except display commands (they are handled differently)
+            if self.parent.GetName() == "LayerManager" and \
+                    command[0][0:2] == "d." and \
+                    'help' not in ' '.join(command[1:]):
+                # display GRASS commands
+                try:
+                    layertype = {'d.rast'         : 'raster',
+                                 'd.rast3d'       : '3d-raster',
+                                 'd.rgb'          : 'rgb',
+                                 'd.his'          : 'his',
+                                 'd.shaded'       : 'shaded',
+                                 'd.legend'       : 'rastleg',
+                                 'd.rast.arrow'   : 'rastarrow',
+                                 'd.rast.num'     : 'rastnum',
+                                 'd.vect'         : 'vector',
+                                 'd.vect.thematic': 'thememap',
+                                 'd.vect.chart'   : 'themechart',
+                                 'd.grid'         : 'grid',
+                                 'd.geodesic'     : 'geodesic',
+                                 'd.rhumbline'    : 'rhumb',
+                                 'd.labels'       : 'labels',
+                                 'd.barscale'     : 'barscale',
+                                 'd.redraw'       : 'redraw'}[command[0]]
+                except KeyError:
+                    GMessage(parent = self.parent,
+                             message = _("Command '%s' not yet implemented in the WxGUI. "
+                                         "Try adding it as a command layer instead.") % command[0])
+                    return
+                
+                if layertype == 'barscale':
+                    self.parent.curr_page.maptree.GetMapDisplay().OnAddBarscale(None)
+                elif layertype == 'rastleg':
+                    self.parent.curr_page.maptree.GetMapDisplay().OnAddLegend(None)
+                elif layertype == 'redraw':
+                    self.parent.curr_page.maptree.GetMapDisplay().OnRender(None)
+                else:
+                    # add layer into layer tree
+                    lname, found = utils.GetLayerNameFromCmd(command, fullyQualified = True,
+                                                             layerType = layertype)
+                    if self.parent.GetName() == "LayerManager":
+                        self.parent.curr_page.maptree.AddLayer(ltype = layertype,
+                                                               lname = lname,
+                                                               lcmd = command)
+            
+            else:
+                # other GRASS commands (r|v|g|...)
+                hasParams = False
+                if command[0] != 'r.mapcalc':
+                    try:
+                        task = GUI(show = None).ParseCommand(command)
+                    except GException, e:
+                        GError(parent = self,
+                               message = unicode(e),
+                               showTraceback = False)
+                        return
+                    
+                    if task:
+                        options = task.get_options()
+                        hasParams = options['params'] and options['flags']
+                        # check for <input>=-
+                        for p in options['params']:
+                            if p.get('prompt', '') == 'input' and \
+                                    p.get('element', '') == 'file' and \
+                                    p.get('age', 'new') == 'old' and \
+                                    p.get('value', '') == '-':
+                                GError(parent = self,
+                                       message = _("Unable to run command:\n%(cmd)s\n\n"
+                                                   "Option <%(opt)s>: read from standard input is not "
+                                                   "supported by wxGUI") % { 'cmd': ' '.join(command),
+                                                                             'opt': p.get('name', '') })
+                                return
+                else:
+                    task = None
+                
+                if len(command) == 1 and hasParams and \
+                        command[0] != 'v.krige.py':
+                    # no arguments given
+                    try:
+                        GUI(parent = self).ParseCommand(command)
+                    except GException, e:
+                        print >> sys.stderr, e
+                    return
+                
+                # switch to 'Command output' if required
+                if switchPage:
+                    self._notebook.SetSelectionByName('output')
+                    
+                    self.parent.SetFocus()
+                    self.parent.Raise()
+                
+                # activate computational region (set with g.region)
+                # for all non-display commands.
+                if compReg:
+                    tmpreg = os.getenv("GRASS_REGION")
+                    if "GRASS_REGION" in os.environ:
+                        del os.environ["GRASS_REGION"]
+                
+                # process GRASS command with argument
+                self.cmdThread.RunCmd(command, stdout = self.cmdStdOut, stderr = self.cmdStdErr,
+                                      onDone = onDone, onPrepare = onPrepare, userData = userData)
+                self.cmdOutputTimer.Start(50)
+                
+                # deactivate computational region and return to display settings
+                if compReg and tmpreg:
+                    os.environ["GRASS_REGION"] = tmpreg
+        else:
+            # Send any other command to the shell. Send output to
+            # console output window
+            if len(command) == 1:
+                try:
+                    task = gtask.parse_interface(command[0])
+                except:
+                    task = None
+            else:
+                task = None
+                
+            if task:
+                # process GRASS command without argument
+                GUI(parent = self).ParseCommand(command)
+            else:
+                self.cmdThread.RunCmd(command, stdout = self.cmdStdOut, stderr = self.cmdStdErr,
+                                      onDone = onDone, onPrepare = onPrepare, userData = userData)
+            self.cmdOutputTimer.Start(50)
+        
+    def ClearHistory(self, event):
+        """!Clear history of commands"""
+        self.cmdOutput.SetReadOnly(False)
+        self.cmdOutput.ClearAll()
+        self.cmdOutput.SetReadOnly(True)
+        self.progressbar.SetValue(0)
+
+    def GetProgressBar(self):
+        """!Return progress bar widget"""
+        return self.progressbar
+    
+    def GetLog(self, err = False):
+        """!Get widget used for logging
+
+        @param err True to get stderr widget
+        """
+        if err:
+            return self.cmdStdErr
+        
+        return self.cmdStdOut
+    
+    def SaveHistory(self, event):
+        """!Save history of commands"""
+        self.history = self.cmdOutput.GetSelectedText()
+        if self.history == '':
+            self.history = self.cmdOutput.GetText()
+
+        # add newline if needed
+        if len(self.history) > 0 and self.history[-1] != '\n':
+            self.history += '\n'
+
+        wildcard = "Text file (*.txt)|*.txt"
+        dlg = wx.FileDialog(self, message = _("Save file as..."), defaultDir = os.getcwd(),
+                            defaultFile = "grass_cmd_history.txt", wildcard = wildcard,
+                            style = wx.SAVE | wx.FD_OVERWRITE_PROMPT)
+
+        # Show the dialog and retrieve the user response. If it is the OK response,
+        # process the data.
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+
+            output = open(path, "w")
+            output.write(self.history)
+            output.close()
+
+        dlg.Destroy()
+
+    def GetCmd(self):
+        """!Get running command or None"""
+        return self.requestQ.get()
+    
+    def SetCopyingOfSelectedText(self, copy):
+        """!Enable or disable copying of selected text in to clipboard.
+        Effects prompt and output.
+        
+        @param copy True for enable, False for disable
+        """
+        if copy:
+            self.cmdPrompt.Bind(wx.stc.EVT_STC_PAINTED, self.cmdPrompt.OnTextSelectionChanged)
+            self.cmdOutput.Bind(wx.stc.EVT_STC_PAINTED, self.cmdOutput.OnTextSelectionChanged)
+        else:
+            self.cmdPrompt.Unbind(wx.stc.EVT_STC_PAINTED)
+            self.cmdOutput.Unbind(wx.stc.EVT_STC_PAINTED)
+        
+    def OnUpdateStatusBar(self, event):
+        """!Update statusbar text"""
+        if event.GetString():
+            nItems = len(self.cmdPrompt.GetCommandItems())
+            self.parent.SetStatusText(_('%d modules match') % nItems, 0)
+        else:
+            self.parent.SetStatusText('', 0)
+        
+        event.Skip()
+
+    def OnCmdOutput(self, event):
+        """!Print command output"""
+        message = event.text
+        type  = event.type
+        if self._notebook.GetSelection() != self._notebook.GetPageIndexByName('output'):
+            page = self._notebook.GetPageIndexByName('output')
+            textP = self._notebook.GetPageText(page)
+            if textP[-1] != ')':
+                textP += ' (...)'
+            self._notebook.SetPageText(page, textP)
+        
+        # message prefix
+        if type == 'warning':
+            messege = 'WARNING: ' + message
+        elif type == 'error':
+            message = 'ERROR: ' + message
+        
+        p1 = self.cmdOutput.GetEndStyled()
+        self.cmdOutput.GotoPos(p1)
+        
+        if '\b' in message:
+            if self.linepos < 0:
+                self.linepos = p1
+            last_c = ''
+            for c in message:
+                if c == '\b':
+                    self.linepos -= 1
+                else:
+                    if c == '\r':
+                        pos = self.cmdOutput.GetCurLine()[1]
+                        # self.cmdOutput.SetCurrentPos(pos)
+                    else:
+                        self.cmdOutput.SetCurrentPos(self.linepos)
+                    self.cmdOutput.ReplaceSelection(c)
+                    self.linepos = self.cmdOutput.GetCurrentPos()
+                    if c != ' ':
+                        last_c = c
+            if last_c not in ('0123456789'):
+                self.cmdOutput.AddTextWrapped('\n', wrap = None)
+                self.linepos = -1
+        else:
+            self.linepos = -1 # don't force position
+            if '\n' not in message:
+                self.cmdOutput.AddTextWrapped(message, wrap = 60)
+            else:
+                self.cmdOutput.AddTextWrapped(message, wrap = None)
+
+        p2 = self.cmdOutput.GetCurrentPos()
+        
+        if p2 >= p1:
+            self.cmdOutput.StartStyling(p1, 0xff)
+        
+            if type == 'error':
+                self.cmdOutput.SetStyling(p2 - p1, self.cmdOutput.StyleError)
+            elif type == 'warning':
+                self.cmdOutput.SetStyling(p2 - p1, self.cmdOutput.StyleWarning)
+            elif type == 'message':
+                self.cmdOutput.SetStyling(p2 - p1, self.cmdOutput.StyleMessage)
+            else: # unknown
+                self.cmdOutput.SetStyling(p2 - p1, self.cmdOutput.StyleUnknown)
+        
+        self.cmdOutput.EnsureCaretVisible()
+        
+    def OnCmdProgress(self, event):
+        """!Update progress message info"""
+        self.progressbar.SetValue(event.value)
+
+    def OnCmdAbort(self, event):
+        """!Abort running command"""
+        self.cmdThread.abort()
+        
+    def OnCmdRun(self, event):
+        """!Run command"""
+        if self.parent.GetName() == 'Modeler':
+            self.parent.OnCmdRun(event)
+        
+        self.WriteCmdLog('(%s)\n%s' % (str(time.ctime()), ' '.join(event.cmd)))
+        self.btnCmdAbort.Enable()
+
+    def OnCmdPrepare(self, event):
+        """!Prepare for running command"""
+        if self.parent.GetName() == 'Modeler':
+            self.parent.OnCmdPrepare(event)
+        
+        event.Skip()
+        
+    def OnCmdDone(self, event):
+        """!Command done (or aborted)"""
+        if self.parent.GetName() == 'Modeler':
+            self.parent.OnCmdDone(event)
+            
+        # Process results here
+        try:
+            ctime = time.time() - event.time
+            if ctime < 60:
+                stime = _("%d sec") % int(ctime)
+            else:
+                mtime = int(ctime / 60)
+                stime = _("%(min)d min %(sec)d sec") %  { 'min' : mtime, 
+                                                          'sec' : int(ctime - (mtime * 60)) }
+        except KeyError:
+            # stopped deamon
+            stime = _("unknown")
+        
+        if event.aborted:
+            # Thread aborted (using our convention of None return)
+            self.WriteLog(_('Please note that the data are left in inconsistent state '
+                            'and may be corrupted'), self.cmdOutput.StyleWarning)
+            msg = _('Command aborted')
+        else:
+            msg = _('Command finished')
+            
+        self.WriteCmdLog('(%s) %s (%s)' % (str(time.ctime()), msg, stime))
+        self.btnCmdAbort.Enable(False)
+        
+        if event.onDone:
+            event.onDone(cmd = event.cmd, returncode = event.returncode)
+        
+        self.progressbar.SetValue(0) # reset progress bar on '0%'
+
+        self.cmdOutputTimer.Stop()
+
+        if event.cmd[0] == 'g.gisenv':
+            Debug.SetLevel()
+            self.Redirect()
+        
+        if self.parent.GetName() == "LayerManager":
+            self.btnCmdAbort.Enable(False)
+            if event.cmd[0] not in globalvar.grassCmd or \
+                    event.cmd[0] == 'r.mapcalc':
+                return
+            
+            display = self.parent.GetLayerTree().GetMapDisplay()
+            if not display or not display.IsAutoRendered():
+                return
+            mapLayers = map(lambda x: x.GetName(),
+                            display.GetMap().GetListOfLayers(l_type = 'raster') +
+                            display.GetMap().GetListOfLayers(l_type = 'vector'))
+            
+            try:
+                task = GUI(show = None).ParseCommand(event.cmd)
+            except GException, e:
+                print >> sys.stderr, e
+                task = None
+                return
+            
+            for p in task.get_options()['params']:
+                if p.get('prompt', '') not in ('raster', 'vector'):
+                    continue
+                mapName = p.get('value', '')
+                if '@' not in mapName:
+                    mapName = mapName + '@' + grass.gisenv()['MAPSET']
+                if mapName in mapLayers:
+                    display.GetWindow().UpdateMap(render = True)
+                    return
+        elif self.parent.GetName() == 'Modeler':
+            pass
+        else: # standalone dialogs
+            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)
+            
+            if hasattr(self.parent.parent, "btn_run"):
+                dialog.btn_run.Enable(True)
+            
+            if event.returncode == 0 and not event.aborted:
+                try:
+                    winName = self.parent.parent.parent.GetName()
+                except AttributeError:
+                    winName = ''
+                
+                if winName == 'LayerManager':
+                    mapTree = self.parent.parent.parent.GetLayerTree()
+                elif winName == 'LayerTree':
+                    mapTree = self.parent.parent.parent
+                elif winName: # GMConsole
+                    mapTree = self.parent.parent.parent.parent.GetLayerTree()
+                else:
+                    mapTree = None
+                
+                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, found = 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 \
+                    hasattr(dialog, "closebox") and \
+                    dialog.closebox.IsChecked() and \
+                    (event.returncode == 0 or event.aborted):
+                self.cmdOutput.Update()
+                time.sleep(2)
+                dialog.Close()
+        
+    def OnProcessPendingOutputWindowEvents(self, event):
+        self.ProcessPendingEvents()
+
+    def ResetFocus(self):
+        """!Reset focus"""
+        self.cmdPrompt.SetFocus()
+        
+class GMStdout:
+    """!GMConsole standard output
+
+    Based on FrameOutErr.py
+
+    Name:      FrameOutErr.py
+    Purpose:   Redirecting stdout / stderr
+    Author:    Jean-Michel Fauth, Switzerland
+    Copyright: (c) 2005-2007 Jean-Michel Fauth
+    Licence:   GPL
+    """
+    def __init__(self, parent):
+        self.parent = parent # GMConsole
+
+    def write(self, s):
+        if len(s) == 0 or s == '\n':
+            return
+
+        for line in s.splitlines():
+            if len(line) == 0:
+                continue
+            
+            evt = wxCmdOutput(text = line + '\n',
+                              type = '')
+            wx.PostEvent(self.parent.cmdOutput, evt)
+        
+class GMStderr:
+    """!GMConsole standard error output
+
+    Based on FrameOutErr.py
+
+    Name:      FrameOutErr.py
+    Purpose:   Redirecting stdout / stderr
+    Author:    Jean-Michel Fauth, Switzerland
+    Copyright: (c) 2005-2007 Jean-Michel Fauth
+    Licence:   GPL
+    """
+    def __init__(self, parent):
+        self.parent = parent # GMConsole
+        
+        self.type = ''
+        self.message = ''
+        self.printMessage = False
+        
+    def flush(self):
+        pass
+    
+    def write(self, s):
+        if "GtkPizza" in s:
+            return
+        
+        # remove/replace escape sequences '\b' or '\r' from stream
+        progressValue = -1
+        
+        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:
+                    progressValue = value
+                else:
+                    progressValue = 0
+            elif 'GRASS_INFO_MESSAGE' in line:
+                self.type = 'message'
+                self.message += line.split(':', 1)[1].strip() + '\n'
+            elif 'GRASS_INFO_WARNING' in line:
+                self.type = 'warning'
+                self.message += line.split(':', 1)[1].strip() + '\n'
+            elif 'GRASS_INFO_ERROR' in line:
+                self.type = 'error'
+                self.message += line.split(':', 1)[1].strip() + '\n'
+            elif 'GRASS_INFO_END' in line:
+                self.printMessage = True
+            elif self.type == '':
+                if len(line) == 0:
+                    continue
+                evt = wxCmdOutput(text = line,
+                                  type = '')
+                wx.PostEvent(self.parent.cmdOutput, evt)
+            elif len(line) > 0:
+                self.message += line.strip() + '\n'
+
+            if self.printMessage and len(self.message) > 0:
+                evt = wxCmdOutput(text = self.message,
+                                  type = self.type)
+                wx.PostEvent(self.parent.cmdOutput, evt)
+
+                self.type = ''
+                self.message = ''
+                self.printMessage = False
+
+        # update progress message
+        if progressValue > -1:
+            # self.gmgauge.SetValue(progressValue)
+            evt = wxCmdProgress(value = progressValue)
+            wx.PostEvent(self.parent.progressbar, evt)
+            
+class GMStc(wx.stc.StyledTextCtrl):
+    """!Styled GMConsole
+
+    Based on FrameOutErr.py
+
+    Name:      FrameOutErr.py
+    Purpose:   Redirecting stdout / stderr
+    Author:    Jean-Michel Fauth, Switzerland
+    Copyright: (c) 2005-2007 Jean-Michel Fauth
+    Licence:   GPL
+    """    
+    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.SetStyle()
+        
+        #
+        # line margins
+        #
+        # TODO print number only from cmdlog
+        self.SetMarginWidth(1, 0)
+        self.SetMarginWidth(2, 0)
+        if margin:
+            self.SetMarginType(0, wx.stc.STC_MARGIN_NUMBER)
+            self.SetMarginWidth(0, 30)
+        else:
+            self.SetMarginWidth(0, 0)
+
+        #
+        # miscellaneous
+        #
+        self.SetViewWhiteSpace(False)
+        self.SetTabWidth(4)
+        self.SetUseTabs(False)
+        self.UsePopUp(True)
+        self.SetSelBackground(True, "#FFFF00")
+        self.SetUseHorizontalScrollBar(True)
+
+        #
+        # bindings
+        #
+        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
+        
+    def OnTextSelectionChanged(self, event):
+        """!Copy selected text to clipboard and skip event.
+        The same function is in TextCtrlAutoComplete class (prompt.py).
+        """
+        self.Copy()
+        event.Skip()
+        
+    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 = Settings()
+        
+        typeface = settings.Get(group = 'appearance', key = 'outputfont', subkey = 'type')   
+        if typeface == "":
+            typeface = "Courier New"
+        
+        typesize = settings.Get(group = 'appearance', 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 app has exited"""
+        
+        wx.TheClipboard.Flush()
+        evt.Skip()
+
+    def AddTextWrapped(self, txt, wrap = None):
+        """!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:
+            if txt[-1] != '\n':
+                txt += '\n'
+        
+        if '\r' in txt:
+            self.parent.linePos = -1
+            for seg in txt.split('\r'):
+                if self.parent.linePos > -1:
+                    self.SetCurrentPos(self.parent.linePos)
+                    self.ReplaceSelection(seg)
+                else:
+                    self.parent.linePos = self.GetCurrentPos()
+                    self.AddText(seg)
+        else:
+            self.parent.linePos = self.GetCurrentPos()
+            try:
+                self.AddText(txt)
+            except UnicodeDecodeError:
+                enc = UserSettings.Get(group = 'atm', key = 'encoding', subkey = 'value')
+                if enc:
+                    txt = unicode(txt, enc)
+                elif 'GRASS_DB_ENCODING' in os.environ:
+                    txt = unicode(txt, os.environ['GRASS_DB_ENCODING'])
+                else:
+                    txt = EncodeString(txt)
+                
+                self.AddText(txt)
+        
+        # reset output window to read only
+        self.SetReadOnly(True)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_core/gselect.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_core/gselect.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_core/gselect.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1708 @@
+"""!
+ at package gui_core.gselect
+
+ at brief Custom control that selects elements
+
+Classes:
+ - gselect::Select
+ - gselect::VectorSelect
+ - gselect::TreeCrtlComboPopup
+ - gselect::VectorDBInfo
+ - gselect::LayerSelect
+ - gselect::DriverSelect
+ - gselect::DatabaseSelect
+ - gselect::TableSelect
+ - gselect::ColumnSelect
+ - gselect::DbaseSelect
+ - gselect::LocationSelect
+ - gselect::MapsetSelect
+ - gselect::SubGroupSelect
+ - gselect::FormatSelect
+ - gselect::GdalSelect
+ - gselect::ProjSelect
+ - gselect::ElementSelect
+
+(C) 2007-2011 by the GRASS Development Team 
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton
+ at author Martin Landa <landa.martin gmail.com>
+ at author Vaclav Petras <wenzeslaus gmail.com> (menu customization)
+"""
+
+import os
+import sys
+import glob
+
+import wx
+import wx.combo
+import wx.lib.filebrowsebutton as filebrowse
+from wx.lib.newevent import NewEvent
+
+from core import globalvar
+
+import grass.script as grass
+from   grass.script import task as gtask
+
+from core.gcmd     import RunCommand, GError, GMessage
+from core.utils    import GetListOfLocations, GetListOfMapsets, GetFormats
+from core.utils    import GetSettingsPath, GetValidLayerName, ListSortLower, GetVectorNumberOfLayers
+from core.settings import UserSettings
+from core.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,
+                 updateOnPopup = True, onPopup = None,
+                 fullyQualified = True):
+        """!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
+        @param fullyQualified True to provide fully qualified names (map at mapset)
+        """
+        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.SetData(type = type, mapsets = mapsets,
+                             multiple = multiple,
+                             updateOnPopup = updateOnPopup, onPopup = onPopup,
+                             fullyQualified = fullyQualified)
+        self.GetChildren()[0].Bind(wx.EVT_KEY_UP, self.OnKeyUp)
+     
+    def OnKeyUp(self, event):
+        """!Shows popupwindow if down arrow key is released"""
+        if event.GetKeyCode() == wx.WXK_DOWN:
+            self.ShowPopup() 
+        else:
+            event.Skip()
+        
+    def SetElementList(self, type, mapsets = None):
+        """!Set element list
+
+        @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()
+    
+    def SetType(self, etype, multiple = False, mapsets = None,
+                updateOnPopup = True, onPopup = None):
+        """!Param set element type for widget
+
+        @param etype element type, see gselect.ElementSelect
+        """
+        self.tcp.SetData(type = etype, mapsets = mapsets,
+                         multiple = multiple,
+                         updateOnPopup = updateOnPopup, onPopup = onPopup)
+        
+class VectorSelect(Select):
+    def __init__(self, parent, ftype, **kwargs):
+        """!Custom to create a ComboBox with a tree control to display and
+        select vector maps. Control allows to filter vector maps. If you
+        don't need this feature use Select class instead
+        
+        @ftype filter vector maps based on feature type
+        """
+        Select.__init__(self, parent = parent, id = wx.ID_ANY,
+                        type = 'vector', **kwargs)
+        
+        self.ftype = ftype
+        
+        # remove vector maps which do not contain given feature type
+        self.tcp.SetFilter(self._isElement)
+        
+    def _isElement(self, vectorName):
+        """!Check if element should be filtered out"""
+        try:
+            if int(grass.vector_info_topo(vectorName)[self.ftype]) < 1:
+                return False
+        except KeyError:
+            return False
+        
+        return True
+
+class TreeCtrlComboPopup(wx.combo.ComboPopup):
+    """!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.onPopup = None
+        self.fullyQualified = True
+        
+        self.SetFilter(None)
+        
+    def Create(self, parent):
+        self.seltree = wx.TreeCtrl(parent, style=wx.TR_HIDE_ROOT
+                                   |wx.TR_HAS_BUTTONS
+                                   |wx.TR_SINGLE
+                                   |wx.TR_LINES_AT_ROOT
+                                   |wx.SIMPLE_BORDER
+                                   |wx.TR_FULL_ROW_HIGHLIGHT)
+        self.seltree.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
+        self.seltree.Bind(wx.EVT_MOTION, self.OnMotion)
+        self.seltree.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
+        self.seltree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.mapsetExpanded)
+        self.seltree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.mapsetCollapsed)
+        self.seltree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.mapsetActivated)
+        self.seltree.Bind(wx.EVT_TREE_SEL_CHANGED, self.mapsetSelected)
+        self.seltree.Bind(wx.EVT_TREE_DELETE_ITEM, lambda x: None)
+        
+    # the following dummy handler are needed to keep tree events from propagating up to
+    # the parent GIS Manager layer tree
+    def mapsetExpanded(self, event):
+        pass
+
+    def mapsetCollapsed(self, event):
+        pass
+
+    def mapsetActivated(self, event):
+        pass
+
+    def mapsetSelected(self, event):
+        pass
+    # end of dummy events
+
+    def GetControl(self):
+        return self.seltree
+
+    def GetStringValue(self):
+        """!Get value as a string separated by commas"""
+        return ','.join(self.value)
+    
+    def SetFilter(self, filter):
+        """!Set filter for GIS elements, see e.g. VectorSelect"""
+        self.filterElements = filter
+    
+    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)
+        
+        # selects map starting according to written text
+        inputText = self.GetCombo().GetValue().strip()
+        if inputText:
+            root = self.seltree.GetRootItem()
+            match = self.FindItem(root, inputText, startLetters = True)
+            self.seltree.EnsureVisible(match)
+            self.seltree.SelectItem(match)
+            
+      
+    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
+        self.seltree.DeleteAllItems()
+        self._getElementList(self.type, self.mapsets, elements, exclude)
+        
+        if len(self.value) > 0:
+            root = self.seltree.GetRootItem()
+            if not root:
+                return
+            item = self.FindItem(root, self.value[0])
+            try:
+                self.seltree.EnsureVisible(item)
+                self.seltree.SelectItem(item)
+            except:
+                pass
+            
+    def SetStringValue(self, value):
+        # this assumes that item strings are unique...
+        root = self.seltree.GetRootItem()
+        if not root:
+            return
+        found = self.FindItem(root, value)
+        winValue = self.GetCombo().GetValue().strip(',')
+        self.value = []
+        if winValue:
+            self.value = winValue.split(',')
+        
+        if found:
+            self.value.append(found)
+            self.seltree.SelectItem(found)
+        
+    def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
+        """!Reads UserSettings to get height (which was 200 in old implementation).
+        """
+        height = UserSettings.Get(group = 'appearance', key = 'gSelectPopupHeight', subkey = 'value')
+        return wx.Size(minWidth, min(height, maxHeight))
+
+    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']
+        
+        # map element types to g.mlist types
+        elementdict = {'cell':'rast',
+                       'raster':'rast',
+                       'rast':'rast',
+                       'raster files':'rast',
+                       'grid3':'rast3d',
+                       'rast3d':'rast3d',
+                       '3d-raster':'rast3d',
+                       'raster3d':'rast3d',
+                       'raster3D files':'rast3d',
+                       'vector':'vect',
+                       'vect':'vect',
+                       'binary vector files':'vect',
+                       'dig':'oldvect',
+                       'oldvect':'oldvect',
+                       'old vector':'oldvect',
+                       'dig_ascii':'asciivect',
+                       'asciivect':'asciivect',
+                       'asciivector':'asciivect',
+                       'ascii vector files':'asciivect',
+                       'icons':'icon',
+                       'icon':'icon',
+                       'paint icon files':'icon',
+                       'paint/labels':'labels',
+                       'labels':'labels',
+                       'label':'labels',
+                       'paint label files':'labels',
+                       'site_lists':'sites',
+                       'sites':'sites',
+                       'site list':'sites',
+                       'site list files':'sites',
+                       'windows':'region',
+                       'region':'region',
+                       'region definition':'region',
+                       'region definition files':'region',
+                       'windows3d':'region3d',
+                       'region3d':'region3d',
+                       'region3D definition':'region3d',
+                       'region3D definition files':'region3d',
+                       'group':'group',
+                       'imagery group':'group',
+                       'imagery group files':'group',
+                       '3d.view':'3dview',
+                       '3dview':'3dview',
+                       '3D viewing parameters':'3dview',
+                       '3D view parameters':'3dview'}
+        
+        if element not in elementdict:
+            self.AddItem(_('Not selectable element'))
+            return
+        
+        if globalvar.have_mlist:
+            filesdict = grass.mlist_grouped(elementdict[element],
+                                            check_search_path = False)
+        else:
+            filesdict = grass.list_grouped(elementdict[element],
+                                           check_search_path = False)
+        
+        # list of mapsets in current location
+        if mapsets is None:
+            mapsets = grass.mapsets(search_path = True)
+        
+        # current mapset first
+        if curr_mapset in mapsets and mapsets[0] != curr_mapset:
+            mapsets.remove(curr_mapset)
+            mapsets.insert(0, curr_mapset)
+        
+        first_mapset = None
+        for mapset in mapsets:
+            mapset_node = self.AddItem(_('Mapset') + ': ' + mapset)
+            if not first_mapset:
+                first_mapset = mapset_node
+            
+            self.seltree.SetItemTextColour(mapset_node, wx.Colour(50, 50, 200))
+            if mapset not in filesdict:
+                continue
+            try:
+                elem_list = filesdict[mapset]
+                elem_list.sort()
+                for elem in elem_list:
+                    if elem != '':
+                        fullqElem = elem + '@' + mapset
+                        if elements is not None:
+                            if (exclude and fullqElem in elements) or \
+                                    (not exclude and fullqElem not in elements):
+                                continue
+                        
+                        if self.filterElements:
+                            if self.filterElements(fullqElem):
+                                self.AddItem(elem, parent = mapset_node)
+                        else:
+                            self.AddItem(elem, parent = mapset_node)
+            except StandardError, e:
+                sys.stderr.write(_("GSelect: invalid item: %s") % e)
+                continue
+            
+            if self.seltree.ItemHasChildren(mapset_node):
+                sel = UserSettings.Get(group='appearance', key='elementListExpand',
+                                       subkey='selection')
+                collapse = True
+
+                if sel == 0: # collapse all except PERMANENT and current
+                    if mapset in ('PERMANENT', curr_mapset):
+                        collapse = False
+                elif sel == 1: # collapse all except PERMANENT
+                    if mapset == 'PERMANENT':
+                        collapse = False
+                elif sel == 2: # collapse all except current
+                    if mapset == curr_mapset:
+                        collapse = False
+                elif sel == 3: # collapse all
+                    pass
+                elif sel == 4: # expand all
+                    collapse = False
+                
+                if collapse:
+                    self.seltree.Collapse(mapset_node)
+                else:
+                    self.seltree.Expand(mapset_node)
+        
+        if first_mapset:
+            # select first mapset (MSW hack)
+            self.seltree.SelectItem(first_mapset)
+    
+    # helpers
+    def FindItem(self, parentItem, text, startLetters = False):
+        """!Finds item with given name or starting with given text"""
+        startletters = startLetters
+        item, cookie = self.seltree.GetFirstChild(parentItem)
+        while wx.TreeItemId.IsOk(item):
+            if self.seltree.GetItemText(item) == text:
+                return item
+            if self.seltree.ItemHasChildren(item):
+                item = self.FindItem(item, text, startLetters = startletters)
+                if wx.TreeItemId.IsOk(item):
+                    return item
+            elif startletters and self.seltree.GetItemText(item).startswith(text.split('@', 1)[0]):
+                return item
+            item, cookie = self.seltree.GetNextChild(parentItem, cookie)
+        return wx.TreeItemId()
+    
+    def AddItem(self, value, parent=None):
+        if not parent:
+            root = self.seltree.GetRootItem()
+            if not root:
+                root = self.seltree.AddRoot("<hidden root>")
+            parent = root
+
+        item = self.seltree.AppendItem(parent, text=value)
+        return item
+    
+    # able to recieve only wx.EVT_KEY_UP
+    def OnKeyUp(self, event):
+        """!Enables to select items using keyboard"""
+        
+        item = self.seltree.GetSelection()
+        if event.GetKeyCode() == wx.WXK_DOWN:
+            self.seltree.SelectItem(self.seltree.GetNextVisible(item))
+            
+        # problem with GetPrevVisible   
+        elif event.GetKeyCode() == wx.WXK_UP: 
+            if self.seltree.ItemHasChildren(item) and self.seltree.IsExpanded(self.seltree.GetPrevSibling(item)):
+                itemPrev = self.seltree.GetLastChild(self.seltree.GetPrevSibling(item))
+            else:
+                itemPrev = self.seltree.GetPrevSibling(item)
+                if not wx.TreeItemId.IsOk(itemPrev):
+                    itemPrev = self.seltree.GetItemParent(item)
+                    if item == self.seltree.GetFirstChild(self.seltree.GetRootItem())[0]:
+                        itemPrev = item
+            self.seltree.SelectItem(itemPrev)
+        
+        # selects first item starting with the written text in next mapset
+        elif event.GetKeyCode() == wx.WXK_TAB:
+            selected = self.seltree.GetSelection()
+            if self.seltree.ItemHasChildren(selected):
+                parent = selected
+            else:
+                parent = self.seltree.GetItemParent(selected)
+            nextSibling = self.seltree.GetNextSibling(parent)
+            if wx.TreeItemId.IsOk(nextSibling):
+                match = self.FindItem(nextSibling, self.GetCombo().GetValue().strip(), True) 
+            else: 
+                match = self.FindItem(self.seltree.GetFirstChild(self.seltree.GetItemParent(parent))[0],
+                                        self.GetCombo().GetValue().strip(), True) 
+            self.seltree.SelectItem(match)
+            
+        elif event.GetKeyCode() == wx.WXK_RIGHT:
+            if self.seltree.ItemHasChildren(item):
+                self.seltree.Expand(item)
+        
+        elif event.GetKeyCode() == wx.WXK_LEFT:
+            if self.seltree.ItemHasChildren(item):
+                self.seltree.Collapse(item)
+                
+        elif event.GetKeyCode() == wx.WXK_ESCAPE:
+            self.Dismiss()
+            
+        elif event.GetKeyCode() == wx.WXK_RETURN:
+            if self.seltree.GetRootItem() == self.seltree.GetItemParent(item):
+                self.value = [] 
+            else:
+                mapsetItem = self.seltree.GetItemParent(item)
+                fullName = self.seltree.GetItemText(item)
+                if self.fullyQualified:
+                    fullName += '@' + self.seltree.GetItemText(mapsetItem).split(':', -1)[1].strip()
+                
+                if self.multiple is True:
+                    # text item should be unique
+                    self.value.append(fullName)
+                else:
+                    self.value = [fullName]
+            
+            self.Dismiss()
+        
+    def OnMotion(self, evt):
+        """!Have the selection follow the mouse, like in a real combobox
+        """
+        item, flags = self.seltree.HitTest(evt.GetPosition())
+        if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
+            self.seltree.SelectItem(item)
+            self.curitem = item
+        evt.Skip()
+
+    def OnLeftDown(self, evt):
+        """!Do the combobox selection
+        """
+        item, flags = self.seltree.HitTest(evt.GetPosition())
+        if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
+            self.curitem = item
+            
+            if self.seltree.GetRootItem() == self.seltree.GetItemParent(item):
+                self.value = [] # cannot select mapset item
+            else:
+                mapsetItem = self.seltree.GetItemParent(item)
+                fullName = self.seltree.GetItemText(item)
+                if self.fullyQualified:
+                    fullName += '@' + self.seltree.GetItemText(mapsetItem).split(':', -1)[1].strip()
+                
+                if self.multiple is True:
+                    # text item should be unique
+                    self.value.append(fullName)
+                else:
+                    self.value = [fullName]
+            
+            self.Dismiss()
+        
+        evt.Skip()
+
+    def SetData(self, **kargs):
+        """!Set object properties"""
+        if 'type' in kargs:
+            self.type = kargs['type']
+        if 'mapsets' in kargs:
+            self.mapsets = kargs['mapsets']
+        if 'multiple' in kargs:
+            self.multiple = kargs['multiple']
+        if 'updateOnPopup' in kargs:
+            self.updateOnPopup = kargs['updateOnPopup']
+        if 'onPopup' in kargs:
+            self.onPopup = kargs['onPopup']
+        if 'fullyQualified' in kargs:
+            self.fullyQualified = kargs['fullyQualified']
+        
+    def GetType(self):
+        """!Get element type
+        """
+        return self.type
+    
+class VectorDBInfo:
+    """!Class providing information about attribute tables
+    linked to a vector map"""
+    def __init__(self, map):
+        self.map = map
+
+        # dictionary of layer number and associated (driver, database, table)
+        self.layers = {}
+         # dictionary of table and associated columns (type, length, values, ids)
+        self.tables = {}
+        
+        if not self._CheckDBConnection(): # -> self.layers
+            return
+
+        self._DescribeTables() # -> self.tables
+
+    def _CheckDBConnection(self):
+        """!Check DB connection"""
+        nuldev = file(os.devnull, 'w+')
+        self.layers = grass.vector_db(map=self.map, stderr=nuldev)
+        nuldev.close()
+        
+        if (len(self.layers.keys()) == 0):
+            return False
+
+        return True
+
+    def _DescribeTables(self):
+        """!Describe linked tables"""
+        for layer in self.layers.keys():
+            # determine column names and types
+            table = self.layers[layer]["table"]
+            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
+
+                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():
+                for col in columns.keys():
+                    if col.lower() == self.layers[layer]["key"]:
+                        self.layers[layer]["key"] = col.upper()
+                        break
+            
+            self.tables[table] = columns
+            
+        return True
+    
+    def Reset(self):
+        """!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
+    
+    def GetKeyColumn(self, layer):
+        """!Get key column of given layer
+        
+        @param layer vector layer number
+        """
+        return str(self.layers[layer]['key'])
+    
+    def GetTable(self, layer):
+        """!Get table name of given layer
+        
+        @param layer vector layer number
+        """
+        return self.layers[layer]['table']
+    
+    def GetDbSettings(self, layer):
+        """!Get database settins
+
+        @param layer layer number
+        
+        @return (driver, database)
+        """
+        return self.layers[layer]['driver'], self.layers[layer]['database']
+    
+    def GetTableDesc(self, table):
+        """!Get table columns
+
+        @param table table name
+        """
+        return self.tables[table]
+
+class LayerSelect(wx.ComboBox):
+    """!Creates combo box for selecting data layers defined for vector.
+    """
+    def __init__(self, parent,
+                 id = wx.ID_ANY, pos = wx.DefaultPosition,
+                 size = globalvar.DIALOG_LAYER_SIZE,
+                 vector = None, choices = [], initial = [], default = None):
+        
+        super(LayerSelect, self).__init__(parent, id, pos=pos, size=size,
+                                          choices=choices)
+        
+        self.parent  = parent
+        self.initial = initial
+        
+        self.SetName("LayerSelect")
+
+        # default value
+        self.default = default
+
+        self.InsertLayers(vector = vector)
+        
+    def InsertLayers(self, vector):
+        """!Insert layers for a vector into the layer combobox
+
+        @param vector name of vector map
+        """
+        if vector:
+            layers = GetVectorNumberOfLayers(vector)
+        else:
+            layers = list()
+        
+        for layer in self.initial:
+            if layer in layers:
+                continue
+            layers.append(layer)
+        
+        if self.default:
+            if len(layers) == 0:
+                layers.insert(0, str(self.default))
+            elif self.default not in layers:
+                layers.append(self.default)
+        
+        if len(layers) >= 1:
+            self.SetItems(layers)
+
+    def Reset(self):
+        """!Reset value"""
+        items = self.GetItems()
+        if items:
+            if '-1' in items:
+                self.SetStringSelection('-1')
+            else:
+                self.SetSelection(0)
+        else:
+            self.SetValue('')
+        
+class DriverSelect(wx.ComboBox):
+    """!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):
+
+        super(DriverSelect, self).__init__(parent, id, value, pos, size,
+                                           choices, style=wx.CB_READONLY)
+        
+        self.SetName("DriverSelect")
+        
+        self.SetStringSelection(value)
+
+class DatabaseSelect(wx.TextCtrl):
+    """!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):
+        
+        super(DatabaseSelect, self).__init__(parent, id, value, pos, size)
+                               
+        self.SetName("DatabaseSelect")
+
+class TableSelect(wx.ComboBox):
+    """!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,
+                 choices=[]):
+
+        super(TableSelect, self).__init__(parent, id, value, pos, size, choices,
+                                          style=wx.CB_READONLY)
+
+        self.SetName("TableSelect")
+
+        if not choices:
+            self.InsertTables()
+                
+    def InsertTables(self, driver=None, database=None):
+        """!Insert attribute tables into combobox"""
+        items = []
+
+        if not driver or not database:
+            connect = grass.db_connection()
+            
+            driver = connect['driver']
+            database = connect['database']
+        
+        ret = RunCommand('db.tables',
+                         flags = 'p',
+                         read = True,
+                         driver = driver,
+                         database = database)
+        
+        if ret:
+            for table in ret.splitlines():
+                items.append(table)
+            
+        self.SetItems(items)
+        self.SetValue('')
+        
+class ColumnSelect(wx.ComboBox):
+    """!Creates combo box for selecting columns in the attribute table
+    for a vector map.
+
+    @param parent window parent
+    @param id window id
+    @param value default value
+    @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, param = None, **kwargs):
+        self.defaultValue = value
+        self.param = param
+        
+        super(ColumnSelect, self).__init__(parent, id, value, size = size, **kwargs)
+        self.SetName("ColumnSelect")
+        
+        if vector:
+            self.InsertColumns(vector, layer)
+    
+    def InsertColumns(self, vector, layer, excludeKey = False, excludeCols = None, 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 excludeCols list of columns to be removed from the list
+        @param type only columns of given type (given as list)
+        """
+        if not dbInfo:
+            dbInfo = VectorDBInfo(vector)
+        
+        try:
+            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 excludeCols: # exclude key column
+                for key in columnchoices.iterkeys():
+                    if key in excludeCols:
+                        columns.remove(key)
+            if type: # only selected column types
+                for key, value in columnchoices.iteritems():
+                    if value['type'] not in type:
+                        try:
+                            columns.remove(key)
+                        except ValueError:
+                            pass
+        except (KeyError, ValueError):
+            columns = list()
+            
+        self.SetItems(columns)
+        self.SetValue(self.defaultValue)
+        
+        if self.param:
+            value = self.param.get('value', '')
+            if value != '' and value in columns:
+                self.SetValue(value)
+        
+    def InsertTableColumns(self, table, driver=None, database=None):
+        """!Insert table columns
+
+        @param table table name
+        @param driver driver name
+        @param database database name
+        """
+        columns = list()
+        
+        ret = RunCommand('db.columns',
+                         read = True,
+                         driver = driver,
+                         database = database,
+                         table = table)
+        
+        if ret:
+            columns = ret.splitlines()
+        
+        self.SetItems(columns)
+        self.SetValue(self.defaultValue)
+        
+        if self.param:
+            value = self.param.get('value', '')
+            if value != '' and value in columns:
+                self.SetValue(value)
+
+class DbaseSelect(wx.lib.filebrowsebutton.DirBrowseButton):
+    """!Widget for selecting GRASS Database"""
+    def __init__(self, parent, **kwargs):
+        super(DbaseSelect, self).__init__(parent, id = wx.ID_ANY,
+                                          size = globalvar.DIALOG_GSELECT_SIZE, labelText = '',
+                                          dialogTitle = _('Choose GIS Data Directory'),
+                                          buttonText = _('Browse'),
+                                          startDirectory = grass.gisenv()['GISDBASE'],
+                                          **kwargs)
+        
+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")
+        
+        if not gisdbase:
+            self.gisdbase = grass.gisenv()['GISDBASE']
+        else:
+            self.gisdbase = gisdbase
+        
+        self.SetItems(GetListOfLocations(self.gisdbase))
+
+    def UpdateItems(self, dbase):
+        """!Update list of locations
+
+        @param dbase path to GIS database
+        """
+        self.gisdbase = dbase
+        if dbase:
+            self.SetItems(GetListOfLocations(self.gisdbase))
+        else:
+            self.SetItems([])
+        
+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,
+                 searchPath = False, skipCurrent = False, **kwargs):
+        super(MapsetSelect, self).__init__(parent, id, size = size, 
+                                           style = wx.CB_READONLY, **kwargs)
+        self.searchPath  = searchPath
+        self.skipCurrent = skipCurrent
+        
+        self.SetName("MapsetSelect")
+        if not gisdbase:
+            self.gisdbase = grass.gisenv()['GISDBASE']
+        else:
+            self.gisdbase = gisdbase
+        
+        if not location:
+            self.location = grass.gisenv()['LOCATION_NAME']
+        else:
+            self.location = location
+        
+        if setItems:
+            self.SetItems(self._getMapsets())
+        
+    def UpdateItems(self, location, dbase = None):
+        """!Update list of mapsets for given location
+
+        @param dbase path to GIS database (None to use currently selected)
+        @param location name of location
+        """
+        if dbase:
+            self.gisdbase = dbase
+        self.location = location
+        if location:
+            self.SetItems(self._getMapsets())
+        else:
+            self.SetItems([])
+     
+    def _getMapsets(self):
+        if self.searchPath:
+            mlist = RunCommand('g.mapsets',
+                               read = True, flags = 'p',
+                               fs = 'newline').splitlines()
+        else:
+            mlist = GetListOfMapsets(self.gisdbase, self.location,
+                                     selectable = False)
+        
+        gisenv = grass.gisenv()
+        if self.skipCurrent and \
+                gisenv['LOCATION_NAME'] == self.location and \
+                gisenv['MAPSET'] in mlist:
+            mlist.remove(gisenv['MAPSET'])
+        
+        return mlist
+    
+class SubGroupSelect(wx.ComboBox):
+    """!Widget for selecting subgroups"""
+    def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE, 
+                 **kwargs):
+        super(SubGroupSelect, self).__init__(parent, id, size = size, 
+                                             **kwargs)
+        self.SetName("SubGroupSelect")
+
+    def Insert(self, group):
+        """!Insert subgroups for defined group"""
+        if not group:
+            return
+        gisenv = grass.gisenv()
+        try:
+            name, mapset = group.split('@', 1)
+        except ValueError:
+            name = group
+            mapset = gisenv['MAPSET']
+        
+        path = os.path.join(gisenv['GISDBASE'], gisenv['LOCATION_NAME'], mapset,
+                            'group', name, 'subgroup')
+        try:
+            self.SetItems(os.listdir(path))
+        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, 
+                                           **kwargs)
+        self.SetName("FormatSelect")
+        
+        if ogr:
+            ftype = 'ogr'
+        else:
+            ftype = 'gdal'
+        
+        formats = list()
+        for f in 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.settingsBox = wx.StaticBox(parent = self, id=wx.ID_ANY,
+                                        label=" %s " % _("Settings"))
+        
+        self.inputBox = wx.StaticBox(parent = self, id=wx.ID_ANY,
+                                     label=" %s " % _("Source"))
+        
+        # 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
+        
+        if self.ogr:
+            self.settingsFile = os.path.join(GetSettingsPath(), 'wxOGR')
+        else:
+            self.settingsFile = os.path.join(GetSettingsPath(), 'wxGDAL')
+        
+        self._settings = self._loadSettings()
+        self.settingsChoice = wx.Choice(parent = self, id = wx.ID_ANY)
+        self.settingsChoice.Bind(wx.EVT_CHOICE, self.OnSettingsLoad)
+        self.settingsChoice.SetItems(self._settings.keys())
+        self.btnSettings = wx.Button(parent = self, id = wx.ID_SAVE)
+        self.btnSettings.Bind(wx.EVT_BUTTON, self.OnSettingsSave)
+        
+        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 (%s)|%s|%s (*.*)|*.*' % \
+                (self._getExtPattern('tif'), self._getExtPattern('tif'), _('All files'))
+        else:
+            filemask = 'ESRI Shapefile (%s)|%s|%s (*.*)|*.*' % \
+                (self._getExtPattern('shp'), self._getExtPattern('shp'), _('All files'))
+        
+        dsnFile = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY, 
+                                              size=globalvar.DIALOG_GSELECT_SIZE, labelText = '',
+                                              dialogTitle=_('Choose file to import'),
+                                              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)
+        self.extension = wx.TextCtrl(parent = self, id = wx.ID_ANY)
+        self.extension.Bind(wx.EVT_TEXT, self.OnSetExtension)
+        self.extension.Hide()
+        
+        if ogr:
+            fType = 'ogr'
+        else:
+            fType = 'gdal'
+        self.input = { 'file' : [_("File:"),
+                                 dsnFile,
+                                 GetFormats()[fType]['file']],
+                       'dir'  : [_("Directory:"),
+                                 dsnDir,
+                                 GetFormats()[fType]['file']],
+                       'db'   : [_("Database:"),
+                                 dsnDbFile,
+                                 GetFormats()[fType]['database']],
+                       'pro'  : [_("Protocol:"),
+                                 dsnPro,
+                                 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])
+        
+        self.dsnText = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                     label = self.input[self.dsnType][0],
+                                     size = (75, -1))
+        self.extensionText = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                           label = _("Extension:"))
+        self.extensionText.Hide()
+        
+        self._layout()
+        
+        if not ogr:
+            self.OnSetFormat(event = None, format = 'GeoTIFF')
+        else:
+            self.OnSetFormat(event = None, format = 'ESRI Shapefile')
+        
+    def _layout(self):
+        """!Layout"""
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        settingsSizer = wx.StaticBoxSizer(self.settingsBox, wx.HORIZONTAL)
+        settingsSizer.Add(item = wx.StaticText(parent = self,
+                                               id = wx.ID_ANY,
+                                               label = _("Load settings:")),
+                          flag = wx.ALIGN_CENTER_VERTICAL | wx.RIGHT,
+                          border  = 5)
+        settingsSizer.Add(item = self.settingsChoice,
+                          proportion = 1,
+                          flag = wx.EXPAND)
+        settingsSizer.Add(item = self.btnSettings,
+                          flag = wx.LEFT,
+                          border = 5)
+        
+        inputSizer = wx.StaticBoxSizer(self.inputBox, wx.HORIZONTAL)
+        
+        self.dsnSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        self.dsnSizer.AddGrowableRow(1)
+        self.dsnSizer.AddGrowableCol(3)
+        
+        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), span = (1, 3))
+        
+        self.dsnSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                               label = _("Format:")),
+                          flag = wx.ALIGN_CENTER_VERTICAL,
+                          pos = (1, 0))
+        self.dsnSizer.Add(item=self.format,
+                          flag = wx.ALIGN_CENTER_VERTICAL,
+                          pos = (1, 1))
+        self.dsnSizer.Add(item = self.extensionText,
+                          flag=wx.ALIGN_CENTER_VERTICAL,
+                          pos = (1, 2))
+        self.dsnSizer.Add(item=self.extension,
+                          flag = wx.ALIGN_CENTER_VERTICAL,
+                          pos = (1, 3))
+        
+        inputSizer.Add(item=self.dsnSizer, proportion=1,
+                       flag=wx.EXPAND | wx.ALL)
+
+        mainSizer.Add(item=settingsSizer, proportion=0,
+                      flag=wx.ALL | wx.EXPAND, border=5)
+        mainSizer.Add(item=self.source, proportion=0,
+                      flag=wx.LEFT | wx.RIGHT | wx.EXPAND, border=5)
+        mainSizer.Add(item=inputSizer, proportion=0,
+                      flag=wx.ALL | wx.EXPAND, border=5)
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+
+    def _getExtPatternGlob(self, ext):
+        """!Get pattern for case-insensitive globing"""
+        pattern = '*.'
+        for c in ext:
+            pattern += '[%s%s]' % (c.lower(), c.upper())
+        return pattern
+    
+    def _getExtPattern(self, ext):
+        """!Get pattern for case-insensitive file mask"""
+        return '*.%s;*.%s' % (ext.lower(), ext.upper())
+
+    def OnSettingsLoad(self, event):
+        """!Load named settings"""
+        name = event.GetString()
+        if name not in self._settings:
+            GError(parent = self,
+                   message = _("Settings named '%s' not found") % name)
+            return
+        data = self._settings[name]
+        self.OnSetType(event = None, sel = self.sourceMap[data[0]])
+        self.OnSetFormat(event = None, format = data[2])
+        self.OnSetDsn(event = None, path = data[1])
+        
+    def OnSettingsSave(self, event):
+        """!Save settings"""
+        dlg = wx.TextEntryDialog(parent = self,
+                                 message = _("Name:"),
+                                 caption = _("Save settings"))
+        if dlg.ShowModal() != wx.ID_OK:
+            return
+        
+        if not dlg.GetValue():
+            GMessage(parent = self,
+                     message = _("Name not given, settings is not saved."))
+            return
+        
+        name = dlg.GetValue()
+        try:
+            fd = open(self.settingsFile, 'a')
+            fd.write(name + ';' + self.dsnType + ';' +
+                     self._getDsn() + ';' +
+                     self.format.GetStringSelection())
+            fd.write('\n')
+        except IOError:
+            GError(parent = self,
+                   message = _("Unable to save settings"))
+            return
+        fd.close()
+        
+        self._settings = self._loadSettings()
+        self.settingsChoice.Append(name)
+        self.settingsChoice.SetStringSelection(name)
+        
+        dlg.Destroy()
+                
+    def _loadSettings(self):
+        """!Load settings from the file
+
+        The file is defined by self.SettingsFile.
+        
+        @return parsed dict
+        @return empty dict on error
+        """
+        data = dict()
+        if not os.path.exists(self.settingsFile):
+            return data
+        
+        try:
+            fd = open(self.settingsFile, 'r')
+            for line in fd.readlines():
+                try:
+                    name, ftype, dsn, format = line.rstrip('\n').split(';')
+                    data[name] = (ftype, dsn, format)
+                except ValueError:
+                    pass
+        except IOError:
+            return data
+        
+        fd.close()
+        
+        return data
+
+    def OnSetType(self, event, sel = None):
+        """!Datasource type changed"""
+        if event:
+            sel = event.GetSelection()
+        else:
+            self.source.SetSelection(sel)
+        
+        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|%s (*.*)|*.*' % \
+                    (self._getExtPattern(ext), self._getExtPattern(ext), _('All files'))
+            except KeyError:
+                format += '%s (*.*)|*.*' % _('All files')
+            
+            win = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY, 
+                                              size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
+                                              dialogTitle=_('Choose file to import'),
+                                              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.OnSetFormat(event = None, format = 'GeoTIFF')
+            else:
+                self.OnSetFormat(event = None, format = 'ESRI Shapefile')
+        else:
+            if 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()
+        
+        if sel == self.sourceMap['dir']:
+            if not self.extension.IsShown():
+                self.extensionText.Show()
+                self.extension.Show()
+        else:
+            if self.extension.IsShown():
+                self.extensionText.Hide()
+                self.extension.Hide()
+            
+        self.dsnSizer.Layout()
+        
+    def _getDsn(self):
+        """!Get datasource name"""
+        if self.format.GetStringSelection() == 'PostgreSQL':
+            return 'PG:dbname=%s' % self.input[self.dsnType][1].GetStringSelection()
+        
+        return self.input[self.dsnType][1].GetValue()
+    
+    def OnSetDsn(self, event, path = None):
+        """!Input DXF file/OGR dsn defined, update list of layer widget"""
+        if event:
+            path = event.GetString()
+        else:
+            if self.format.GetStringSelection() == 'PostgreSQL':
+                for item in path.split(':', 1)[1].split(','):
+                    key, value = item.split('=', 1)
+                    if key == 'dbname':
+                        self.input[self.dsnType][1].SetStringSelection(value)
+                        break
+            else:
+                self.input[self.dsnType][1].SetValue(path)
+        
+        if not path:
+            return 
+
+        self._reloadLayers()
+        
+        if event:
+            event.Skip()
+        
+    def _reloadLayers(self):
+        """!Reload list of layers"""
+        dsn = self._getDsn()
+        if not dsn:
+            return
+        
+        data = list()        
+        layerId = 1
+        
+        if self.dsnType == 'file':
+            baseName = os.path.basename(dsn)
+            grassName = GetValidLayerName(baseName.split('.', -1)[0])
+            data.append((layerId, baseName, grassName))
+        elif self.dsnType == 'dir':
+            ext = self.extension.GetValue()
+            for file in glob.glob(os.path.join(dsn, "%s") % self._getExtPatternGlob(ext)):
+                baseName = os.path.basename(file)
+                grassName = GetValidLayerName(baseName.split('.', -1)[0])
+                data.append((layerId, baseName, grassName))
+                layerId += 1
+        elif self.dsnType == 'db':
+            ret = RunCommand('v.in.ogr',
+                             quiet = True,
+                             read = True,
+                             flags = 'l',
+                             dsn = dsn)
+            if not ret:
+                self.parent.list.LoadData()
+                if hasattr(self, "btn_run"):
+                    self.btn_run.Enable(False)
+                return
+            layerId = 1
+            for line in ret.split(','):
+                layerName = line.strip()
+                grassName = 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)
+        
+    def OnSetExtension(self, event):
+        """!Extension changed"""
+        # reload layers
+        self._reloadLayers()
+        
+    def OnSetFormat(self, event, format = None):
+        """!Format changed"""
+        if self.dsnType not in ['file', 'dir', 'db']:
+            return
+        
+        win = self.input[self.dsnType][1]
+        self.dsnSizer.Remove(win)
+        
+        if self.dsnType == 'file':
+            win.Destroy()
+        else: # database
+            win.Hide()
+        
+        if event:
+            format = event.GetString()
+        else:
+            self.format.SetStringSelection(format)
+        
+        if self.dsnType == 'file':
+            try:
+                ext = self.format.GetExtension(format)
+                if not ext:
+                    raise KeyError
+                format += ' (%s)|%s|%s (*.*)|*.*' % \
+                    (self._getExtPattern(ext), self._getExtPattern(ext), _('All files'))
+            except KeyError:
+                format += '%s (*.*)|*.*' % _('All files')
+            
+            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)
+            
+        elif self.dsnType == 'dir':
+            pass
+        
+        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', ['--help']):
+                    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), span = (1, 3))
+        self.dsnSizer.Layout()
+        
+        # update extension
+        self.extension.SetValue(self.GetFormatExt())
+
+        # reload layers
+        self._reloadLayers()
+        
+    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())
+    
+class ProjSelect(wx.ComboBox):
+    """!Widget for selecting input raster/vector map used by
+    r.proj/v.proj modules."""
+    def __init__(self, parent, isRaster, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
+                 **kwargs):
+        super(ProjSelect, self).__init__(parent, id, size = size, 
+                                         style = wx.CB_READONLY, **kwargs)
+        self.SetName("ProjSelect")
+        self.isRaster = isRaster
+        
+    def UpdateItems(self, dbase, location, mapset):
+        """!Update list of maps
+        
+        """
+        if not dbase:
+            dbase = grass.gisenv()['GISDBASE']
+        if not mapset:
+            mapset = grass.gisenv()['MAPSET']
+        if self.isRaster:
+            ret = RunCommand('r.proj',
+                             quiet = True,
+                             read = True,
+                             flags = 'l',
+                             dbase = dbase,
+                             location = location,
+                             mapset = mapset)
+        else:
+            ret = RunCommand('v.proj',
+                             quiet = True,
+                             read = True,
+                             flags = 'l',
+                             dbase = dbase,
+                             location = location,
+                             mapset = mapset)
+        listMaps = list()
+        if ret:
+            for line in ret.splitlines():
+                listMaps.append(line.strip())
+        ListSortLower(listMaps)
+        
+        self.SetItems(listMaps)
+        self.SetValue('')
+
+class ElementSelect(wx.Choice):
+    def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE, 
+                 **kwargs):
+        """!Widget for selecting GIS element
+        
+        @param parent parent window
+        """
+        super(ElementSelect, self).__init__(parent, id, size = size, 
+                                            **kwargs)
+        self.SetName("ElementSelect")
+        
+        task = gtask.parse_interface('g.list')
+        p = task.get_param(value = 'type')
+        self.values = p.get('values', [])
+                
+        self.SetItems(self.values)
+
+    def GetValue(self, name):
+        """!Translate value
+
+        @param name element name
+        """
+        return name


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_core/mapdisp.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_core/mapdisp.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_core/mapdisp.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,338 @@
+"""!
+ at package gui_core.mapdisp
+
+ at brief Base classes for Map display window
+
+Classes:
+ - mapdisp::MapFrameBase
+
+(C) 2009-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+ at author Michael Barton <michael.barton at asu.edu>
+"""
+
+import os
+import sys
+
+import wx
+
+from core       import globalvar
+from core.debug import Debug
+
+from grass.script import core as grass
+
+class MapFrameBase(wx.Frame):
+    """!Base class for map display window
+    
+    Derived class must use statusbarManager or override
+    GetProperty, SetProperty and HasProperty methods.
+    If derived class enables and disables auto-rendering,
+    it should override IsAutoRendered method.
+    """
+    def __init__(self, parent = None, id = wx.ID_ANY, title = None,
+                 style = wx.DEFAULT_FRAME_STYLE, toolbars = None,
+                 Map = None, auimgr = None, name = None, **kwargs):
+        """!
+        @param toolbars array of activated toolbars, e.g. ['map', 'digit']
+        @param Map instance of render.Map
+        @param auimgs AUI manager
+        @param name frame name
+        @param kwargs wx.Frame attributes
+        """
+        
+
+        self.Map        = Map       # instance of render.Map
+        self.parent     = parent
+        
+        wx.Frame.__init__(self, parent, id, title, style = style, name = name, **kwargs)
+        
+        # available cursors
+        self.cursors = {
+            # default: cross
+            # "default" : wx.StockCursor(wx.CURSOR_DEFAULT),
+            "default" : wx.StockCursor(wx.CURSOR_ARROW),
+            "cross"   : wx.StockCursor(wx.CURSOR_CROSS),
+            "hand"    : wx.StockCursor(wx.CURSOR_HAND),
+            "pencil"  : wx.StockCursor(wx.CURSOR_PENCIL),
+            "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
+            }
+                
+        #
+        # set the size & system icon
+        #
+        self.SetClientSize(self.GetSize())
+        self.iconsize = (16, 16)
+
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
+        
+        # toolbars
+        self.toolbars = {}
+        
+        #
+        # Fancy gui
+        #
+        self._mgr = wx.aui.AuiManager(self)
+        
+    def _initMap(self, map):
+        """!Initialize map display, set dimensions and map region
+        """
+        if not grass.find_program('g.region', ['--help']):
+            sys.exit(_("GRASS module '%s' not found. Unable to start map "
+                       "display window.") % 'g.region')
+        
+        self.width, self.height = self.GetClientSize()
+        
+        Debug.msg(2, "MapFrame._initMap():")
+        map.ChangeMapSize(self.GetClientSize())
+        map.region = map.GetRegion() # g.region -upgc
+        # self.Map.SetRegion() # adjust region to match display window
+        
+    def SetProperty(self, name, value):
+        """!Sets property"""
+        self.statusbarManager.SetProperty(name, value)
+        
+    def GetProperty(self, name):
+        """!Returns property"""
+        return self.statusbarManager.GetProperty(name)
+        
+    def HasProperty(self, name):
+        """!Checks whether object has property"""
+        return self.statusbarManager.HasProperty(name)
+    
+    def GetPPM(self):
+        """! Get pixel per meter
+        
+        @todo now computed every time, is it necessary?
+        @todo enable user to specify ppm (and store it in UserSettings)
+        """
+        # TODO: need to be fixed...
+        ### screen X region problem
+        ### user should specify ppm
+        dc = wx.ScreenDC()
+        dpSizePx = wx.DisplaySize()   # display size in pixels
+        dpSizeMM = wx.DisplaySizeMM() # display size in mm (system)
+        dpSizeIn = (dpSizeMM[0] / 25.4, dpSizeMM[1] / 25.4) # inches
+        sysPpi  = dc.GetPPI()
+        comPpi = (dpSizePx[0] / dpSizeIn[0],
+                  dpSizePx[1] / dpSizeIn[1])
+
+        ppi = comPpi                  # pixel per inch
+        ppm = ((ppi[0] / 2.54) * 100, # pixel per meter
+                    (ppi[1] / 2.54) * 100)
+        
+        Debug.msg(4, "MapFrameBase.GetPPM(): size: px=%d,%d mm=%f,%f "
+                  "in=%f,%f ppi: sys=%d,%d com=%d,%d; ppm=%f,%f" % \
+                  (dpSizePx[0], dpSizePx[1], dpSizeMM[0], dpSizeMM[1],
+                   dpSizeIn[0], dpSizeIn[1],
+                   sysPpi[0], sysPpi[1], comPpi[0], comPpi[1],
+                   ppm[0], ppm[1]))
+        
+        return ppm
+    
+    def SetMapScale(self, value, map = None):
+        """! Set current map scale
+        
+        @param value scale value (n if scale is 1:n)
+        @param map Map instance (if none self.Map is used)
+        """
+        if not map:
+            map = self.Map
+        
+        region = self.Map.region
+        dEW = value * (region['cols'] / self.GetPPM()[0])
+        dNS = value * (region['rows'] / self.GetPPM()[1])
+        region['n'] = region['center_northing'] + dNS / 2.
+        region['s'] = region['center_northing'] - dNS / 2.
+        region['w'] = region['center_easting']  - dEW / 2.
+        region['e'] = region['center_easting']  + dEW / 2.
+        
+        # add to zoom history
+        self.GetWindow().ZoomHistory(region['n'], region['s'],
+                                   region['e'], region['w'])
+    
+    def GetMapScale(self, map = None):
+        """! Get current map scale
+        
+        @param map Map instance (if none self.Map is used)
+        """
+        if not map:
+            map = self.Map
+        
+        region = map.region
+        ppm = self.GetPPM()
+
+        heightCm = region['rows'] / ppm[1] * 100
+        widthCm  = region['cols'] / ppm[0] * 100
+
+        Debug.msg(4, "MapFrame.GetMapScale(): width_cm=%f, height_cm=%f" %
+                  (widthCm, heightCm))
+
+        xscale = (region['e'] - region['w']) / (region['cols'] / ppm[0])
+        yscale = (region['n'] - region['s']) / (region['rows'] / ppm[1])
+        scale = (xscale + yscale) / 2.
+        
+        Debug.msg(3, "MapFrame.GetMapScale(): xscale=%f, yscale=%f -> scale=%f" % \
+                      (xscale, yscale, scale))
+        
+        return scale
+        
+    def GetProgressBar(self):
+        """!Returns progress bar
+        
+        Progress bar can be used by other classes.
+        """
+        return self.statusbarManager.GetProgressBar()
+        
+    def GetMap(self):
+        """!Returns current Map instance
+        """
+        return self.Map
+
+    def GetWindow(self):
+        """!Get map window"""
+        return self.MapWindow
+        
+    def GetMapToolbar(self):
+       """!Returns toolbar with zooming tools"""
+       raise NotImplementedError()
+       
+    def GetToolbar(self, name):
+        """!Returns toolbar if exists else None.
+        
+        Toolbars dictionary contains currently used toolbars only.
+        """
+        if name in self.toolbars:
+            return self.toolbars[name]
+        
+        return None
+       
+    def StatusbarUpdate(self):
+        """!Update statusbar content"""
+        self.statusbarManager.Update()
+        
+    def IsAutoRendered(self):
+        """!Check if auto-rendering is enabled"""
+        return self.GetProperty('render')
+        
+    def CoordinatesChanged(self):
+        """!Shows current coordinates on statusbar.
+        
+        Used in BufferedWindow to report change of map coordinates (under mouse cursor).
+        """
+        self.statusbarManager.ShowItem('coordinates')
+        
+    def StatusbarReposition(self):
+        """!Reposition items in statusbar"""
+        self.statusbarManager.Reposition()
+        
+    def StatusbarEnableLongHelp(self, enable = True):
+        """!Enable/disable toolbars long help"""
+        for toolbar in self.toolbars.itervalues():
+            toolbar.EnableLongHelp(enable)
+        
+    def IsStandalone(self):
+        """!Check if Map display is standalone"""
+        raise NotImplementedError("IsStandalone")
+   
+    def OnRender(self, event):
+        """!Re-render map composition (each map layer)
+        """
+        raise NotImplementedError("OnRender")
+        
+    def OnDraw(self, event):
+        """!Re-display current map composition
+        """
+        self.MapWindow.UpdateMap(render = False)
+        
+    def OnErase(self, event):
+        """!Erase the canvas
+        """
+        self.MapWindow.EraseMap()
+        
+    def OnZoomIn(self, event):
+        """!Zoom in the map.
+        Set mouse cursor, zoombox attributes, and zoom direction
+        """
+        toolbar = self.GetMapToolbar()
+        self._switchTool(toolbar, event)
+        
+        win = self.GetWindow()
+        self._prepareZoom(mapWindow = win, zoomType = 1)
+        
+    def OnZoomOut(self, event):
+        """!Zoom out the map.
+        Set mouse cursor, zoombox attributes, and zoom direction
+        """
+        toolbar = self.GetMapToolbar()
+        self._switchTool(toolbar, event)
+        
+        win = self.GetWindow()
+        self._prepareZoom(mapWindow = win, zoomType = -1)
+        
+    def _prepareZoom(self, mapWindow, zoomType):
+        """!Prepares MapWindow for zoom, toggles toolbar
+        
+        @param mapWindow MapWindow to prepare
+        @param zoomType 1 for zoom in, -1 for zoom out
+        """
+        mapWindow.mouse['use'] = "zoom"
+        mapWindow.mouse['box'] = "box"
+        mapWindow.zoomtype = zoomType
+        mapWindow.pen = wx.Pen(colour = 'Red', width = 2, style = wx.SHORT_DASH)
+        
+        # change the cursor
+        mapWindow.SetCursor(self.cursors["cross"])
+    
+    def _switchTool(self, toolbar, event):
+        """!Helper function to switch tools"""
+        if toolbar:
+            toolbar.OnTool(event)
+            toolbar.action['desc'] = ''
+            
+    def OnPan(self, event):
+        """!Panning, set mouse to drag
+        """
+        toolbar = self.GetMapToolbar()
+        self._switchTool(toolbar, event)
+        
+        win = self.GetWindow()
+        self._preparePan(mapWindow = win)
+    
+    def _preparePan(self, mapWindow):
+        """!Prepares MapWindow for pan, toggles toolbar
+        
+        @param mapWindow MapWindow to prepare
+        """
+        mapWindow.mouse['use'] = "pan"
+        mapWindow.mouse['box'] = "pan"
+        mapWindow.zoomtype = 0
+        
+        # change the cursor
+        mapWindow.SetCursor(self.cursors["hand"])
+        
+    def OnZoomBack(self, event):
+        """!Zoom last (previously stored position)
+        """
+        self.MapWindow.ZoomBack()
+        
+    def OnZoomToMap(self, event):
+        """!
+        Set display extents to match selected raster (including NULLs)
+        or vector map.
+        """
+        self.MapWindow.ZoomToMap(layers = self.Map.GetListOfLayers())
+    
+    def OnZoomToWind(self, event):
+        """!Set display geometry to match computational region
+        settings (set with g.region)
+        """
+        self.MapWindow.ZoomToWind()
+        
+    def OnZoomToDefault(self, event):
+        """!Set display geometry to match default region settings
+        """
+        self.MapWindow.ZoomToDefault()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_core/mapwindow.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_core/mapwindow.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_core/mapwindow.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,240 @@
+"""!
+ at package gui_core.mapwindow
+
+ at brief Map display canvas - base class for buffered window.
+
+Classes:
+ - mapwindow::MapWindow
+
+(C) 2006-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+ at author Michael Barton
+ at author Jachym Cepicky
+"""
+
+import wx
+
+from core.settings import UserSettings
+
+class MapWindow(object):
+    """!Abstract map display window class
+    
+    Superclass for BufferedWindow class (2D display mode), and GLWindow
+    (3D display mode).
+    
+    Subclasses have to define
+     - _bindMouseEvents method which binds MouseEvent handlers
+     - Pixel2Cell
+     - Cell2Pixel (if it is possible)
+    
+    """
+    def __init__(self, parent, id = wx.ID_ANY,
+                 Map = None, tree = None, lmgr = None, **kwargs):
+        self.parent = parent # MapFrame
+        self.Map    = Map
+        self.tree   = tree
+        self.lmgr   = lmgr
+        
+        # mouse attributes -- position on the screen, begin and end of
+        # dragging, and type of drawing
+        self.mouse = {
+            'begin': [0, 0], # screen coordinates
+            'end'  : [0, 0],
+            'use'  : "pointer",
+            'box'  : "point"
+            }
+        # last east, north coordinates, changes on mouse motion
+        self.lastEN = None 
+        
+        # stores overridden cursor
+        self._overriddenCursor = None
+
+    def RegisterMouseEventHandler(self, event, handler, cursor = None):
+        """!Binds event handler
+        
+        Call event.Skip() in handler to allow default processing in MapWindow.
+        
+        \code
+        # your class methods
+        def OnButton(self, event):
+            # current map display's map window
+            # expects LayerManager to be the parent
+            self.mapwin = self.parent.GetLayerTree().GetMapDisplay().GetWindow()
+            if self.mapwin.RegisterMouseEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction,
+                                                     wx.StockCursor(wx.CURSOR_CROSS)):
+                self.parent.GetLayerTree().GetMapDisplay().Raise()
+            else:
+                # handle that you cannot get coordinates
+        
+        def OnMouseAction(self, event):
+            # get real world coordinates of mouse click
+            coor = self.mapwin.Pixel2Cell(event.GetPositionTuple()[:])
+            self.text.SetLabel('Coor: ' + str(coor))
+            self.mapwin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN)
+            event.Skip()
+        \endcode
+        
+        @param event one of mouse events
+        @param handler function to handle event
+        @param cursor cursor which temporary overrides current cursor
+        
+        @return True if successful
+        @return False if event cannot be bind
+        """
+        
+        # if it is a VDigitWindow it cannot be used
+        # hasattr is ugly
+        if hasattr(self, "digit"):
+            return False
+        
+        self.Bind(event, handler)
+        self.mouse['useBeforeGenericEvent'] = self.mouse['use']
+        self.mouse['use'] = 'genericEvent'
+        
+        if cursor:
+            self._overriddenCursor = self.GetCursor()
+            self.SetCursor(cursor)
+        
+        return True
+
+
+    def UnregisterMouseEventHandler(self, event):
+        """!Unbinds event handler a restores previous state
+        
+        You should unbind to restore normal MapWindow behaviour.
+        Note that this operation will unbind any other external (non-MapWindow) handlers.
+        
+        @param event event to unbind
+        
+        @return True if successful
+        @return False if event cannot be unbind
+        """
+        if hasattr(self, "digit"):
+            return False
+        
+        # it is not yet possible in wxPython to unbind exact event
+        ret = self.Unbind(event)
+        
+        # restore bind state
+        self._bindMouseEvents()
+        
+        # restore mouse use (previous state)
+        self.mouse['use'] = self.mouse['useBeforeGenericEvent']
+        
+        # restore overridden cursor
+        if self._overriddenCursor:
+            self.SetCursor(self._overriddenCursor)
+        
+        return ret
+    
+    def Pixel2Cell(self, (x, y)):
+        raise NotImplementedError()
+    
+    def Cell2Pixel(self, (east, north)):
+        raise NotImplementedError()
+
+    def OnMotion(self, event):
+        """!Tracks mouse motion and update statusbar
+        
+        @see GetLastEN
+        """
+        try:
+            self.lastEN = self.Pixel2Cell(event.GetPositionTuple())
+        except (ValueError):
+            self.lastEN = None
+        # FIXME: special case for vdigit and access to statusbarManager
+        if self.parent.statusbarManager.GetMode() == 0: # Coordinates            
+            updated = False
+            if hasattr(self, "digit"):
+                precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                                             subkey = 'precision'))
+                updated = self._onMotion(self.lastEN, precision)
+
+            if not updated:
+                self.parent.CoordinatesChanged()
+        
+        event.Skip()
+
+    def GetLastEN(self):
+        """!Returns last coordinates of mouse cursor.
+        
+        @see OnMotion
+        """
+        return self.lastEN
+    
+    def GetLayerByName(self, name, mapType, dataType = 'layer'):
+        """!Get layer from layer tree by nam
+        
+        @param name layer name
+        @param type 'item' / 'layer' / 'nviz'
+
+        @return layer / map layer properties / nviz properties
+        @return None
+        """
+        if not self.tree:
+            return None
+        
+        try:
+            mapLayer = self.Map.GetListOfLayers(l_type = mapType, l_name = name)[0]
+        except IndexError:
+            return None
+        
+        if dataType == 'layer':
+            return mapLayer
+        item = self.tree.FindItemByData('maplayer', mapLayer)
+        if not item:
+            return None
+        if dataType == 'nviz':
+            return self.tree.GetPyData(item)[0]['nviz']
+        
+        return item
+        
+    def GetSelectedLayer(self, type = 'layer', multi = False):
+        """!Get selected layer from layer tree
+        
+        @param type 'item' / 'layer' / 'nviz'
+        @param multi return first selected layer or all
+        
+        @return layer / map layer properties / nviz properties
+        @return None / [] on failure
+        """
+        ret = []
+        if not self.tree or \
+                not self.tree.GetSelection():
+            if multi:
+                return []
+            else:
+                return None
+        
+        if multi and \
+                type == 'item':
+            return self.tree.GetSelections()
+        
+        for item in self.tree.GetSelections():
+            if not item.IsChecked():
+                if multi:
+                    continue
+                else:
+                    return None
+
+            if type == 'item': # -> multi = False
+                return item
+        
+            try:
+                if type == 'nviz':
+                    layer = self.tree.GetPyData(item)[0]['nviz']
+                else:
+                    layer = self.tree.GetPyData(item)[0]['maplayer']
+            except:
+                layer = None
+
+            if multi:
+                ret.append(layer)
+            else:
+                return layer
+            
+        return ret


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_core/menu.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_core/menu.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_core/menu.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,120 @@
+"""!
+ at package gui_core.menu
+
+ at brief Menu classes for wxGUI
+
+Classes:
+ - menu::Menu
+
+(C) 2010 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+ at author Pawel Netzel (menu customization)
+ at author Milena Nowotarska (menu customization)
+ at author Robert Szczepanek (menu customization)
+ at author Vaclav Petras <wenzeslaus gmail.com> (menu customization)
+"""
+
+import wx
+
+from core          import globalvar
+from core          import utils
+from core.gcmd     import EncodeString
+from core.settings import UserSettings
+
+class Menu(wx.MenuBar):
+    def __init__(self, parent, data):
+        """!Creates menubar"""
+        wx.MenuBar.__init__(self)
+        self.parent   = parent
+        self.menudata = data
+        self.menucmd  = dict()
+        
+        self.menustyle = UserSettings.Get(group='appearance', key='menustyle', subkey='selection')
+        
+        for eachMenuData in self.menudata.GetMenu():
+            for eachHeading in eachMenuData:
+                menuLabel = eachHeading[0]
+                menuItems = eachHeading[1]
+                self.Append(self._createMenu(menuItems), menuLabel)
+        
+    def _createMenu(self, menuData):
+        """!Creates menu"""
+        menu = wx.Menu()
+        for eachItem in menuData:
+            if len(eachItem) == 2:
+                label = eachItem[0]
+                subMenu = self._createMenu(eachItem[1])
+                menu.AppendMenu(wx.ID_ANY, label, subMenu)
+            else:
+                self._createMenuItem(menu, self.menustyle, *eachItem)
+        
+        self.parent.Bind(wx.EVT_MENU_HIGHLIGHT_ALL, self.OnMenuHighlight)
+        
+        return menu
+
+    def _createMenuItem(self, menu, menustyle, label, help, handler, gcmd, keywords,
+                        shortcut = '', wxId = wx.ID_ANY, kind = wx.ITEM_NORMAL):
+        """!Creates menu items
+        There are three menu styles (menu item text styles).
+        1 -- label only, 2 -- label and cmd name, 3 -- cmd name only
+        """
+        if not label:
+            menu.AppendSeparator()
+            return
+        
+        if len(gcmd) > 0:
+            helpString = gcmd + ' -- ' + help
+            if menustyle == 1:
+                label += '   [' + gcmd + ']'
+            elif menustyle == 2:
+                label = '      [' + gcmd + ']'
+        else:
+            helpString = help
+        
+        if shortcut:
+            label += '\t' + shortcut
+        
+        menuItem = menu.Append(wxId, label, helpString, kind)
+        
+        self.menucmd[menuItem.GetId()] = gcmd
+        
+        if gcmd: 
+            try: 
+                cmd = utils.split(str(gcmd)) 
+            except UnicodeError: 
+                cmd = utils.split(EncodeString((gcmd))) 
+            if cmd and cmd[0] not in globalvar.grassCmd: 
+                menuItem.Enable(False)
+        
+        rhandler = eval('self.parent.' + handler)
+        
+        self.parent.Bind(wx.EVT_MENU, rhandler, menuItem)
+
+    def GetData(self):
+        """!Get menu data"""
+        return self.menudata
+    
+    def GetCmd(self):
+        """!Get list of commands
+
+        @return list of commands
+        """
+        return self.menucmd
+        
+    def OnMenuHighlight(self, event):
+        """
+        Default menu help handler
+        """
+         # Show how to get menu item info from this event handler
+        id = event.GetMenuId()
+        item = self.FindItemById(id)
+        if item:
+            text = item.GetText()
+            help = item.GetHelp()
+
+        # but in this case just call Skip so the default is done
+        event.Skip()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_core/preferences.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_core/preferences.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_core/preferences.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1565 @@
+"""!
+ at package gui_core.preferences
+
+ at brief User preferences dialog
+
+Sets default display font, etc.  If you want to add some value to
+settings you have to add default value to defaultSettings and set
+constraints in internalSettings in Settings class. Everything can be
+used in PreferencesDialog.
+
+Classes:
+ - preferences::PreferencesBaseDialog
+ - preferences::PreferencesDialog
+ - preferences::DefaultFontDialog
+ - preferences::MapsetAccess
+ - preferences::CheckListMapset
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton (Arizona State University)
+ at author Martin Landa <landa.martin gmail.com>
+ at author Vaclav Petras <wenzeslaus gmail.com> (menu customization)
+"""
+
+import os
+import sys
+import copy
+try:
+    import pwd
+    havePwd = True
+except ImportError:
+    havePwd = False
+
+import wx
+import wx.lib.colourselect    as csel
+import wx.lib.mixins.listctrl as listmix
+
+from wx.lib.newevent import NewEvent
+
+from grass.script import core as grass
+
+from core          import globalvar
+from core.gcmd     import RunCommand
+from core.utils    import ListOfMapsets, GetColorTables, ReadEpsgCodes
+from core.settings import UserSettings
+
+wxSettingsChanged, EVT_SETTINGS_CHANGED = NewEvent()
+
+class PreferencesBaseDialog(wx.Dialog):
+    """!Base preferences dialog"""
+    def __init__(self, parent, settings, title = _("User settings"),
+                 size = (500, 375),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+        self.parent = parent # ModelerFrame
+        self.title  = title
+        self.size   = size
+        self.settings = settings
+        
+        wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title,
+                           style = style)
+        
+        # notebook
+        self.notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
+        
+        # dict for window ids
+        self.winId = {}
+        
+        # create notebook pages
+        
+        # buttons
+        self.btnDefault = wx.Button(self, wx.ID_ANY, _("Set to default"))
+        self.btnSave = wx.Button(self, wx.ID_SAVE)
+        self.btnApply = wx.Button(self, wx.ID_APPLY)
+        self.btnCancel = wx.Button(self, wx.ID_CANCEL)
+        self.btnSave.SetDefault()
+        
+        # bindigs
+        self.btnDefault.Bind(wx.EVT_BUTTON, self.OnDefault)
+        self.btnDefault.SetToolTipString(_("Revert settings to default and apply changes"))
+        self.btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+        self.btnApply.SetToolTipString(_("Apply changes for the current session"))
+        self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
+        self.btnSave.SetToolTipString(_("Apply and save changes to user settings file (default for next sessions)"))
+        self.btnSave.SetDefault()
+        self.btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+        self.btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
+
+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+
+        self._layout()
+        
+    def _layout(self):
+        """!Layout window"""
+        # sizers
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(item = self.btnDefault, proportion = 1,
+                     flag = wx.ALL, border = 5)
+        btnStdSizer = wx.StdDialogButtonSizer()
+        btnStdSizer.AddButton(self.btnCancel)
+        btnStdSizer.AddButton(self.btnSave)
+        btnStdSizer.AddButton(self.btnApply)
+        btnStdSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = self.notebook, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.EXPAND, border = 0)
+        mainSizer.Add(item = btnStdSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border = 5)
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+        
+    def OnDefault(self, event):
+        """!Button 'Set to default' pressed"""
+        self.settings.userSettings = copy.deepcopy(self.settings.defaultSettings)
+        
+        # update widgets
+        for gks in self.winId.keys():
+            try:
+                group, key, subkey = gks.split(':')
+                value = self.settings.Get(group, key, subkey)
+            except ValueError:
+                group, key, subkey, subkey1 = gks.split(':')
+                value = self.settings.Get(group, key, [subkey, subkey1])
+            win = self.FindWindowById(self.winId[gks])
+            if win.GetName() in ('GetValue', 'IsChecked'):
+                value = win.SetValue(value)
+            elif win.GetName() == 'GetSelection':
+                value = win.SetSelection(value)
+            elif win.GetName() == 'GetStringSelection':
+                value = win.SetStringSelection(value)
+            else:
+                value = win.SetValue(value)
+        
+    def OnApply(self, event):
+        """!Button 'Apply' pressed
+        Posts event EVT_SETTINGS_CHANGED.
+        """
+        if self._updateSettings():
+            self.parent.goutput.WriteLog(_('Settings applied to current session but not saved'))
+            event = wxSettingsChanged()
+            wx.PostEvent(self, event)
+            self.Close()
+
+    def OnCloseWindow(self, event):
+        self.Hide()
+        
+    def OnCancel(self, event):
+        """!Button 'Cancel' pressed"""
+        self.Close()
+        
+    def OnSave(self, event):
+        """!Button 'Save' pressed
+        Posts event EVT_SETTINGS_CHANGED.
+        """
+        if self._updateSettings():
+            self.settings.SaveToFile()
+            self.parent.goutput.WriteLog(_('Settings saved to file \'%s\'.') % self.settings.filePath)
+            event = wxSettingsChanged()
+            wx.PostEvent(self, event)
+            self.Close()
+
+    def _updateSettings(self):
+        """!Update user settings"""
+        for item in self.winId.keys():
+            try:
+                group, key, subkey = item.split(':')
+                subkey1 = None
+            except ValueError:
+                group, key, subkey, subkey1 = item.split(':')
+            
+            id = self.winId[item]
+            win = self.FindWindowById(id)
+            if win.GetName() == 'GetValue':
+                value = win.GetValue()
+            elif win.GetName() == 'GetSelection':
+                value = win.GetSelection()
+            elif win.GetName() == 'IsChecked':
+                value = win.IsChecked()
+            elif win.GetName() == 'GetStringSelection':
+                value = win.GetStringSelection()
+            elif win.GetName() == 'GetColour':
+                value = tuple(win.GetValue())
+            else:
+                value = win.GetValue()
+
+            if key == 'keycolumn' and value == '':
+                wx.MessageBox(parent = self,
+                              message = _("Key column cannot be empty string."),
+                              caption = _("Error"), style = wx.OK | wx.ICON_ERROR)
+                win.SetValue(self.settings.Get(group = 'atm', key = 'keycolumn', subkey = 'value'))
+                return False
+
+            if subkey1:
+                self.settings.Set(group, value, key, [subkey, subkey1])
+            else:
+                self.settings.Set(group, value, key, subkey)
+        
+        if self.parent.GetName() == 'Modeler':
+            return True
+        
+        #
+        # update default window dimension
+        #
+        if self.settings.Get(group = 'general', key = 'defWindowPos', subkey = 'enabled') is True:
+            dim = ''
+            # layer manager
+            pos = self.parent.GetPosition()
+            size = self.parent.GetSize()
+            dim = '%d,%d,%d,%d' % (pos[0], pos[1], size[0], size[1])
+            # opened displays
+            for page in range(0, self.parent.gm_cb.GetPageCount()):
+                pos = self.parent.gm_cb.GetPage(page).maptree.mapdisplay.GetPosition()
+                size = self.parent.gm_cb.GetPage(page).maptree.mapdisplay.GetSize()
+
+                dim += ',%d,%d,%d,%d' % (pos[0], pos[1], size[0], size[1])
+
+            self.settings.Set(group = 'general', key = 'defWindowPos', subkey = 'dim', value = dim)
+        else:
+            self.settings.Set(group = 'general', key = 'defWindowPos', subkey = 'dim', value = '')
+
+        return True
+
+class PreferencesDialog(PreferencesBaseDialog):
+    """!User preferences dialog"""
+    def __init__(self, parent, title = _("GUI Settings"),
+                 settings = UserSettings):
+        
+        PreferencesBaseDialog.__init__(self, parent = parent, title = title,
+                                       settings = settings)
+        
+        # create notebook pages
+        self._createGeneralPage(self.notebook)
+        self._createAppearancePage(self.notebook)
+        self._createDisplayPage(self.notebook)
+        self._createCmdPage(self.notebook)
+        self._createAttributeManagerPage(self.notebook)
+        self._createProjectionPage(self.notebook)
+        
+        self.SetMinSize(self.GetBestSize())
+        self.SetSize(self.size)
+        
+    def _createGeneralPage(self, notebook):
+        """!Create notebook page for general settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("General"))
+        
+        border = wx.BoxSizer(wx.VERTICAL)
+        #
+        # Layer Manager settings
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Layer Manager settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        #
+        # ask when removing map layer from layer tree
+        #
+        row = 0
+        askOnRemoveLayer = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                       label = _("Ask when removing map layer from layer tree"),
+                                       name = 'IsChecked')
+        askOnRemoveLayer.SetValue(self.settings.Get(group = 'manager', key = 'askOnRemoveLayer', subkey = 'enabled'))
+        self.winId['manager:askOnRemoveLayer:enabled'] = askOnRemoveLayer.GetId()
+        
+        gridSizer.Add(item = askOnRemoveLayer,
+                      pos = (row, 0), span = (1, 2))
+        
+        row += 1
+        askOnQuit = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                label = _("Ask when quiting wxGUI or closing display"),
+                                name = 'IsChecked')
+        askOnQuit.SetValue(self.settings.Get(group = 'manager', key = 'askOnQuit', subkey = 'enabled'))
+        self.winId['manager:askOnQuit:enabled'] = askOnQuit.GetId()
+        
+        gridSizer.Add(item = askOnQuit,
+                      pos = (row, 0), span = (1, 2))
+
+        row += 1
+        hideSearch = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                 label = _("Hide '%s' tab (requires GUI restart)") % _("Search module"),
+                                 name = 'IsChecked')
+        hideSearch.SetValue(self.settings.Get(group = 'manager', key = 'hideTabs', subkey = 'search'))
+        self.winId['manager:hideTabs:search'] = hideSearch.GetId()
+        
+        gridSizer.Add(item = hideSearch,
+                      pos = (row, 0), span = (1, 2))
+        
+        row += 1
+        hidePyShell = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                  label = _("Hide '%s' tab (requires GUI restart)") % _("Python shell"),
+                                  name = 'IsChecked')
+        hidePyShell.SetValue(self.settings.Get(group = 'manager', key = 'hideTabs', subkey = 'pyshell'))
+        self.winId['manager:hideTabs:pyshell'] = hidePyShell.GetId()
+        
+        gridSizer.Add(item = hidePyShell,
+                      pos = (row, 0), span = (1, 2))
+        
+        #
+        # Selected text is copied to clipboard
+        #
+        row += 1
+        copySelectedTextToClipboard = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                                  label = _("Automatically copy selected text to clipboard (in Command console)"),
+                                                  name = 'IsChecked')
+        copySelectedTextToClipboard.SetValue(self.settings.Get(group = 'manager', key = 'copySelectedTextToClipboard', subkey = 'enabled'))
+        self.winId['manager:copySelectedTextToClipboard:enabled'] = copySelectedTextToClipboard.GetId()
+        
+        gridSizer.Add(item = copySelectedTextToClipboard,
+                      pos = (row, 0), span = (1, 2))
+        
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 3)
+        
+        #
+        # workspace
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Workspace settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        row = 0
+        posDisplay = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                 label = _("Suppress positioning Map Display Window(s)"),
+                                 name = 'IsChecked')
+        posDisplay.SetValue(self.settings.Get(group = 'general', key = 'workspace',
+                                              subkey = ['posDisplay', 'enabled']))
+        self.winId['general:workspace:posDisplay:enabled'] = posDisplay.GetId()
+        
+        gridSizer.Add(item = posDisplay,
+                      pos = (row, 0), span = (1, 2))
+        
+        row += 1 
+        
+        posManager = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                 label = _("Suppress positioning Layer Manager window"),
+                                 name = 'IsChecked')
+        posManager.SetValue(self.settings.Get(group = 'general', key = 'workspace',
+                                              subkey = ['posManager', 'enabled']))
+        self.winId['general:workspace:posManager:enabled'] = posManager.GetId()
+        
+        gridSizer.Add(item = posManager,
+                      pos = (row, 0), span = (1, 2))
+        
+        row += 1
+        defaultPos = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                 label = _("Save current window layout as default"),
+                                 name = 'IsChecked')
+        defaultPos.SetValue(self.settings.Get(group = 'general', key = 'defWindowPos', subkey = 'enabled'))
+        defaultPos.SetToolTip(wx.ToolTip (_("Save current position and size of Layer Manager window and opened "
+                                            "Map Display window(s) and use as default for next sessions.")))
+        self.winId['general:defWindowPos:enabled'] = defaultPos.GetId()
+        
+        gridSizer.Add(item = defaultPos,
+                      pos = (row, 0), span = (1, 2))
+        
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 3)
+        
+        panel.SetSizer(border)
+        
+        return panel
+    
+
+        panel.SetSizer(border)
+        
+        return panel
+
+    def _createAppearancePage(self, notebook):
+        """!Create notebook page for display settings"""
+   
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("Appearance"))
+
+        border = wx.BoxSizer(wx.VERTICAL)
+
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Font settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+
+        #
+        # font settings
+        #
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 3)
+
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Font for command output:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        outfontButton = wx.Button(parent = panel, id = wx.ID_ANY,
+                               label = _("Set font"), size = (100, -1))
+        gridSizer.Add(item = outfontButton,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+
+        #
+        # appearence
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Appearance settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        gridSizer  =  wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+
+        #
+        # element list
+        #
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Element list:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        elementList = wx.Choice(parent = panel, id = wx.ID_ANY, size = (325, -1),
+                                choices = self.settings.Get(group = 'appearance', key = 'elementListExpand',
+                                                            subkey = 'choices', internal = True),
+                                name = "GetSelection")
+        elementList.SetSelection(self.settings.Get(group = 'appearance', key = 'elementListExpand',
+                                                   subkey = 'selection'))
+        self.winId['appearance:elementListExpand:selection'] = elementList.GetId()
+
+        gridSizer.Add(item = elementList,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 1))
+        
+        #
+        # menu style
+        #
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Menu style (requires GUI restart):")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        listOfStyles = self.settings.Get(group = 'appearance', key = 'menustyle',
+                                         subkey = 'choices', internal = True)
+        
+        menuItemText = wx.Choice(parent = panel, id = wx.ID_ANY, size = (325, -1),
+                                 choices = listOfStyles,
+                                 name = "GetSelection")
+        menuItemText.SetSelection(self.settings.Get(group = 'appearance', key = 'menustyle', subkey = 'selection'))
+        
+        self.winId['appearance:menustyle:selection'] = menuItemText.GetId()
+        
+        gridSizer.Add(item = menuItemText,
+                      flag = wx.ALIGN_RIGHT,
+                      pos = (row, 1))
+        
+        #
+        # gselect.TreeCtrlComboPopup height
+        #
+        row += 1
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Height of map selection popup window (in pixels):")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        min = self.settings.Get(group = 'appearance', key = 'gSelectPopupHeight', subkey = 'min', internal = True)
+        max = self.settings.Get(group = 'appearance', key = 'gSelectPopupHeight', subkey = 'max', internal = True)
+        value = self.settings.Get(group = 'appearance', key = 'gSelectPopupHeight', subkey = 'value')
+        
+        popupHeightSpin = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (100, -1))
+        popupHeightSpin.SetRange(min,max)
+        popupHeightSpin.SetValue(value)
+        
+        self.winId['appearance:gSelectPopupHeight:value'] = popupHeightSpin.GetId()
+        
+        gridSizer.Add(item = popupHeightSpin,
+                      flag = wx.ALIGN_RIGHT,
+                      pos = (row, 1))
+        
+        
+        #
+        # icon theme
+        #
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Icon theme (requires GUI restart):")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        iconTheme = wx.Choice(parent = panel, id = wx.ID_ANY, size = (100, -1),
+                              choices = self.settings.Get(group = 'appearance', key = 'iconTheme',
+                                                        subkey = 'choices', internal = True),
+                              name = "GetStringSelection")
+        iconTheme.SetStringSelection(self.settings.Get(group = 'appearance', key = 'iconTheme', subkey = 'type'))
+        self.winId['appearance:iconTheme:type'] = iconTheme.GetId()
+
+        gridSizer.Add(item = iconTheme,
+                      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)
+                
+        # bindings
+        outfontButton.Bind(wx.EVT_BUTTON, self.OnSetOutputFont)
+        
+        return panel
+    
+    def _createDisplayPage(self, notebook):
+        """!Create notebook page for display settings"""
+   
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("Map Display"))
+
+        border = wx.BoxSizer(wx.VERTICAL)
+
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Font settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+
+        #
+        # font settings
+        #
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Default font for GRASS displays:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        fontButton = wx.Button(parent = panel, id = wx.ID_ANY,
+                               label = _("Set font"), size = (100, -1))
+        gridSizer.Add(item = fontButton,
+                      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.ALL | wx.EXPAND, border = 3)
+
+        #
+        # display settings
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Default display settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+
+        
+        #
+        # display driver
+        #
+        row = 0
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Display driver:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos=(row, 0))
+        listOfDrivers = self.settings.Get(group='display', key='driver', subkey='choices', internal=True)
+        # check if cairo is available
+        if 'cairo' not in listOfDrivers:
+            for line in RunCommand('d.mon',
+                                   flags = 'l',
+                                   read = True).splitlines():
+                if 'cairo' in line:
+                    listOfDrivers.append('cairo')
+                    break
+        
+        driver = wx.Choice(parent=panel, id=wx.ID_ANY, size=(150, -1),
+                           choices=listOfDrivers,
+                           name="GetStringSelection")
+        driver.SetStringSelection(self.settings.Get(group='display', key='driver', subkey='type'))
+        self.winId['display:driver:type'] = driver.GetId()
+
+        gridSizer.Add(item = driver,
+                      flag = wx.ALIGN_RIGHT,
+                      pos = (row, 1))
+
+
+        #
+        # Statusbar mode
+        #
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Statusbar mode:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        listOfModes = self.settings.Get(group = 'display', key = 'statusbarMode', subkey = 'choices', internal = True)
+        statusbarMode = wx.Choice(parent = panel, id = wx.ID_ANY, size = (150, -1),
+                                  choices = listOfModes,
+                                  name = "GetSelection")
+        statusbarMode.SetSelection(self.settings.Get(group = 'display', key = 'statusbarMode', subkey = 'selection'))
+        self.winId['display:statusbarMode:selection'] = statusbarMode.GetId()
+
+        gridSizer.Add(item = statusbarMode,
+                      flag = wx.ALIGN_RIGHT,
+                      pos = (row, 1))
+
+        #
+        # Background color
+        #
+        row += 1
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Background color:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        bgColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                    colour = self.settings.Get(group = 'display', key = 'bgcolor', subkey = 'color'),
+                                    size = globalvar.DIALOG_COLOR_SIZE)
+        bgColor.SetName('GetColour')
+        self.winId['display:bgcolor:color'] = bgColor.GetId()
+        
+        gridSizer.Add(item = bgColor,
+                      flag = wx.ALIGN_RIGHT,
+                      pos = (row, 1))
+        
+        #
+        # Align extent to display size
+        #
+        row += 1
+        alignExtent = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                  label = _("Align region extent based on display size"),
+                                  name = "IsChecked")
+        alignExtent.SetValue(self.settings.Get(group = 'display', key = 'alignExtent', subkey = 'enabled'))
+        self.winId['display:alignExtent:enabled'] = alignExtent.GetId()
+
+        gridSizer.Add(item = alignExtent,
+                      pos = (row, 0), span = (1, 2))
+        
+        #
+        # Use computation resolution
+        #
+        row += 1
+        compResolution = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                     label = _("Constrain display resolution to computational settings"),
+                                     name = "IsChecked")
+        compResolution.SetValue(self.settings.Get(group = 'display', key = 'compResolution', subkey = 'enabled'))
+        self.winId['display:compResolution:enabled'] = compResolution.GetId()
+
+        gridSizer.Add(item = compResolution,
+                      pos = (row, 0), span = (1, 2))
+
+        #
+        # auto-rendering
+        #
+        row += 1
+        autoRendering = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                    label = _("Enable auto-rendering"),
+                                    name = "IsChecked")
+        autoRendering.SetValue(self.settings.Get(group = 'display', key = 'autoRendering', subkey = 'enabled'))
+        self.winId['display:autoRendering:enabled'] = autoRendering.GetId()
+
+        gridSizer.Add(item = autoRendering,
+                      pos = (row, 0), span = (1, 2))
+        
+        #
+        # auto-zoom
+        #
+        row += 1
+        autoZooming = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                  label = _("Enable auto-zooming to selected map layer"),
+                                  name = "IsChecked")
+        autoZooming.SetValue(self.settings.Get(group = 'display', key = 'autoZooming', subkey = 'enabled'))
+        self.winId['display:autoZooming:enabled'] = autoZooming.GetId()
+
+        gridSizer.Add(item = autoZooming,
+                      pos = (row, 0), span = (1, 2))
+        
+        #
+        # mouse wheel zoom
+        #
+        row += 1
+        wheelZooming = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                  label = _("Enable zooming by mouse wheel"),
+                                  name = "IsChecked")
+        wheelZooming.SetValue(self.settings.Get(group = 'display', key = 'mouseWheelZoom',
+                                                subkey = 'enabled'))
+        self.winId['display:mouseWheelZoom:enabled'] = wheelZooming.GetId()
+
+        gridSizer.Add(item = wheelZooming,
+                      pos = (row, 0), flag = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL)
+                      
+        listOfModes = self.settings.Get(group = 'display', key = 'mouseWheelZoom', subkey = 'choices', internal = True)
+        zoomMode = wx.Choice(parent = panel, id = wx.ID_ANY, size = (200, -1),
+                             choices = listOfModes,
+                             name = "GetSelection")
+        zoomMode.SetSelection(self.settings.Get(group = 'display', key = 'mouseWheelZoom', subkey = 'selection'))
+        self.winId['display:mouseWheelZoom:selection'] = zoomMode.GetId()
+
+        gridSizer.Add(item = zoomMode,
+                      flag = wx.ALIGN_RIGHT,
+                      pos = (row, 1))
+
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+        
+        panel.SetSizer(border)
+                
+        # bindings
+        fontButton.Bind(wx.EVT_BUTTON, self.OnSetFont)
+        wheelZooming.Bind(wx.EVT_CHECKBOX, self.OnEnableWheelZoom)
+        
+        # enable/disable controls according to settings
+        self.OnEnableWheelZoom(None)
+        
+        return panel
+
+    def _createCmdPage(self, notebook):
+        """!Create notebook page for commad dialog settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("Command"))
+        
+        border = wx.BoxSizer(wx.VERTICAL)
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Command dialog settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        #
+        # command dialog settings
+        #
+        row = 0
+        # overwrite
+        overwrite = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                label = _("Allow output files to overwrite existing files"),
+                                name = "IsChecked")
+        overwrite.SetValue(self.settings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'))
+        self.winId['cmd:overwrite:enabled'] = overwrite.GetId()
+        
+        gridSizer.Add(item = overwrite,
+                      pos = (row, 0), span = (1, 2))
+        row += 1
+        # close
+        close = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                            label = _("Close dialog when command is successfully finished"),
+                            name = "IsChecked")
+        close.SetValue(self.settings.Get(group = 'cmd', key = 'closeDlg', subkey = 'enabled'))
+        self.winId['cmd:closeDlg:enabled'] = close.GetId()
+        
+        gridSizer.Add(item = close,
+                      pos = (row, 0), span = (1, 2))
+        row += 1
+        # add layer
+        add = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                          label = _("Add created map into layer tree"),
+                          name = "IsChecked")
+        add.SetValue(self.settings.Get(group = 'cmd', key = 'addNewLayer', subkey = 'enabled'))
+        self.winId['cmd:addNewLayer:enabled'] = add.GetId()
+    
+        gridSizer.Add(item = add,
+                      pos = (row, 0), span = (1, 2))
+        
+        row += 1
+        # interactive input
+        interactive = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                  label = _("Allow interactive input"),
+                                  name = "IsChecked")
+        interactive.SetValue(self.settings.Get(group = 'cmd', key = 'interactiveInput', subkey = 'enabled'))
+        self.winId['cmd:interactiveInput:enabled'] = interactive.GetId()
+        gridSizer.Add(item = interactive,
+                      pos = (row, 0), span = (1, 2))
+        
+        row += 1
+        # verbosity
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Verbosity level:")),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 0))
+        verbosity = wx.Choice(parent = panel, id = wx.ID_ANY, size = (200, -1),
+                              choices = self.settings.Get(group = 'cmd', key = 'verbosity', subkey = 'choices', internal = True),
+                              name = "GetStringSelection")
+        verbosity.SetStringSelection(self.settings.Get(group = 'cmd', key = 'verbosity', subkey = 'selection'))
+        self.winId['cmd:verbosity:selection'] = verbosity.GetId()
+        
+        gridSizer.Add(item = verbosity,
+                      pos = (row, 1), flag = wx.ALIGN_RIGHT)
+        
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 3)
+        
+        #
+        # raster settings
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Raster settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        #
+        # raster overlay
+        #
+        row = 0
+        rasterOverlay = wx.CheckBox(parent=panel, id=wx.ID_ANY,
+                                    label=_("Overlay raster maps"),
+                                    name='IsChecked')
+        rasterOverlay.SetValue(self.settings.Get(group='cmd', key='rasterOverlay', subkey='enabled'))
+        self.winId['cmd:rasterOverlay:enabled'] = rasterOverlay.GetId()
+        
+        gridSizer.Add(item=rasterOverlay,
+                      pos=(row, 0), span=(1, 2))
+        
+        # default color table
+        row += 1
+        rasterCTCheck = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                    label = _("Default color table"),
+                                    name = 'IsChecked')
+        rasterCTCheck.SetValue(self.settings.Get(group = 'cmd', key = 'rasterColorTable', subkey = 'enabled'))
+        self.winId['cmd:rasterColorTable:enabled'] = rasterCTCheck.GetId()
+        rasterCTCheck.Bind(wx.EVT_CHECKBOX, self.OnCheckColorTable)
+        
+        gridSizer.Add(item = rasterCTCheck,
+                      pos = (row, 0))
+        
+        rasterCTName = wx.Choice(parent = panel, id = wx.ID_ANY, size = (200, -1),
+                               choices = GetColorTables(),
+                               name = "GetStringSelection")
+        rasterCTName.SetStringSelection(self.settings.Get(group = 'cmd', key = 'rasterColorTable', subkey = 'selection'))
+        self.winId['cmd:rasterColorTable:selection'] = rasterCTName.GetId()
+        if not rasterCTCheck.IsChecked():
+            rasterCTName.Enable(False)
+        
+        gridSizer.Add(item = rasterCTName,
+                      pos = (row, 1))
+        
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+        
+        #
+        # vector settings
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Vector settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.FlexGridSizer (cols = 7, hgap = 3, vgap = 3)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Display:")),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        for type in ('point', 'line', 'centroid', 'boundary',
+                     'area', 'face'):
+            chkbox = wx.CheckBox(parent = panel, label = type)
+            checked = self.settings.Get(group = 'cmd', key = 'showType',
+                                        subkey = [type, 'enabled'])
+            chkbox.SetValue(checked)
+            self.winId['cmd:showType:%s:enabled' % type] = chkbox.GetId()
+            gridSizer.Add(item = chkbox)
+
+        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 _createAttributeManagerPage(self, notebook):
+        """!Create notebook page for 'Attribute Table Manager' settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("Attributes"))
+
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+
+        #
+        # highlighting
+        #
+        highlightBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                                    label = " %s " % _("Highlighting"))
+        highlightSizer = wx.StaticBoxSizer(highlightBox, wx.VERTICAL)
+
+        flexSizer = wx.FlexGridSizer (cols = 2, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(0)
+        
+        label = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Color:"))
+        hlColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                    colour = self.settings.Get(group = 'atm', key = 'highlight', subkey = 'color'),
+                                    size = globalvar.DIALOG_COLOR_SIZE)
+        hlColor.SetName('GetColour')
+        self.winId['atm:highlight:color'] = hlColor.GetId()
+
+        flexSizer.Add(label, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(hlColor, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
+
+        label = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Line width (in pixels):"))
+        hlWidth = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (50, -1),
+                              initial = self.settings.Get(group = 'atm', key = 'highlight',subkey = 'width'),
+                              min = 1, max = 1e6)
+        self.winId['atm:highlight:width'] = hlWidth.GetId()
+
+        flexSizer.Add(label, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(hlWidth, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
+
+        highlightSizer.Add(item = flexSizer,
+                           proportion = 0,
+                           flag = wx.ALL | wx.EXPAND,
+                           border = 5)
+
+        pageSizer.Add(item = highlightSizer,
+                      proportion = 0,
+                      flag = wx.ALL | wx.EXPAND,
+                      border = 5)
+
+        #
+        # data browser related settings
+        #
+        dataBrowserBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                                    label = " %s " % _("Data browser"))
+        dataBrowserSizer = wx.StaticBoxSizer(dataBrowserBox, wx.VERTICAL)
+
+        flexSizer = wx.FlexGridSizer (cols = 2, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(0)
+        label = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Left mouse double click:"))
+        leftDbClick = wx.Choice(parent = panel, id = wx.ID_ANY,
+                                choices = self.settings.Get(group = 'atm', key = 'leftDbClick', subkey = 'choices', internal = True),
+                                name = "GetSelection")
+        leftDbClick.SetSelection(self.settings.Get(group = 'atm', key = 'leftDbClick', subkey = 'selection'))
+        self.winId['atm:leftDbClick:selection'] = leftDbClick.GetId()
+
+        flexSizer.Add(label, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(leftDbClick, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
+
+        # encoding
+        label = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                              label = _("Encoding (e.g. utf-8, ascii, iso8859-1, koi8-r):"))
+        encoding = wx.TextCtrl(parent = panel, id = wx.ID_ANY,
+                               value = self.settings.Get(group = 'atm', key = 'encoding', subkey = 'value'),
+                               name = "GetValue", size = (200, -1))
+        self.winId['atm:encoding:value'] = encoding.GetId()
+
+        flexSizer.Add(label, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(encoding, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
+
+        # ask on delete record
+        askOnDeleteRec = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                     label = _("Ask when deleting data record(s) from table"),
+                                     name = 'IsChecked')
+        askOnDeleteRec.SetValue(self.settings.Get(group = 'atm', key = 'askOnDeleteRec', subkey = 'enabled'))
+        self.winId['atm:askOnDeleteRec:enabled'] = askOnDeleteRec.GetId()
+
+        flexSizer.Add(askOnDeleteRec, proportion = 0)
+
+        dataBrowserSizer.Add(item = flexSizer,
+                           proportion = 0,
+                           flag = wx.ALL | wx.EXPAND,
+                           border = 5)
+
+        pageSizer.Add(item = dataBrowserSizer,
+                      proportion = 0,
+                      flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
+                      border = 3)
+
+        #
+        # create table
+        #
+        createTableBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                                    label = " %s " % _("Create table"))
+        createTableSizer = wx.StaticBoxSizer(createTableBox, wx.VERTICAL)
+
+        flexSizer = wx.FlexGridSizer (cols = 2, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(0)
+
+        label = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                              label = _("Key column:"))
+        keyColumn = wx.TextCtrl(parent = panel, id = wx.ID_ANY,
+                                size = (250, -1))
+        keyColumn.SetValue(self.settings.Get(group = 'atm', key = 'keycolumn', subkey = 'value'))
+        self.winId['atm:keycolumn:value'] = keyColumn.GetId()
+        
+        flexSizer.Add(label, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(keyColumn, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
+
+        createTableSizer.Add(item = flexSizer,
+                             proportion = 0,
+                             flag = wx.ALL | wx.EXPAND,
+                             border = 5)
+
+        pageSizer.Add(item = createTableSizer,
+                      proportion = 0,
+                      flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
+                      border = 3)
+        
+        panel.SetSizer(pageSizer)
+
+        return panel
+
+    def _createProjectionPage(self, notebook):
+        """!Create notebook page for workspace settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("Projection"))
+        
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        #
+        # projections statusbar settings
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Projection statusbar settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(1)
+
+        # epsg
+        row = 0
+        label = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                              label = _("EPSG code:"))
+        epsgCode = wx.ComboBox(parent = panel, id = wx.ID_ANY,
+                               name = "GetValue",
+                               size = (150, -1))
+        self.epsgCodeDict = dict()
+        epsgCode.SetValue(str(self.settings.Get(group = 'projection', key = 'statusbar', subkey = 'epsg')))
+        self.winId['projection:statusbar:epsg'] = epsgCode.GetId()
+        
+        gridSizer.Add(item = label,
+                      pos = (row, 0),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = epsgCode,
+                      pos = (row, 1), span = (1, 2))
+        
+        # proj
+        row += 1
+        label = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                              label = _("Proj.4 string (required):"))
+        projString = wx.TextCtrl(parent = panel, id = wx.ID_ANY,
+                                 value = self.settings.Get(group = 'projection', key = 'statusbar', subkey = 'proj4'),
+                                 name = "GetValue", size = (400, -1))
+        self.winId['projection:statusbar:proj4'] = projString.GetId()
+
+        gridSizer.Add(item = label,
+                      pos = (row, 0),
+                      flag  =  wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = projString,
+                      pos = (row, 1), span = (1, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # epsg file
+        row += 1
+        label = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                              label = _("EPSG file:"))
+        projFile = wx.TextCtrl(parent = panel, id = wx.ID_ANY,
+                               value  =  self.settings.Get(group = 'projection', key = 'statusbar', subkey = 'projFile'),
+                               name = "GetValue", size = (400, -1))
+        self.winId['projection:statusbar:projFile'] = projFile.GetId()
+        gridSizer.Add(item = label,
+                      pos = (row, 0),
+                      flag  =  wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = projFile,
+                      pos = (row, 1),
+                      flag  =  wx.ALIGN_CENTER_VERTICAL)
+        
+        # note + button
+        row += 1
+        note = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                             label = _("Load EPSG codes (be patient), enter EPSG code or "
+                                       "insert Proj.4 string directly."))
+        gridSizer.Add(item = note,
+                      span = (1, 2),
+                      pos = (row, 0))
+
+        row += 1
+        epsgLoad = wx.Button(parent = panel, id = wx.ID_ANY,
+                             label = _("&Load EPSG codes"))
+        gridSizer.Add(item = epsgLoad,
+                      flag = wx.ALIGN_RIGHT,
+                      pos = (row, 1))
+        
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 3)
+
+        #
+        # format
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Coordinates format"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(2)
+
+        row = 0
+        # ll format
+        ll = wx.RadioBox(parent = panel, id = wx.ID_ANY,
+                         label = " %s " % _("LL projections"),
+                         choices = ["DMS", "DEG"],
+                         name = "GetStringSelection")
+        self.winId['projection:format:ll'] = ll.GetId()
+        if self.settings.Get(group = 'projection', key = 'format', subkey = 'll') == 'DMS':
+            ll.SetSelection(0)
+        else:
+            ll.SetSelection(1)
+        
+        # precision
+        precision =  wx.SpinCtrl(parent = panel, id = wx.ID_ANY,
+                                 min = 0, max = 12,
+                                 name = "GetValue")
+        precision.SetValue(int(self.settings.Get(group = 'projection', key = 'format', subkey = 'precision')))
+        self.winId['projection:format:precision'] = precision.GetId()
+                
+        gridSizer.Add(item = ll,
+                      pos = (row, 0))
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Precision:")),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
+                      border = 20,
+                      pos = (row, 1))
+        gridSizer.Add(item = precision,
+                      flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 2))
+        
+        
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+        
+        panel.SetSizer(border)
+
+        # bindings
+        epsgLoad.Bind(wx.EVT_BUTTON, self.OnLoadEpsgCodes)
+        epsgCode.Bind(wx.EVT_COMBOBOX, self.OnSetEpsgCode)
+        epsgCode.Bind(wx.EVT_TEXT_ENTER, self.OnSetEpsgCode)
+        
+        return panel
+
+    def OnCheckColorTable(self, event):
+        """!Set/unset default color table"""
+        win = self.FindWindowById(self.winId['cmd:rasterColorTable:selection'])
+        if event.IsChecked():
+            win.Enable()
+        else:
+            win.Enable(False)
+        
+    def OnLoadEpsgCodes(self, event):
+        """!Load EPSG codes from the file"""
+        win = self.FindWindowById(self.winId['projection:statusbar:projFile'])
+        path = win.GetValue()
+
+        self.epsgCodeDict = ReadEpsgCodes(path)
+        list = self.FindWindowById(self.winId['projection:statusbar:epsg'])
+        if type(self.epsgCodeDict) == type(''):
+            wx.MessageBox(parent = self,
+                          message = _("Unable to read EPSG codes: %s") % self.epsgCodeDict,
+                          caption = _("Error"),  style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            self.epsgCodeDict = dict()
+            list.SetItems([])
+            list.SetValue('')
+            self.FindWindowById(self.winId['projection:statusbar:proj4']).SetValue('')
+            return
+        
+        choices = map(str, self.epsgCodeDict.keys())
+
+        list.SetItems(choices)
+        try:
+            code = int(list.GetValue())
+        except ValueError:
+            code = -1
+        win = self.FindWindowById(self.winId['projection:statusbar:proj4'])
+        if code in self.epsgCodeDict:
+            win.SetValue(self.epsgCodeDict[code][1])
+        else:
+            list.SetSelection(0)
+            code = int(list.GetStringSelection())
+            win.SetValue(self.epsgCodeDict[code][1])
+    
+    def OnSetEpsgCode(self, event):
+        """!EPSG code selected"""
+        winCode = self.FindWindowById(event.GetId())
+        win = self.FindWindowById(self.winId['projection:statusbar:proj4'])
+        if not self.epsgCodeDict:
+            wx.MessageBox(parent = self,
+                          message = _("EPSG code %s not found") % event.GetString(),
+                          caption = _("Error"),  style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            winCode.SetValue('')
+            win.SetValue('')
+        
+        try:
+            code = int(event.GetString())
+        except ValueError:
+            wx.MessageBox(parent = self,
+                          message = _("EPSG code %s not found") % str(code),
+                          caption = _("Error"),  style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            winCode.SetValue('')
+            win.SetValue('')
+        
+        
+        try:
+            win.SetValue(self.epsgCodeDict[code][1].replace('<>', '').strip())
+        except KeyError:
+            wx.MessageBox(parent = self,
+                          message = _("EPSG code %s not found") % str(code),
+                          caption = _("Error"),  style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            winCode.SetValue('')
+            win.SetValue('')
+        
+    def OnSetFont(self, event):
+        """'Set font' button pressed"""
+        dlg = DefaultFontDialog(parent = self,
+                                title = _('Select default display font'),
+                                style = wx.DEFAULT_DIALOG_STYLE,
+                                type = 'font')
+        
+        if dlg.ShowModal() == wx.ID_OK:
+            # set default font and encoding environmental variables
+            if dlg.font:
+                os.environ["GRASS_FONT"] = dlg.font
+                self.settings.Set(group = 'display', value = dlg.font,
+                                  key = 'font', subkey = 'type')
+
+            if dlg.encoding and \
+                    dlg.encoding != "ISO-8859-1":
+                os.environ["GRASS_ENCODING"] = dlg.encoding
+                self.settings.Set(group = 'display', value = dlg.encoding,
+                                  key = 'font', subkey = 'encoding')
+                
+        dlg.Destroy()
+        
+        event.Skip()
+
+    def OnSetOutputFont(self, event):
+        """'Set output font' button pressed
+        """
+        dlg = DefaultFontDialog(parent = self,
+                                title = _('Select output font'),
+                                style = wx.DEFAULT_DIALOG_STYLE,
+                                type = 'outputfont')
+        
+        if dlg.ShowModal() == wx.ID_OK:
+            # set output font and font size variables
+            if dlg.font:
+                self.settings.Set(group = 'appearance', value = dlg.font,
+                                  key = 'outputfont', subkey = 'type')
+                
+                self.settings.Set(group = 'appearance', value = dlg.fontsize,
+                                  key = 'outputfont', subkey = 'size')
+        
+# Standard font dialog broken for Mac in OS X 10.6
+#        type = self.settings.Get(group = 'display', key = 'outputfont', subkey = 'type')   
+                           
+#        size = self.settings.Get(group = 'display', key = 'outputfont', subkey = 'size')
+#        if size == None or size == 0: size = 10
+#        size = float(size)
+        
+#        data = wx.FontData()
+#        data.EnableEffects(True)
+#        data.SetInitialFont(wx.Font(pointSize = size, family = wx.FONTFAMILY_MODERN, faceName = type, style = wx.NORMAL, weight = 0))
+
+#        dlg = wx.FontDialog(self, data)
+
+#        if dlg.ShowModal() == wx.ID_OK:
+#            data = dlg.GetFontData()
+#            font = data.GetChosenFont()
+
+#            self.settings.Set(group = 'display', value = font.GetFaceName(),
+#                                  key = 'outputfont', subkey = 'type')
+#            self.settings.Set(group = 'display', value = font.GetPointSize(),
+#                                  key = 'outputfont', subkey = 'size')
+                
+        dlg.Destroy()
+
+        event.Skip()
+
+    def OnEnableWheelZoom(self, event):
+        """!Enable/disable wheel zoom mode control"""
+        checkId = self.winId['display:mouseWheelZoom:enabled']
+        choiceId = self.winId['display:mouseWheelZoom:selection']
+        enable = self.FindWindowById(checkId).IsChecked()
+        self.FindWindowById(choiceId).Enable(enable)
+        
+class DefaultFontDialog(wx.Dialog):
+    """
+    Opens a file selection dialog to select default font
+    to use in all GRASS displays
+    """
+    def __init__(self, parent, title, id = wx.ID_ANY,
+                 style = wx.DEFAULT_DIALOG_STYLE |
+                 wx.RESIZE_BORDER,
+                 settings = UserSettings,
+                 type = 'font'):
+        
+        self.settings = settings
+        self.type = type
+        
+        wx.Dialog.__init__(self, parent, id, title, style = style)
+
+        panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        self.fontlist = self.GetFonts()
+        
+        border = wx.BoxSizer(wx.VERTICAL)
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Font settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        gridSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+        gridSizer.AddGrowableCol(0)
+
+        label = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                              label = _("Select font:"))
+        gridSizer.Add(item = label,
+                      flag = wx.ALIGN_TOP,
+                      pos = (0,0))
+        
+        self.fontlb = wx.ListBox(parent = panel, id = wx.ID_ANY, pos = wx.DefaultPosition,
+                                 choices = self.fontlist,
+                                 style = wx.LB_SINGLE|wx.LB_SORT)
+        self.Bind(wx.EVT_LISTBOX, self.EvtListBox, self.fontlb)
+        self.Bind(wx.EVT_LISTBOX_DCLICK, self.EvtListBoxDClick, self.fontlb)
+
+        gridSizer.Add(item = self.fontlb,
+                flag = wx.EXPAND, pos = (1, 0))
+
+        if self.type == 'font':
+            if "GRASS_FONT" in os.environ:
+                self.font = os.environ["GRASS_FONT"]
+            else:
+                self.font = self.settings.Get(group = 'display',
+                                              key = 'font', subkey = 'type')
+            self.encoding = self.settings.Get(group = 'display',
+                                          key = 'font', subkey = 'encoding')
+
+            label = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                  label = _("Character encoding:"))
+            gridSizer.Add(item = label,
+                          flag = wx.ALIGN_CENTER_VERTICAL,
+                          pos = (2, 0))
+
+            self.textentry = wx.TextCtrl(parent = panel, id = wx.ID_ANY,
+                                         value = self.encoding)
+            gridSizer.Add(item = self.textentry,
+                    flag = wx.EXPAND, pos = (3, 0))
+
+            self.textentry.Bind(wx.EVT_TEXT, self.OnEncoding)
+
+        elif self.type == 'outputfont':
+            self.font = self.settings.Get(group = 'appearance',
+                                              key = 'outputfont', subkey = 'type')
+            self.fontsize = self.settings.Get(group = 'appearance',
+                                          key = 'outputfont', subkey = 'size')
+            label = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                              label = _("Font size:"))
+            gridSizer.Add(item = label,
+                      flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (2, 0))
+                      
+            self.spin = wx.SpinCtrl(parent = panel, id = wx.ID_ANY)
+            if self.fontsize:
+                self.spin.SetValue(int(self.fontsize))
+            self.spin.Bind(wx.EVT_SPINCTRL, self.OnSizeSpin)
+            self.spin.Bind(wx.EVT_TEXT, self.OnSizeSpin)
+            gridSizer.Add(item = self.spin,
+                      flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (3, 0))
+
+        else: 
+            return
+
+        if self.font:
+            self.fontlb.SetStringSelection(self.font, True)
+
+        sizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.EXPAND | wx.ALL,
+                  border = 5)
+
+        border.Add(item = sizer, proportion = 1,
+                   flag = wx.ALL | wx.EXPAND, border = 3)
+        
+        btnsizer = wx.StdDialogButtonSizer()
+
+        btn = wx.Button(parent = panel, id = wx.ID_OK)
+        btn.SetDefault()
+        btnsizer.AddButton(btn)
+
+        btn = wx.Button(parent = panel, id = wx.ID_CANCEL)
+        btnsizer.AddButton(btn)
+        btnsizer.Realize()
+
+        border.Add(item = btnsizer, proportion = 0,
+                   flag = wx.EXPAND | wx.ALIGN_RIGHT | wx.ALL, border = 5)
+        
+        panel.SetAutoLayout(True)
+        panel.SetSizer(border)
+        border.Fit(self)
+        
+        self.Layout()
+        
+    def EvtRadioBox(self, event):
+        if event.GetInt() == 0:
+            self.fonttype = 'grassfont'
+        elif event.GetInt() == 1:
+            self.fonttype = 'truetype'
+
+        self.fontlist = self.GetFonts(self.fonttype)
+        self.fontlb.SetItems(self.fontlist)
+
+    def OnEncoding(self, event):
+        self.encoding = event.GetString()
+
+    def EvtListBox(self, event):
+        self.font = event.GetString()
+        event.Skip()
+
+    def EvtListBoxDClick(self, event):
+        self.font = event.GetString()
+        event.Skip()
+        
+    def OnSizeSpin(self, event):
+        self.fontsize = self.spin.GetValue()
+        event.Skip()
+    
+    def GetFonts(self):
+        """
+        parses fonts directory or fretypecap file to get a list of fonts for the listbox
+        """
+        fontlist = []
+
+        ret = RunCommand('d.font',
+                         read = True,
+                         flags = 'l')
+
+        if not ret:
+            return fontlist
+
+        dfonts = ret.splitlines()
+        dfonts.sort(lambda x,y: cmp(x.lower(), y.lower()))
+        for item in range(len(dfonts)):
+           # ignore duplicate fonts and those starting with #
+           if not dfonts[item].startswith('#') and \
+                  dfonts[item] != dfonts[item-1]:
+              fontlist.append(dfonts[item])
+
+        return fontlist
+
+class MapsetAccess(wx.Dialog):
+    """!Controls setting options and displaying/hiding map overlay
+    decorations
+    """
+    def __init__(self, parent, id = wx.ID_ANY,
+                 title = _('Manage access to mapsets'),
+                 size  =  (350, 400),
+                 style  =  wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        
+        wx.Dialog.__init__(self, parent, id, title, size = size, style = style, **kwargs)
+
+        self.all_mapsets_ordered = ListOfMapsets(get = 'ordered')
+        self.accessible_mapsets  = ListOfMapsets(get = 'accessible')
+        self.curr_mapset = grass.gisenv()['MAPSET']
+
+        # make a checklistbox from available mapsets and check those that are active
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        label = wx.StaticText(parent = self, id = wx.ID_ANY,
+                              label = _("Check a mapset to make it accessible, uncheck it to hide it.\n"
+                                        "  Notes:\n"
+                                        "    - The current mapset is always accessible.\n"
+                                        "    - You may only write to the current mapset.\n"
+                                        "    - You may only write to mapsets which you own."))
+        
+        sizer.Add(item = label, proportion = 0,
+                  flag = wx.ALL, border = 5)
+
+        self.mapsetlb = CheckListMapset(parent = self)
+        self.mapsetlb.LoadData()
+        
+        sizer.Add(item = self.mapsetlb, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+
+        # check all accessible mapsets
+        for mset in self.accessible_mapsets:
+            self.mapsetlb.CheckItem(self.all_mapsets_ordered.index(mset), True)
+
+        # FIXME (howto?): grey-out current mapset
+        #self.mapsetlb.Enable(0, False)
+
+        # dialog buttons
+        line = wx.StaticLine(parent = self, id = wx.ID_ANY,
+                             style = wx.LI_HORIZONTAL)
+        sizer.Add(item = line, proportion = 0,
+                  flag = wx.EXPAND | wx.ALIGN_CENTRE | wx.ALL, border = 5)
+
+        btnsizer = wx.StdDialogButtonSizer()
+        okbtn = wx.Button(self, wx.ID_OK)
+        okbtn.SetDefault()
+        btnsizer.AddButton(okbtn)
+
+        cancelbtn = wx.Button(self, wx.ID_CANCEL)
+        btnsizer.AddButton(cancelbtn)
+        btnsizer.Realize()
+
+        sizer.Add(item = btnsizer, proportion = 0,
+                  flag = wx.EXPAND | wx.ALIGN_RIGHT | wx.ALL, border = 5)
+
+        # do layout
+        self.Layout()
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+
+        self.SetMinSize(size)
+        
+    def GetMapsets(self):
+        """!Get list of checked mapsets"""
+        ms = []
+        i = 0
+        for mset in self.all_mapsets_ordered:
+            if self.mapsetlb.IsChecked(i):
+                ms.append(mset)
+            i += 1
+
+        return ms
+
+class CheckListMapset(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin):
+    """!List of mapset/owner/group"""
+    def __init__(self, parent, pos = wx.DefaultPosition,
+                 log = None):
+        self.parent = parent
+        
+        wx.ListCtrl.__init__(self, parent, wx.ID_ANY,
+                             style = wx.LC_REPORT)
+        listmix.CheckListCtrlMixin.__init__(self)
+        self.log = log
+
+        # setup mixins
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+
+    def LoadData(self):
+        """!Load data into list"""
+        self.InsertColumn(0, _('Mapset'))
+        self.InsertColumn(1, _('Owner'))
+        ### self.InsertColumn(2, _('Group'))
+        gisenv = grass.gisenv()
+        locationPath = os.path.join(gisenv['GISDBASE'], gisenv['LOCATION_NAME'])
+
+        for mapset in self.parent.all_mapsets_ordered:
+            index = self.InsertStringItem(sys.maxint, mapset)
+            mapsetPath = os.path.join(locationPath,
+                                      mapset)
+            stat_info = os.stat(mapsetPath)
+            if havePwd:
+                self.SetStringItem(index, 1, "%s" % pwd.getpwuid(stat_info.st_uid)[0])
+                # FIXME: get group name
+                ### self.SetStringItem(index, 2, "%-8s" % stat_info.st_gid) 
+            else:
+                # FIXME: no pwd under MS Windows (owner: 0, group: 0)
+                self.SetStringItem(index, 1, "%-8s" % stat_info.st_uid)
+                ### self.SetStringItem(index, 2, "%-8s" % stat_info.st_gid)
+                
+        self.SetColumnWidth(col = 0, width = wx.LIST_AUTOSIZE)
+        ### self.SetColumnWidth(col = 1, width = wx.LIST_AUTOSIZE)
+        
+    def OnCheckItem(self, index, flag):
+        """!Mapset checked/unchecked"""
+        mapset = self.parent.all_mapsets_ordered[index]
+        if mapset == self.parent.curr_mapset:
+            self.CheckItem(index, True)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_core/prompt.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_core/prompt.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_core/prompt.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1158 @@
+"""!
+ at package gui_core.prompt
+
+ at brief wxGUI command prompt
+
+Classes:
+ - prompt::PromptListCtrl
+ - prompt::TextCtrlAutoComplete
+ - prompt::GPrompt
+ - prompt::GPromptPopUp
+ - prompt::GPromptSTC
+
+(C) 2009-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+ at author Michael Barton <michael.barton at asu.edu>
+ at author Vaclav Petras <wenzeslaus gmail.com> (copy&paste customization)
+"""
+
+import os
+import sys
+import difflib
+import codecs
+
+import wx
+import wx.stc
+import wx.lib.mixins.listctrl as listmix
+
+from grass.script import core as grass
+from grass.script import task as gtask
+
+from core          import globalvar
+from core          import utils
+from lmgr.menudata import ManagerData
+from core.gcmd     import EncodeString, DecodeString, GetRealCmd
+
+class PromptListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
+    """!PopUp window used by GPromptPopUp"""
+    def __init__(self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition,
+                 size = wx.DefaultSize, style = 0):
+        wx.ListCtrl.__init__(self, parent, id, pos, size, style)
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+        
+class TextCtrlAutoComplete(wx.ComboBox, listmix.ColumnSorterMixin):
+    """!Auto complete text area used by GPromptPopUp"""
+    def __init__ (self, parent, statusbar,
+                  id = wx.ID_ANY, choices = [], **kwargs):
+        """!Constructor works just like wx.TextCtrl except you can pass in a
+        list of choices.  You can also change the choice list at any time
+        by calling setChoices.
+        
+        Inspired by http://wiki.wxpython.org/TextCtrlAutoComplete
+        """
+        self.statusbar = statusbar
+        
+        if 'style' in kwargs:
+            kwargs['style'] = wx.TE_PROCESS_ENTER | kwargs['style']
+        else:
+            kwargs['style'] = wx.TE_PROCESS_ENTER
+        
+        wx.ComboBox.__init__(self, parent, id, **kwargs)
+        
+        # some variables
+        self._choices = choices
+        self._hideOnNoMatch = True
+        self._module = None      # currently selected module
+        self._choiceType = None  # type of choice (module, params, flags, raster, vector ...)
+        self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
+        self._historyItem = 0   # last item
+        
+        # sort variable needed by listmix
+        self.itemDataMap = dict()
+        
+        # widgets
+        try:
+            self.dropdown = wx.PopupWindow(self)
+        except NotImplementedError:
+            self.Destroy()
+            raise NotImplementedError
+        
+        # create the list and bind the events
+        self.dropdownlistbox = PromptListCtrl(parent = self.dropdown,
+                                              style = wx.LC_REPORT | wx.LC_SINGLE_SEL | \
+                                                  wx.LC_SORT_ASCENDING | wx.LC_NO_HEADER,
+                                              pos = wx.Point(0, 0))
+        
+        listmix.ColumnSorterMixin.__init__(self, 1)
+        
+        # set choices (list of GRASS modules)
+        self._choicesCmd = globalvar.grassCmd
+        self._choicesMap = dict()
+        for type in ('raster', 'vector'):
+            self._choicesMap[type] = grass.list_strings(type = type[:4])
+        # first search for GRASS module
+        self.SetChoices(self._choicesCmd)
+        
+        self.SetMinSize(self.GetSize())
+        
+        # bindings...
+        self.Bind(wx.EVT_KILL_FOCUS, self.OnControlChanged)
+        self.Bind(wx.EVT_TEXT, self.OnEnteredText)
+        self.Bind(wx.EVT_TEXT_ENTER, self.OnRunCmd)
+        self.Bind(wx.EVT_KEY_DOWN , self.OnKeyDown)
+        ### self.Bind(wx.EVT_LEFT_DOWN, self.OnClick)
+
+        # if need drop down on left click
+        self.dropdown.Bind(wx.EVT_LISTBOX , self.OnListItemSelected, self.dropdownlistbox)
+        self.dropdownlistbox.Bind(wx.EVT_LEFT_DOWN, self.OnListClick)
+        self.dropdownlistbox.Bind(wx.EVT_LEFT_DCLICK, self.OnListDClick)
+        self.dropdownlistbox.Bind(wx.EVT_LIST_COL_CLICK, self.OnListColClick)
+
+        self.Bind(wx.EVT_COMBOBOX, self.OnCommandSelect)
+        
+    def _updateDataList(self, choices):
+        """!Update data list"""
+        # delete, if need, all the previous data
+        if self.dropdownlistbox.GetColumnCount() != 0:
+            self.dropdownlistbox.DeleteAllColumns()
+            self.dropdownlistbox.DeleteAllItems()
+        # and update the dict
+        if choices:
+            for numVal, data in enumerate(choices):
+                self.itemDataMap[numVal] = data
+        else:
+            numVal = 0
+        self.SetColumnCount(numVal)
+    
+    def _setListSize(self):
+        """!Set list size"""
+        choices = self._choices
+        longest = 0
+        for choice in choices:
+            longest = max(len(choice), longest)
+        longest += 3
+        itemcount = min(len( choices ), 7) + 2
+        charheight = self.dropdownlistbox.GetCharHeight()
+        charwidth = self.dropdownlistbox.GetCharWidth()
+        self.popupsize = wx.Size(charwidth*longest, charheight*itemcount)
+        self.dropdownlistbox.SetSize(self.popupsize)
+        self.dropdown.SetClientSize(self.popupsize)
+        
+    def _showDropDown(self, show = True):
+        """!Either display the drop down list (show = True) or hide it
+        (show = False).
+        """
+        if show:
+            size = self.dropdown.GetSize()
+            width, height = self.GetSizeTuple()
+            x, y = self.ClientToScreenXY(0, height)
+            if size.GetWidth() != width:
+                size.SetWidth(width)
+                self.dropdown.SetSize(size)
+                self.dropdownlistbox.SetSize(self.dropdown.GetClientSize())
+            if (y + size.GetHeight()) < self._screenheight:
+                self.dropdown.SetPosition(wx.Point(x, y))
+            else:
+                self.dropdown.SetPosition(wx.Point(x, y - height - size.GetHeight()))
+        
+        self.dropdown.Show(show)
+    
+    def _listItemVisible(self):
+        """!Moves the selected item to the top of the list ensuring it is
+        always visible.
+        """
+        toSel = self.dropdownlistbox.GetFirstSelected()
+        if toSel == -1:
+            return
+        self.dropdownlistbox.EnsureVisible(toSel)
+
+    def _setModule(self, name):
+        """!Set module's choices (flags, parameters)""" 
+        # get module's description
+        if name in self._choicesCmd and not self._module:
+            try:
+                self._module = gtask.parse_interface(name)
+            except IOError:
+                self._module = None
+             
+        # set choices (flags)
+        self._choicesMap['flag'] = self._module.get_list_flags()
+        for idx in range(len(self._choicesMap['flag'])):
+            item = self._choicesMap['flag'][idx]
+            desc = self._module.get_flag(item)['label']
+            if not desc:
+                desc = self._module.get_flag(item)['description']
+            
+            self._choicesMap['flag'][idx] = '%s (%s)' % (item, desc)
+        
+        # set choices (parameters)
+        self._choicesMap['param'] = self._module.get_list_params()
+        for idx in range(len(self._choicesMap['param'])):
+            item = self._choicesMap['param'][idx]
+            desc = self._module.get_param(item)['label']
+            if not desc:
+                desc = self._module.get_param(item)['description']
+            
+            self._choicesMap['param'][idx] = '%s (%s)' % (item, desc)
+    
+    def _setValueFromSelected(self):
+         """!Sets the wx.TextCtrl value from the selected wx.ListCtrl item.
+         Will do nothing if no item is selected in the wx.ListCtrl.
+         """
+         sel = self.dropdownlistbox.GetFirstSelected()
+         if sel < 0:
+             return
+         
+         if self._colFetch != -1:
+             col = self._colFetch
+         else:
+             col = self._colSearch
+         itemtext = self.dropdownlistbox.GetItem(sel, col).GetText()
+         
+         cmd = utils.split(str(self.GetValue()))
+         if len(cmd) > 0 and cmd[0] in self._choicesCmd:
+             # -> append text (skip last item)
+             if self._choiceType == 'param':
+                 itemtext = itemtext.split(' ')[0]
+                 self.SetValue(' '.join(cmd) + ' ' + itemtext + '=')
+                 optType = self._module.get_param(itemtext)['prompt']
+                 if optType in ('raster', 'vector'):
+                     # -> raster/vector map
+                     self.SetChoices(self._choicesMap[optType], optType)
+             elif self._choiceType == 'flag':
+                 itemtext = itemtext.split(' ')[0]
+                 if len(itemtext) > 1:
+                     prefix = '--'
+                 else:
+                     prefix = '-'
+                 self.SetValue(' '.join(cmd[:-1]) + ' ' + prefix + itemtext)
+             elif self._choiceType in ('raster', 'vector'):
+                 self.SetValue(' '.join(cmd[:-1]) + ' ' + cmd[-1].split('=', 1)[0] + '=' + itemtext)
+         else:
+             # -> reset text
+             self.SetValue(itemtext + ' ')
+             
+             # define module
+             self._setModule(itemtext)
+             
+             # use parameters as default choices
+             self._choiceType = 'param'
+             self.SetChoices(self._choicesMap['param'], type = 'param')
+         
+         self.SetInsertionPointEnd()
+         
+         self._showDropDown(False)
+         
+    def GetListCtrl(self):
+        """!Method required by listmix.ColumnSorterMixin"""
+        return self.dropdownlistbox
+    
+    def SetChoices(self, choices, type = 'module'):
+        """!Sets the choices available in the popup wx.ListBox.
+        The items will be sorted case insensitively.
+
+        @param choices list of choices
+        @param type type of choices (module, param, flag, raster, vector)
+        """
+        self._choices = choices
+        self._choiceType = type
+        
+        self.dropdownlistbox.SetWindowStyleFlag(wx.LC_REPORT | wx.LC_SINGLE_SEL |
+                                                wx.LC_SORT_ASCENDING | wx.LC_NO_HEADER)
+        if not isinstance(choices, list):
+            self._choices = [ x for x in choices ]
+        if self._choiceType not in ('raster', 'vector'):
+            # do not sort raster/vector maps
+            utils.ListSortLower(self._choices)
+        
+        self._updateDataList(self._choices)
+        
+        self.dropdownlistbox.InsertColumn(0, "")
+        for num, colVal in enumerate(self._choices):
+            index = self.dropdownlistbox.InsertImageStringItem(sys.maxint, colVal, -1)
+            self.dropdownlistbox.SetStringItem(index, 0, colVal)
+            self.dropdownlistbox.SetItemData(index, num)
+        self._setListSize()
+        
+        # there is only one choice for both search and fetch if setting a single column:
+        self._colSearch = 0
+        self._colFetch = -1
+
+    def OnClick(self, event):
+        """Left mouse button pressed"""
+        sel = self.dropdownlistbox.GetFirstSelected()
+        if not self.dropdown.IsShown():
+            if sel > -1:
+                self.dropdownlistbox.Select(sel)
+            else:
+                self.dropdownlistbox.Select(0)
+            self._listItemVisible()
+            self._showDropDown()
+        else:
+            self.dropdown.Hide()
+        
+    def OnCommandSelect(self, event):
+        """!Command selected from history"""
+        self._historyItem = event.GetSelection() - len(self.GetItems())
+        self.SetFocus()
+        
+    def OnListClick(self, evt):
+        """!Left mouse button pressed"""
+        toSel, flag = self.dropdownlistbox.HitTest( evt.GetPosition() )
+        #no values on poition, return
+        if toSel == -1: return
+        self.dropdownlistbox.Select(toSel)
+
+    def OnListDClick(self, evt):
+        """!Mouse button double click"""
+        self._setValueFromSelected()
+
+    def OnListColClick(self, evt):
+        """!Left mouse button pressed on column"""
+        col = evt.GetColumn()
+        # reverse the sort
+        if col == self._colSearch:
+            self._ascending = not self._ascending
+        self.SortListItems( evt.GetColumn(), ascending=self._ascending )
+        self._colSearch = evt.GetColumn()
+        evt.Skip()
+
+    def OnListItemSelected(self, event):
+        """!Item selected"""
+        self._setValueFromSelected()
+        event.Skip()
+
+    def OnEnteredText(self, event):
+        """!Text entered"""
+        text = event.GetString()
+        
+        if not text:
+            # control is empty; hide dropdown if shown:
+            if self.dropdown.IsShown():
+                self._showDropDown(False)
+            event.Skip()
+            return
+        
+        try:
+            cmd = utils.split(str(text))
+        except ValueError, e:
+            self.statusbar.SetStatusText(str(e))
+            cmd = text.split(' ')
+        pattern = str(text)
+        
+        if len(cmd) > 0 and cmd[0] in self._choicesCmd and not self._module:
+            self._setModule(cmd[0])
+        elif len(cmd) > 1 and cmd[0] in self._choicesCmd:
+            if self._module:
+                if len(cmd[-1].split('=', 1)) == 1:
+                    # new option
+                    if cmd[-1][0] == '-':
+                        # -> flags
+                        self.SetChoices(self._choicesMap['flag'], type = 'flag')
+                        pattern = cmd[-1].lstrip('-')
+                    else:
+                        # -> options
+                        self.SetChoices(self._choicesMap['param'], type = 'param')
+                        pattern = cmd[-1]
+                else:
+                    # value
+                    pattern = cmd[-1].split('=', 1)[1]
+        else:
+            # search for GRASS modules
+            if self._module:
+                # -> switch back to GRASS modules list
+                self.SetChoices(self._choicesCmd)
+                self._module = None
+                self._choiceType = None
+        
+        self._choiceType
+        self._choicesMap
+        found = False
+        choices = self._choices
+        for numCh, choice in enumerate(choices):
+            if choice.lower().startswith(pattern):
+                found = True
+            if found:
+                self._showDropDown(True)
+                item = self.dropdownlistbox.GetItem(numCh)
+                toSel = item.GetId()
+                self.dropdownlistbox.Select(toSel)
+                break
+        
+        if not found:
+            self.dropdownlistbox.Select(self.dropdownlistbox.GetFirstSelected(), False)
+            if self._hideOnNoMatch:
+                self._showDropDown(False)
+                if self._module and '=' not in cmd[-1]:
+                    message = ''
+                    if cmd[-1][0] == '-': # flag
+                        message = _("Warning: flag <%(flag)s> not found in '%(module)s'") % \
+                            { 'flag' : cmd[-1][1:], 'module' : self._module.name }
+                    else: # option
+                        message = _("Warning: option <%(param)s> not found in '%(module)s'") % \
+                            { 'param' : cmd[-1], 'module' : self._module.name }
+                    self.statusbar.SetStatusText(message)
+        
+        if self._module and len(cmd[-1]) == 2 and cmd[-1][-2] == '=':
+            optType = self._module.get_param(cmd[-1][:-2])['prompt']
+            if optType in ('raster', 'vector'):
+                # -> raster/vector map
+                self.SetChoices(self._choicesMap[optType], optType)
+        
+        self._listItemVisible()
+        
+        event.Skip()
+        
+    def OnKeyDown (self, event):
+        """!Do some work when the user press on the keys: up and down:
+        move the cursor left and right: move the search
+        """
+        skip = True
+        sel = self.dropdownlistbox.GetFirstSelected()
+        visible = self.dropdown.IsShown()
+        KC = event.GetKeyCode()
+        
+        if KC == wx.WXK_RIGHT:
+            # right -> show choices
+            if sel < (self.dropdownlistbox.GetItemCount() - 1):
+                self.dropdownlistbox.Select(sel + 1)
+                self._listItemVisible()
+            self._showDropDown()
+            skip = False
+        elif KC == wx.WXK_UP:
+            if visible:
+                if sel > 0:
+                    self.dropdownlistbox.Select(sel - 1)
+                    self._listItemVisible()
+                self._showDropDown()
+                skip = False
+            else:
+                self._historyItem -= 1
+                try:
+                    self.SetValue(self.GetItems()[self._historyItem])
+                except IndexError:
+                    self._historyItem += 1
+        elif KC == wx.WXK_DOWN:
+            if visible:
+                if sel < (self.dropdownlistbox.GetItemCount() - 1):
+                    self.dropdownlistbox.Select(sel + 1)
+                    self._listItemVisible()
+                self._showDropDown()
+                skip = False
+            else:
+                if self._historyItem < -1:
+                    self._historyItem += 1
+                    self.SetValue(self.GetItems()[self._historyItem])
+        
+        if visible:
+            if event.GetKeyCode() == wx.WXK_RETURN:
+                self._setValueFromSelected()
+                skip = False
+            if event.GetKeyCode() == wx.WXK_ESCAPE:
+                self._showDropDown(False)
+                skip = False
+        if skip:
+            event.Skip()
+        
+    def OnControlChanged(self, event):
+        """!Control changed"""
+        if self.IsShown():
+            self._showDropDown(False)
+        
+        event.Skip()
+
+class GPrompt(object):
+    """!Abstract class for interactive wxGUI prompt
+
+    See subclass GPromptPopUp and GPromptSTC.
+    """
+    def __init__(self, parent):
+        self.parent = parent                 # GMConsole
+        self.panel  = self.parent.GetPanel()
+        
+        if self.parent.parent.GetName() not in ("LayerManager", "Modeler"):
+            self.standAlone = True
+        else:
+            self.standAlone = False
+        
+        # dictionary of modules (description, keywords, ...)
+        if not self.standAlone:
+            if self.parent.parent.GetName() == 'Modeler':
+                self.moduleDesc = ManagerData().GetModules()
+            else:
+                self.moduleDesc = parent.parent.menubar.GetData().GetModules()
+            self.moduleList = self._getListOfModules()
+            self.mapList = self._getListOfMaps()
+            self.mapsetList = utils.ListOfMapsets()
+        else:
+            self.moduleDesc = self.moduleList = self.mapList = None
+        
+        # auto complete items
+        self.autoCompList   = list()
+        self.autoCompFilter = None
+        
+        # command description (gtask.grassTask)
+        self.cmdDesc = None
+        self.cmdbuffer = self._readHistory()
+        self.cmdindex = len(self.cmdbuffer)
+        
+    def _readHistory(self):
+        """!Get list of commands from history file"""
+        hist = list()
+        env = grass.gisenv()
+        try:
+            fileHistory = codecs.open(os.path.join(env['GISDBASE'],
+                                                   env['LOCATION_NAME'],
+                                                   env['MAPSET'],
+                                                   '.bash_history'),
+                                      encoding = 'utf-8', mode = 'r', errors='replace')
+        except IOError:
+            return hist
+        
+        try:
+            for line in fileHistory.readlines():
+                hist.append(line.replace('\n', ''))
+        finally:
+            fileHistory.close()
+        
+        return hist
+
+    def GetCommandDesc(self, cmd):
+        """!Get description for given command"""
+        if cmd in self.moduleDesc:
+            return self.moduleDesc[cmd]['desc']
+        
+        return ''
+    
+    def GetCommandItems(self):
+        """!Get list of available commands"""
+        items = list()
+        
+        if self.autoCompFilter is not None:
+            mList = self.autoCompFilter
+        else:
+            mList = self.moduleList
+            
+        if not mList:
+            return items
+        
+        prefixes = mList.keys()
+        prefixes.sort()
+        
+        for prefix in prefixes:
+            for command in mList[prefix]:
+                name = prefix + '.' + command
+                if name not in items:
+                    items.append(name)
+                
+        items.sort()
+        
+        return items
+    
+    def _getListOfModules(self):
+        """!Get list of modules"""
+        result = dict()
+        for module in globalvar.grassCmd:
+            try:
+                group, name = module.split('.',1)
+            except ValueError:
+                continue # TODO
+            
+            if group not in result:
+                result[group] = list()
+            result[group].append(name)
+            
+            # for better auto-completion: 
+            # not only result['r']={...,'colors.out',...}, but also result['r.colors']={'out',...}
+            for i in range(len(name.split('.'))-1):
+                group = '.'.join([group,name.split('.',1)[0]])
+                name = name.split('.',1)[1]
+                if group not in result:
+                    result[group] = list()
+                result[group].append(name)
+      
+        # sort list of names
+        for group in result.keys():
+            result[group].sort()
+        
+        return result
+    
+    def _getListOfMaps(self):
+        """!Get list of maps"""
+        result = dict()
+        result['raster'] = grass.list_strings('rast')
+        result['vector'] = grass.list_strings('vect')
+        
+        return result
+
+    def OnRunCmd(self, event):
+        """!Run command"""
+        cmdString = event.GetString()
+        
+        if self.standAlone:
+            return
+        
+        if cmdString[:2] == 'd.' and not self.parent.curr_page:
+            self.parent.NewDisplay(show=True)
+        
+        cmd = utils.split(cmdString)
+        if len(cmd) > 1:
+            self.parent.RunCmd(cmd, switchPage = True)
+        else:
+            self.parent.RunCmd(cmd, switchPage = False)
+        
+        self.OnUpdateStatusBar(None)
+        
+    def OnUpdateStatusBar(self, event):
+        """!Update Layer Manager status bar"""
+        if self.standAlone:
+            return
+        
+        if event is None:
+            self.parent.parent.statusbar.SetStatusText("")
+        else:
+            self.parent.parent.statusbar.SetStatusText(_("Type GRASS command and run by pressing ENTER"))
+            event.Skip()
+        
+    def GetPanel(self):
+        """!Get main widget panel"""
+        return self.panel
+
+    def GetInput(self):
+        """!Get main prompt widget"""
+        return self.input
+    
+    def SetFilter(self, data, module = True):
+        """!Set filter
+
+        @param data data dict
+        @param module True to filter modules, otherwise data
+        """
+        if module:
+            if data:
+                self.moduleList = data
+            else:
+                self.moduleList = self._getListOfModules()
+        else:
+            if data:
+                self.dataList = data
+            else:
+                self.dataList = self._getListOfMaps()
+        
+class GPromptPopUp(GPrompt, TextCtrlAutoComplete):
+    """!Interactive wxGUI prompt - popup version"""
+    def __init__(self, parent):
+        GPrompt.__init__(self, parent)
+        
+        ### todo: fix TextCtrlAutoComplete to work also on Macs
+        ### reason: missing wx.PopupWindow()
+        try:
+            TextCtrlAutoComplete.__init__(self, parent = self.panel, id = wx.ID_ANY,
+                                          value = "",
+                                          style = wx.TE_LINEWRAP | wx.TE_PROCESS_ENTER,
+                                          statusbar = self.parent.parent.statusbar)
+            self.SetItems(self._readHistory())
+        except NotImplementedError:
+            # wx.PopupWindow may be not available in wxMac
+            # see http://trac.wxwidgets.org/ticket/9377
+            wx.TextCtrl.__init__(parent = self.panel, id = wx.ID_ANY,
+                                 value = "",
+                                 style=wx.TE_LINEWRAP | wx.TE_PROCESS_ENTER,
+                                 size = (-1, 25))
+            self.searchBy.Enable(False)
+            self.search.Enable(False)
+        
+        self.SetFont(wx.Font(10, wx.FONTFAMILY_MODERN, wx.NORMAL, wx.NORMAL, 0, ''))
+        
+        wx.CallAfter(self.SetInsertionPoint, 0)
+        
+        # bidnings
+        self.Bind(wx.EVT_TEXT_ENTER, self.OnRunCmd)
+        self.Bind(wx.EVT_TEXT,       self.OnUpdateStatusBar)
+        
+    def OnCmdErase(self, event):
+        """!Erase command prompt"""
+        self.input.SetValue('')
+
+class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
+    """!Styled wxGUI prompt with autocomplete and calltips"""    
+    def __init__(self, parent, id = wx.ID_ANY, margin = False):
+        GPrompt.__init__(self, parent)
+        wx.stc.StyledTextCtrl.__init__(self, self.panel, id)
+        
+        #
+        # styles
+        #                
+        self.SetWrapMode(True)
+        self.SetUndoCollection(True)        
+        
+        #
+        # create command and map lists for autocompletion
+        #
+        self.AutoCompSetIgnoreCase(False) 
+        
+        #
+        # line margins
+        #
+        # TODO print number only from cmdlog
+        self.SetMarginWidth(1, 0)
+        self.SetMarginWidth(2, 0)
+        if margin:
+            self.SetMarginType(0, wx.stc.STC_MARGIN_NUMBER)
+            self.SetMarginWidth(0, 30)
+        else:
+            self.SetMarginWidth(0, 0)
+        
+        #
+        # miscellaneous
+        #
+        self.SetViewWhiteSpace(False)
+        self.SetUseTabs(False)
+        self.UsePopUp(True)
+        self.SetSelBackground(True, "#FFFF00")
+        self.SetUseHorizontalScrollBar(True)
+        
+        #
+        # bindings
+        #
+        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
+        self.Bind(wx.stc.EVT_STC_AUTOCOMP_SELECTION, self.OnItemSelected)
+        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemChanged)
+        
+    def OnTextSelectionChanged(self, event):
+        """!Copy selected text to clipboard and skip event.
+        The same function is in GMStc class (goutput.py).
+        """
+        self.Copy()
+        event.Skip()
+        
+    def OnItemChanged(self, event):
+        """!Change text in statusbar 
+        if the item selection in the auto-completion list is changed"""
+        # list of commands
+        if self.toComplete['entity'] == 'command':
+            item = self.toComplete['cmd'].rpartition('.')[0] + '.' + self.autoCompList[event.GetIndex()] 
+            try:
+                desc = self.moduleDesc[item]['desc']        
+            except KeyError:
+                desc = '' 
+            self.ShowStatusText(desc)
+        # list of flags    
+        elif self.toComplete['entity'] == 'flags':
+            desc = self.cmdDesc.get_flag(self.autoCompList[event.GetIndex()])['description']
+            self.ShowStatusText(desc)
+        # list of parameters
+        elif self.toComplete['entity'] == 'params':
+            item = self.cmdDesc.get_param(self.autoCompList[event.GetIndex()])
+            desc = item['name'] + '=' + item['type']
+            if not item['required']:
+                desc = '[' + desc + ']'
+            desc += ': ' + item['description']
+            self.ShowStatusText(desc)
+        # list of flags and commands       
+        elif self.toComplete['entity'] == 'params+flags':
+            if self.autoCompList[event.GetIndex()][0] == '-':
+                desc = self.cmdDesc.get_flag(self.autoCompList[event.GetIndex()].strip('-'))['description']
+            else:
+                item = self.cmdDesc.get_param(self.autoCompList[event.GetIndex()])
+                desc = item['name'] + '=' + item['type']
+                if not item['required']:
+                    desc = '[' + desc + ']'
+                desc += ': ' + item['description']
+            self.ShowStatusText(desc)
+        else:
+            self.ShowStatusText('')
+            
+    def OnItemSelected(self, event):
+        """!Item selected from the list"""
+        lastWord = self.GetWordLeft()
+        # to insert selection correctly if selected word partly matches written text
+        match = difflib.SequenceMatcher(None, event.GetText(), lastWord)
+        matchTuple = match.find_longest_match(0, len(event.GetText()), 0, len(lastWord))
+    
+        compl = event.GetText()[matchTuple[2]:]
+        text = self.GetTextLeft() + compl
+        # add space or '=' at the end
+        end = '='
+        for char in ('.','-','='):
+            if text.split(' ')[-1].find(char) >= 0:
+                end = ' '
+        
+        compl += end
+        text += end
+
+        self.AddText(compl)
+        pos = len(text)
+        self.SetCurrentPos(pos)
+        
+        cmd = text.strip().split(' ')[0]
+        
+        if not self.cmdDesc or cmd != self.cmdDesc.get_name():
+            if cmd in ('r.mapcalc', 'v.type'):
+                cmd = cmd + '_wrapper'
+            
+            if cmd in ('r.mapcalc', 'r3.mapcalc') and \
+                    self.parent.parent.GetName() == 'LayerManager':
+                self.parent.parent.OnMapCalculator(event = None, cmd = [cmd])
+                # add command to history & clean prompt
+                self.UpdateCmdHistory([cmd])
+                self.OnCmdErase(None)
+            else:
+                try:
+                    self.cmdDesc = gtask.parse_interface(GetRealCmd(cmd))
+                except IOError:
+                    self.cmdDesc = None
+        
+    def UpdateCmdHistory(self, cmd):
+        """!Update command history
+        
+        @param cmd command given as a list
+        """
+        # add command to history    
+        self.cmdbuffer.append(' '.join(cmd))
+        
+        # keep command history to a managable size
+        if len(self.cmdbuffer) > 200:
+            del self.cmdbuffer[0]
+        self.cmdindex = len(self.cmdbuffer)
+        
+    def EntityToComplete(self):
+        """!Determines which part of command (flags, parameters) should
+        be completed at current cursor position"""
+        entry = self.GetTextLeft()
+        toComplete = dict()
+        try:
+            cmd = entry.split()[0].strip()
+        except IndexError:
+            return None
+        
+        if len(utils.split(str(entry))) > 1:
+            if cmd in globalvar.grassCmd:
+                toComplete['cmd'] = cmd
+                if entry[-1] == ' ':
+                    words = entry.split(' ')
+                    if any(word.startswith('-') for word in words):
+                        toComplete['entity'] = 'params'
+                    else:
+                        toComplete['entity'] = 'params+flags'
+                else:
+                    # get word left from current position
+                    word = self.GetWordLeft(withDelimiter = True)
+                    
+                    if word[0] == '=' and word[-1] == '@':
+                        toComplete['entity'] = 'mapsets'
+                    elif word[0] == '=':
+                        # get name of parameter
+                        paramName = self.GetWordLeft(withDelimiter = False, ignoredDelimiter = '=').strip('=')
+                        if paramName:
+                            try:
+                                param = self.cmdDesc.get_param(paramName)
+                            except (ValueError, AttributeError):
+                                return None
+                        else:
+                            return None
+                        
+                        if param['values']:
+                            toComplete['entity'] = 'param values'
+                        elif param['prompt'] == 'raster' and param['element'] == 'cell':
+                            toComplete['entity'] = 'raster map'
+                        elif param['prompt'] == 'vector' and param['element'] == 'vector':
+                            toComplete['entity'] = 'vector map'
+                    elif word[0] == '-':
+                        toComplete['entity'] = 'flags'
+                    elif word[0] == ' ':
+                        toComplete['entity'] = 'params'
+            else:
+                return None
+        else:
+            toComplete['entity'] = 'command'
+            toComplete['cmd'] = cmd
+        
+        return toComplete
+    
+    def GetWordLeft(self, withDelimiter = False, ignoredDelimiter = None):
+        """!Get word left from current cursor position. The beginning
+        of the word is given by space or chars: .,-= 
+        
+        @param withDelimiter returns the word with the initial delimeter
+        @param ignoredDelimiter finds the word ignoring certain delimeter
+        """
+        textLeft = self.GetTextLeft()
+        
+        parts = list()
+        if ignoredDelimiter is None:
+            ignoredDelimiter = ''
+        
+        for char in set(' .,-=') - set(ignoredDelimiter):
+            if not withDelimiter:
+                delimiter = ''
+            else:
+                delimiter = char
+            parts.append(delimiter + textLeft.rpartition(char)[2])
+        return min(parts, key=lambda x: len(x))
+         
+    def ShowList(self):
+        """!Show sorted auto-completion list if it is not empty"""
+        if len(self.autoCompList) > 0:
+            self.autoCompList.sort()
+            self.AutoCompShow(lenEntered = 0, itemList = ' '.join(self.autoCompList))    
+        
+    def OnKeyPressed(self, event):
+        """!Key press capture for autocompletion, calltips, and command history
+
+        @todo event.ControlDown() for manual autocomplete
+        """
+        # keycodes used: "." = 46, "=" = 61, "-" = 45 
+        pos = self.GetCurrentPos()
+        # complete command after pressing '.'
+        if event.GetKeyCode() == 46 and not event.ShiftDown():
+            self.autoCompList = list()
+            entry = self.GetTextLeft()
+            self.InsertText(pos, '.')
+            self.CharRight()
+            self.toComplete = self.EntityToComplete()
+            try:
+                if self.toComplete['entity'] == 'command': 
+                    self.autoCompList = self.moduleList[entry.strip()]
+            except (KeyError, TypeError):
+                return
+            self.ShowList()
+
+        # complete flags after pressing '-'       
+        elif event.GetKeyCode() == 45 and not event.ShiftDown(): 
+            self.autoCompList = list()
+            entry = self.GetTextLeft()
+            self.InsertText(pos, '-')
+            self.CharRight()
+            self.toComplete = self.EntityToComplete()
+            if self.toComplete['entity'] == 'flags' and self.cmdDesc:
+                if self.GetTextLeft()[-2:] == ' -': # complete e.g. --quite
+                    for flag in self.cmdDesc.get_options()['flags']:
+                        if len(flag['name']) == 1:
+                            self.autoCompList.append(flag['name'])
+                else:
+                    for flag in self.cmdDesc.get_options()['flags']:
+                        if len(flag['name']) > 1:
+                            self.autoCompList.append(flag['name'])            
+            self.ShowList()
+            
+        # complete map or values after parameter
+        elif event.GetKeyCode() == 61 and not event.ShiftDown():
+            self.autoCompList = list()
+            self.InsertText(pos, '=')
+            self.CharRight()
+            self.toComplete = self.EntityToComplete()
+            if self.toComplete and 'entity' in self.toComplete:
+                if self.toComplete['entity'] == 'raster map':
+                    self.autoCompList = self.mapList['raster']
+                elif self.toComplete['entity'] == 'vector map':
+                    self.autoCompList = self.mapList['vector']
+                elif self.toComplete['entity'] == 'param values':
+                    param = self.GetWordLeft(withDelimiter = False, ignoredDelimiter='=').strip(' =')
+                    self.autoCompList = self.cmdDesc.get_param(param)['values']
+            self.ShowList()
+        
+        # complete mapset ('@')
+        elif event.GetKeyCode() == 50 and event.ShiftDown():
+            self.autoCompList = list()
+            self.InsertText(pos, '@')
+            self.CharRight()
+            self.toComplete = self.EntityToComplete()
+            
+            if self.toComplete and self.toComplete['entity'] == 'mapsets':
+                self.autoCompList = self.mapsetList
+            self.ShowList()
+            
+        # complete after pressing CTRL + Space          
+        elif event.GetKeyCode() == wx.WXK_SPACE and event.ControlDown():
+            self.autoCompList = list()
+            self.toComplete = self.EntityToComplete()
+            if self.toComplete is None:
+                return 
+
+            #complete command
+            if self.toComplete['entity'] == 'command':
+                for command in globalvar.grassCmd:
+                    if command.find(self.toComplete['cmd']) == 0:
+                        dotNumber = list(self.toComplete['cmd']).count('.') 
+                        self.autoCompList.append(command.split('.',dotNumber)[-1])
+                
+            
+            # complete flags in such situations (| is cursor):
+            # r.colors -| ...w, q, l
+            # r.colors -w| ...w, q, l  
+            elif self.toComplete['entity'] == 'flags' and self.cmdDesc:
+                for flag in self.cmdDesc.get_options()['flags']:
+                    if len(flag['name']) == 1:
+                        self.autoCompList.append(flag['name'])
+                    
+            # complete parameters in such situations (| is cursor):
+            # r.colors -w | ...color, map, rast, rules
+            # r.colors col| ...color
+            elif self.toComplete['entity'] == 'params' and self.cmdDesc:
+                for param in self.cmdDesc.get_options()['params']:
+                    if param['name'].find(self.GetWordLeft(withDelimiter=False)) == 0:
+                        self.autoCompList.append(param['name'])           
+            
+            # complete flags or parameters in such situations (| is cursor):
+            # r.colors | ...-w, -q, -l, color, map, rast, rules
+            # r.colors color=grey | ...-w, -q, -l, color, map, rast, rules
+            elif self.toComplete['entity'] == 'params+flags' and self.cmdDesc:
+                self.autoCompList = list()
+                
+                for param in self.cmdDesc.get_options()['params']:
+                    self.autoCompList.append(param['name'])
+                for flag in self.cmdDesc.get_options()['flags']:
+                    if len(flag['name']) == 1:
+                        self.autoCompList.append('-' + flag['name'])
+                    else:
+                        self.autoCompList.append('--' + flag['name'])
+                    
+                self.ShowList() 
+                   
+            # complete map or values after parameter  
+            # r.buffer input=| ...list of raster maps
+            # r.buffer units=| ... feet, kilometers, ...   
+            elif self.toComplete['entity'] == 'raster map':
+                self.autoCompList = list()
+                self.autoCompList = self.mapList['raster']
+            elif self.toComplete['entity'] == 'vector map':
+                self.autoCompList = list()
+                self.autoCompList = self.mapList['vector']
+            elif self.toComplete['entity'] == 'param values':
+                self.autoCompList = list()
+                param = self.GetWordLeft(withDelimiter = False, ignoredDelimiter='=').strip(' =')
+                self.autoCompList = self.cmdDesc.get_param(param)['values']
+                
+            self.ShowList()
+
+        elif event.GetKeyCode() == wx.WXK_TAB:
+            # show GRASS command calltips (to hide press 'ESC')
+            entry = self.GetTextLeft()
+            try:
+                cmd = entry.split()[0].strip()
+            except IndexError:
+                cmd = ''
+            
+            if cmd not in globalvar.grassCmd:
+                return
+            
+            info = gtask.command_info(GetRealCmd(cmd))
+            
+            self.CallTipSetBackground("#f4f4d1")
+            self.CallTipSetForeground("BLACK")
+            self.CallTipShow(pos, info['usage'] + '\n\n' + info['description'])
+            
+            
+        elif event.GetKeyCode() in [wx.WXK_UP, wx.WXK_DOWN] and \
+                 not self.AutoCompActive():
+            # Command history using up and down   
+            if len(self.cmdbuffer) < 1:
+                return
+            
+            self.DocumentEnd()
+            
+            # move through command history list index values
+            if event.GetKeyCode() == wx.WXK_UP:
+                self.cmdindex = self.cmdindex - 1
+            if event.GetKeyCode() == wx.WXK_DOWN:
+                self.cmdindex = self.cmdindex + 1
+            if self.cmdindex < 0:
+                self.cmdindex = 0
+            if self.cmdindex > len(self.cmdbuffer) - 1:
+                self.cmdindex = len(self.cmdbuffer) - 1
+            
+            try:
+                txt = self.cmdbuffer[self.cmdindex]
+            except:
+                txt = ''
+            
+            # clear current line and insert command history    
+            self.DelLineLeft()
+            self.DelLineRight()
+            pos = self.GetCurrentPos()            
+            self.InsertText(pos,txt)
+            self.LineEnd()
+            self.parent.parent.statusbar.SetStatusText('')
+            
+        elif event.GetKeyCode() in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER] and \
+                self.AutoCompActive() == False:
+            # run command on line when <return> is pressed
+            
+            if self.parent.GetName() == "ModelerDialog":
+                self.parent.OnOk(None)
+                return
+            
+            # find the command to run
+            line = self.GetCurLine()[0].strip()
+            if len(line) == 0:
+                return
+            
+            # parse command into list
+            try:
+                cmd = utils.split(str(line))
+            except UnicodeError:
+                cmd = utils.split(EncodeString((line)))
+            cmd = map(DecodeString, cmd)
+            
+            #  send the command list to the processor 
+            if cmd[0] in ('r.mapcalc', 'r3.mapcalc') and len(cmd) == 1:
+                self.parent.parent.OnMapCalculator(event = None, cmd = cmd)
+            else:
+                self.parent.RunCmd(cmd)
+            
+            # add command to history & clean prompt
+            self.UpdateCmdHistory(cmd)
+            self.OnCmdErase(None)
+            self.parent.parent.statusbar.SetStatusText('')
+            
+        elif event.GetKeyCode() == wx.WXK_SPACE:
+            items = self.GetTextLeft().split()
+            if len(items) == 1:
+                cmd = items[0].strip()
+                if cmd in globalvar.grassCmd and \
+                        cmd != 'r.mapcalc' and \
+                        (not self.cmdDesc or cmd != self.cmdDesc.get_name()):
+                    
+                    try:
+                        self.cmdDesc = gtask.parse_interface(GetRealCmd(cmd))
+                    except IOError:
+                        self.cmdDesc = None
+            event.Skip()
+        
+        else:
+            event.Skip()
+
+    def ShowStatusText(self, text):
+        """!Sets statusbar text, if it's too long, it is cut off"""
+        maxLen = self.parent.parent.statusbar.GetFieldRect(0).GetWidth()/ 7 # any better way?
+        if len(text) < maxLen:
+            self.parent.parent.statusbar.SetStatusText(text)
+        else:
+            self.parent.parent.statusbar.SetStatusText(text[:maxLen]+'...')
+        
+        
+    def GetTextLeft(self):
+        """!Returns all text left of the caret"""
+        pos = self.GetCurrentPos()
+        self.HomeExtend()
+        entry = self.GetSelectedText()
+        self.SetCurrentPos(pos)
+        
+        return entry
+    
+    def OnDestroy(self, event):
+        """!The clipboard contents can be preserved after
+        the app has exited"""
+        wx.TheClipboard.Flush()
+        event.Skip()
+
+    def OnCmdErase(self, event):
+        """!Erase command prompt"""
+        self.Home()
+        self.DelLineRight()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_core/toolbars.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_core/toolbars.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_core/toolbars.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,233 @@
+"""!
+ at package gui_core.toolbars
+
+ at brief Base classes toolbar widgets
+
+Classes:
+ - toolbars::BaseToolbar
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+import platform
+
+import wx
+
+from core               import globalvar
+from core.debug         import Debug
+from icons.icon         import MetaIcon
+
+BaseIcons = {
+    'display'    : MetaIcon(img = 'show',
+                            label = _('Display map'),
+                            desc  =  _('Re-render modified map layers only')),
+    'render'     : MetaIcon(img = 'layer-redraw',
+                            label = _('Render map'),
+                            desc = _('Force re-rendering all map layers')),
+    'erase'      : MetaIcon(img = 'erase',
+                            label = _('Erase display'),
+                            desc = _('Erase display canvas with given background color')),
+    'pointer'    : MetaIcon(img = 'pointer',
+                            label = _('Pointer')),
+    'zoomIn'     : MetaIcon(img = 'zoom-in',
+                            label = _('Zoom in'),
+                            desc = _('Drag or click mouse to zoom')),
+    'zoomOut'    : MetaIcon(img = 'zoom-out',
+                            label = _('Zoom out'),
+                            desc = _('Drag or click mouse to unzoom')),
+    'zoomBack'   : MetaIcon(img = 'zoom-last',
+                            label = _('Return to previous zoom')),
+    'zoomMenu'   : MetaIcon(img = 'zoom-more',
+                            label = _('Various zoom options'),
+                            desc = _('Zoom to computational, default, saved region, ...')),
+    'zoomExtent' : MetaIcon(img = 'zoom-extent',
+                            label = _('Zoom to selected map layer(s)')),
+    'pan'        : MetaIcon(img = 'pan',
+                            label = _('Pan'),
+                            desc = _('Drag with mouse to pan')),
+    'saveFile'   : MetaIcon(img = 'map-export',
+                            label = _('Save display to graphic file')),
+    'print'      : MetaIcon(img = 'print',
+                            label = _('Print display')),
+    'font'       : MetaIcon(img = 'font',
+                            label = _('Select font')),
+    'help'       : MetaIcon(img = 'help',
+                            label = _('Show manual')),
+    'quit'       : MetaIcon(img = 'quit',
+                            label = _('Quit')),
+    'addRast'    : MetaIcon(img = 'layer-raster-add',
+                            label = _('Add raster map layer')),
+    'addVect'    : MetaIcon(img = 'layer-vector-add',
+                            label = _('Add vector map layer')),
+    'overlay'    : MetaIcon(img = 'overlay-add',
+                            label = _('Add map elements'),
+                            desc = _('Overlay elements like scale and legend onto map')),
+    'histogramD' : MetaIcon(img = 'layer-raster-histogram',
+                            label = _('Create histogram with d.histogram')),
+    'settings'   : MetaIcon(img = 'settings',
+                            label = _("Settings")),
+    }
+    
+class BaseToolbar(wx.ToolBar):
+    """!Abstract toolbar class"""
+    def __init__(self, parent):
+        self.parent = parent
+        wx.ToolBar.__init__(self, parent = self.parent, id = wx.ID_ANY)
+        
+        self.action = dict()
+        
+        self.Bind(wx.EVT_TOOL, self.OnTool)
+        
+        self.SetToolBitmapSize(globalvar.toolbarSize)
+        
+    def InitToolbar(self, toolData):
+        """!Initialize toolbar, add tools to the toolbar
+        """
+        for tool in toolData:
+            self.CreateTool(*tool)
+        
+        self._data = toolData
+        
+    def _toolbarData(self):
+        """!Toolbar data (virtual)"""
+        return None
+    
+    def CreateTool(self, label, bitmap, kind,
+                   shortHelp, longHelp, handler, pos = -1):
+        """!Add tool to the toolbar
+        
+        @param pos if -1 add tool, if > 0 insert at given pos
+        @return id of tool
+        """
+        bmpDisabled = wx.NullBitmap
+        tool = -1
+        if label: 
+            tool = vars(self)[label] = wx.NewId()
+            Debug.msg(3, "CreateTool(): tool=%d, label=%s bitmap=%s" % \
+                          (tool, label, bitmap))
+            if pos < 0:
+                toolWin = self.AddLabelTool(tool, label, bitmap,
+                                            bmpDisabled, kind,
+                                            shortHelp, longHelp)
+            else:
+                toolWin = self.InsertLabelTool(pos, tool, label, bitmap,
+                                            bmpDisabled, kind,
+                                            shortHelp, longHelp)
+            self.Bind(wx.EVT_TOOL, handler, toolWin)
+        else: # separator
+            self.AddSeparator()
+        
+        return tool
+    
+    def EnableLongHelp(self, enable = True):
+        """!Enable/disable long help
+        
+        @param enable True for enable otherwise disable
+        """
+        for tool in self._data:
+            if tool[0] == '': # separator
+                continue
+            
+            if enable:
+                self.SetToolLongHelp(vars(self)[tool[0]], tool[4])
+            else:
+                self.SetToolLongHelp(vars(self)[tool[0]], "")
+        
+    def OnTool(self, event):
+        """!Tool selected
+        """
+        if self.parent.GetName() == "GCPFrame":
+            return
+        
+        if hasattr(self.parent, 'toolbars'):
+            if self.parent.GetToolbar('vdigit'):
+                # update vdigit toolbar (unselect currently selected tool)
+                id = self.parent.toolbars['vdigit'].GetAction(type = 'id')
+                self.parent.toolbars['vdigit'].ToggleTool(id, False)
+        
+        if event:
+            # deselect previously selected tool
+            id = self.action.get('id', -1)
+            if id != event.GetId():
+                self.ToggleTool(self.action['id'], False)
+            else:
+                self.ToggleTool(self.action['id'], True)
+            
+            self.action['id'] = event.GetId()
+            
+            event.Skip()
+        else:
+            # initialize toolbar
+            self.ToggleTool(self.action['id'], True)
+        
+    def GetAction(self, type = 'desc'):
+        """!Get current action info"""
+        return self.action.get(type, '')
+    
+    def SelectDefault(self, event):
+        """!Select default tool"""
+        self.ToggleTool(self.defaultAction['id'], True)
+        self.defaultAction['bind'](event)
+        self.action = { 'id' : self.defaultAction['id'],
+                        'desc' : self.defaultAction.get('desc', '') }
+        
+    def FixSize(self, width):
+        """!Fix toolbar width on Windows
+            
+        @todo Determine why combobox causes problems here
+        """
+        if platform.system() == 'Windows':
+            size = self.GetBestSize()
+            self.SetSize((size[0] + width, size[1]))
+
+    def Enable(self, tool, enable = True):
+        """!Enable defined tool
+        
+        @param tool name
+        @param enable True to enable otherwise disable tool
+        """
+        try:
+            id = getattr(self, tool)
+        except AttributeError:
+            return
+        
+        self.EnableTool(id, enable)
+        
+    def _getToolbarData(self, data):
+        """!Define tool
+        """
+        retData = list()
+        for args in data:
+            retData.append(self._defineTool(*args))
+        return retData
+
+    def _defineTool(self, name = None, icon = None, handler = None, item = wx.ITEM_NORMAL, pos = -1):
+        """!Define tool
+        """
+        if name:
+            return (name, icon.GetBitmap(),
+                    item, icon.GetLabel(), icon.GetDesc(),
+                    handler, pos)
+        return ("", "", "", "", "", "") # separator
+
+    def _onMenu(self, data):
+        """!Toolbar pop-up menu"""
+        menu = wx.Menu()
+        
+        for icon, handler in data:
+            item = wx.MenuItem(menu, wx.ID_ANY, icon.GetLabel())
+            item.SetBitmap(icon.GetBitmap(self.parent.iconsize))
+            menu.AppendItem(item)
+            self.Bind(wx.EVT_MENU, handler, item)
+        
+        self.PopupMenu(menu)
+        menu.Destroy()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/gui_core/widgets.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/gui_core/widgets.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/gui_core/widgets.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,470 @@
+"""!
+ at package gui_core.widgets
+
+ at brief Core GUI widgets
+
+Classes:
+ - widgets::GNotebook
+ - widgets::ScrolledPanel
+ - widgets::NumTextCtrl
+ - widgets::FloatSlider
+ - widgets::SymbolButton
+ - widgets::StaticWrapText
+ - widgets::BaseValidator
+ - widgets::IntegerValidator
+ - widgets::FloatValidator
+ - widgets::ItemTree
+
+(C) 2008-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
+ at author Enhancements by Michael Barton <michael.barton asu.edu>
+ at author Anna Kratochvilova <kratochanna gmail.com> (Google SoC 2011)
+"""
+
+import os
+import string
+
+import wx
+import wx.lib.scrolledpanel as SP
+try:
+    import wx.lib.agw.flatnotebook   as FN
+except ImportError:
+    import wx.lib.flatnotebook   as FN
+try:
+    from wx.lib.buttons import ThemedGenBitmapTextButton as BitmapTextButton
+except ImportError: # not sure about TGBTButton version
+    from wx.lib.buttons import GenBitmapTextButton as BitmapTextButton
+try:
+    import wx.lib.agw.customtreectrl as CT
+except ImportError:
+    import wx.lib.customtreectrl as CT
+
+from core        import globalvar
+from core.debug  import Debug
+
+from wx.lib.newevent import NewEvent
+wxSymbolSelectionChanged, EVT_SYMBOL_SELECTION_CHANGED  = NewEvent()
+
+class GNotebook(FN.FlatNotebook):
+    """!Generic notebook widget
+    """
+    def __init__(self, parent, style, **kwargs):
+        if globalvar.hasAgw:
+            FN.FlatNotebook.__init__(self, parent, id = wx.ID_ANY, agwStyle = style, **kwargs)
+        else:
+            FN.FlatNotebook.__init__(self, parent, id = wx.ID_ANY, style = style, **kwargs)
+        
+        self.notebookPages = {}
+            
+    def AddPage(self, **kwargs):
+        """!Add a page
+        """
+        if 'name' in kwargs:
+            self.notebookPages[kwargs['name']] = kwargs['page']
+            del kwargs['name']
+        super(GNotebook, self).AddPage(**kwargs)
+        
+    def InsertPage(self, **kwargs):
+        """!Insert a new page
+        """
+        if 'name' in kwargs:
+            self.notebookPages[kwargs['name']] = kwargs['page']
+            del kwargs['name']
+        super(GNotebook, self).InsertPage(**kwargs)
+        
+    def SetSelectionByName(self, page):
+        """!Set notebook
+        
+        @param page names, eg. 'layers', 'output', 'search', 'pyshell', 'nviz'
+        """
+        idx = self.GetPageIndexByName(page)
+        if self.GetSelection() != idx:
+            self.SetSelection(idx)
+        
+    def GetPageIndexByName(self, page):
+        """!Get notebook page index
+        
+        @param page name
+        """
+        if page not in self.notebookPages:
+            return -1
+        
+        return self.GetPageIndex(self.notebookPages[page])
+
+class ScrolledPanel(SP.ScrolledPanel):
+    """!Custom ScrolledPanel to avoid strange behaviour concerning focus"""
+    def __init__(self, parent, style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER):
+        SP.ScrolledPanel.__init__(self, parent = parent, id = wx.ID_ANY, style = style)
+
+    def OnChildFocus(self, event):
+        pass
+
+class NumTextCtrl(wx.TextCtrl):
+    """!Class derived from wx.TextCtrl for numerical values only"""
+    def __init__(self, parent,  **kwargs):
+##        self.precision = kwargs.pop('prec')
+        wx.TextCtrl.__init__(self, parent = parent,
+            validator = NTCValidator(flag = 'DIGIT_ONLY'), **kwargs)
+        
+            
+    def SetValue(self, value):
+        super(NumTextCtrl, self).SetValue( str(value))
+        
+    def GetValue(self):
+        val = super(NumTextCtrl, self).GetValue()
+        if val == '':
+            val = '0'
+        try:
+            return float(val)
+        except ValueError:
+            val = ''.join(''.join(val.split('-')).split('.'))
+            return float(val)
+        
+    def SetRange(self, min, max):
+        pass
+   
+class FloatSlider(wx.Slider):
+    """!Class derived from wx.Slider for floats"""
+    def __init__(self, **kwargs):
+        Debug.msg(1, "FloatSlider.__init__()")
+        wx.Slider.__init__(self, **kwargs)
+        self.coef = 1.
+        #init range
+        self.minValueOrig = 0
+        self.maxValueOrig = 1
+        
+    def SetValue(self, value):
+        value *= self.coef 
+        if abs(value) < 1 and value != 0:
+            while abs(value) < 1:
+                value *= 100
+                self.coef *= 100
+            super(FloatSlider, self).SetRange(self.minValueOrig * self.coef, self.maxValueOrig * self.coef)
+        super(FloatSlider, self).SetValue(value)
+        
+        Debug.msg(4, "FloatSlider.SetValue(): value = %f" % value)
+        
+    def SetRange(self, minValue, maxValue):
+        self.coef = 1.
+        self.minValueOrig = minValue
+        self.maxValueOrig = maxValue
+        if abs(minValue) < 1 or abs(maxValue) < 1:
+            while (abs(minValue) < 1 and minValue != 0) or (abs(maxValue) < 1 and maxValue != 0):
+                minValue *= 100
+                maxValue *= 100
+                self.coef *= 100
+            super(FloatSlider, self).SetValue(super(FloatSlider, self).GetValue() * self.coef)
+        super(FloatSlider, self).SetRange(minValue, maxValue)
+        Debug.msg(4, "FloatSlider.SetRange(): minValue = %f, maxValue = %f" % (minValue, maxValue))
+            
+    def GetValue(self):
+        val = super(FloatSlider, self).GetValue()
+        Debug.msg(4, "FloatSlider.GetValue(): value = %f" % (val/self.coef))
+        return val/self.coef
+           
+class SymbolButton(BitmapTextButton):
+    """!Button with symbol and label."""
+    def __init__(self, parent, usage, label, **kwargs):
+        """!Constructor
+        
+        @param parent parent (usually wx.Panel)
+        @param usage determines usage and picture
+        @param label displayed label
+        """
+        size = (15, 15)
+        buffer = wx.EmptyBitmap(*size)
+        BitmapTextButton.__init__(self, parent = parent, label = " " + label, bitmap = buffer, **kwargs)
+        
+        dc = wx.MemoryDC()
+        dc.SelectObject(buffer)
+        maskColor = wx.Color(255, 255, 255)
+        dc.SetBrush(wx.Brush(maskColor))
+        dc.Clear()
+        
+        if usage == 'record':
+            self.DrawRecord(dc, size)
+        elif usage == 'stop':
+            self.DrawStop(dc, size)
+        elif usage == 'play':
+            self.DrawPlay(dc, size)
+        elif usage == 'pause':
+            self.DrawPause(dc, size)
+
+        buffer.SetMaskColour(maskColor)
+        self.SetBitmapLabel(buffer)
+        dc.SelectObject(wx.NullBitmap)
+        
+    def DrawRecord(self, dc, size):
+        """!Draw record symbol"""
+        dc.SetBrush(wx.Brush(wx.Color(255, 0, 0)))
+        dc.DrawCircle(size[0]/2, size[1] / 2, size[0] / 2)
+        
+    def DrawStop(self, dc, size):
+        """!Draw stop symbol"""
+        dc.SetBrush(wx.Brush(wx.Color(50, 50, 50)))
+        dc.DrawRectangle(0, 0, size[0], size[1])
+        
+    def DrawPlay(self, dc, size):
+        """!Draw play symbol"""
+        dc.SetBrush(wx.Brush(wx.Color(0, 255, 0)))
+        points = (wx.Point(0, 0), wx.Point(0, size[1]), wx.Point(size[0], size[1] / 2))
+        dc.DrawPolygon(points)
+        
+    def DrawPause(self, dc, size):
+        """!Draw pause symbol"""
+        dc.SetBrush(wx.Brush(wx.Color(50, 50, 50)))
+        dc.DrawRectangle(0, 0, 2 * size[0] / 5, size[1])
+        dc.DrawRectangle(3 * size[0] / 5, 0, 2 * size[0] / 5, size[1])
+
+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 - 50,
+                              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 BaseValidator(wx.PyValidator):
+    def __init__(self):
+        wx.PyValidator.__init__(self)
+        
+        self.Bind(wx.EVT_TEXT, self.OnText) 
+
+    def OnText(self, event):
+        """!Do validation"""
+        self.Validate()
+        
+        event.Skip()
+        
+    def Validate(self):
+        """Validate input"""
+        textCtrl = self.GetWindow()
+        text = textCtrl.GetValue()
+
+        if text:
+            try:
+                self.type(text)
+            except ValueError:
+                textCtrl.SetBackgroundColour("grey")
+                textCtrl.SetFocus()
+                textCtrl.Refresh()
+                return False
+        
+        sysColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)
+        textCtrl.SetBackgroundColour(sysColor)
+        
+        textCtrl.Refresh()
+        
+        return True
+
+    def TransferToWindow(self):
+        return True # Prevent wxDialog from complaining.
+    
+    def TransferFromWindow(self):
+        return True # Prevent wxDialog from complaining.
+
+class IntegerValidator(BaseValidator):
+    """!Validator for floating-point input"""
+    def __init__(self):
+        BaseValidator.__init__(self)
+        self.type = int
+        
+    def Clone(self):
+        """!Clone validator"""
+        return IntegerValidator()
+
+class FloatValidator(BaseValidator):
+    """!Validator for floating-point input"""
+    def __init__(self):
+        BaseValidator.__init__(self)
+        self.type = float
+        
+    def Clone(self):
+        """!Clone validator"""
+        return FloatValidator()
+
+class NTCValidator(wx.PyValidator):
+    """!validates input in textctrls, taken from wxpython demo"""
+    def __init__(self, flag = None):
+        wx.PyValidator.__init__(self)
+        self.flag = flag
+        self.Bind(wx.EVT_CHAR, self.OnChar)
+
+    def Clone(self):
+        return NTCValidator(self.flag)
+
+    def OnChar(self, event):
+        key = event.GetKeyCode()
+        if key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255:
+            event.Skip()
+            return
+        if self.flag == 'DIGIT_ONLY' and chr(key) in string.digits + '.-':
+            event.Skip()
+            return
+        if not wx.Validator_IsSilent():
+            wx.Bell()
+        # Returning without calling even.Skip eats the event before it
+        # gets to the text control
+        return  
+    
+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 element in data 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 SingleSymbolPanel(wx.Panel):
+    """!Panel for displaying one symbol.
+    
+    Changes background when selected. Assumes that parent will catch
+    events emitted on mouse click. Used in gui_core::dialog::SymbolDialog.
+    """
+    def __init__(self, parent, symbolPath):
+        """!Panel constructor
+        
+        @param parent parent (gui_core::dialog::SymbolDialog)
+        @param symbolPath absolute path to symbol
+        """
+        wx.Panel.__init__(self, parent, id = wx.ID_ANY, style = wx.BORDER_RAISED)
+        self.SetName(os.path.splitext(os.path.basename(symbolPath))[0])
+        self.sBmp = wx.StaticBitmap(self, wx.ID_ANY, wx.Bitmap(symbolPath))
+
+        self.selected = False
+        self.selectColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT)
+        self.deselectColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)
+        
+        sizer = wx.BoxSizer()
+        sizer.Add(item = self.sBmp, proportion = 0, flag = wx.ALL | wx.ALIGN_CENTER, border = 5)
+        self.SetBackgroundColour(self.deselectColor)
+        self.SetMinSize(self.GetBestSize())
+        self.SetSizerAndFit(sizer)
+        
+        # binding to both (staticBitmap, Panel) necessary
+        self.sBmp.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
+        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
+        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
+        self.sBmp.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
+        
+    def OnLeftDown(self, event):
+        """!Panel selected, background changes"""
+        self.selected = True
+        self.SetBackgroundColour(self.selectColor)
+        event.Skip()
+        
+        event = wxSymbolSelectionChanged(name = self.GetName(), doubleClick = False)
+        wx.PostEvent(self.GetParent(), event)
+        
+    def OnDoubleClick(self, event):
+        event = wxSymbolSelectionChanged(name = self.GetName(), doubleClick = True)
+        wx.PostEvent(self.GetParent(), event)
+        
+    def Deselect(self):
+        """!Panel deselected, background changes back to default"""
+        self.selected = False
+        self.SetBackgroundColour(self.deselectColor)
+        
+    def Select(self):
+        """!Select panel, no event emitted"""
+        self.selected = True
+        self.SetBackgroundColour(self.selectColor)
+        


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

Modified: grass/branches/releasebranch_6_4/gui/wxpython/icons/grass2_icons.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/icons/grass2_icons.py	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/icons/grass2_icons.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -7,7 +7,7 @@
 
 import os
 
-from gui_modules import globalvar
+from core import globalvar
 
 iconPath = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass2")
 

Modified: grass/branches/releasebranch_6_4/gui/wxpython/icons/grass_icons.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/icons/grass_icons.py	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/icons/grass_icons.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -4,7 +4,7 @@
 
 import os
 
-from gui_modules import globalvar
+from core import globalvar
 
 iconPath = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass")
 iconPathVDigit  = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass", "edit")

Modified: grass/branches/releasebranch_6_4/gui/wxpython/icons/icon.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/icons/icon.py	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/icons/icon.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -1,33 +1,28 @@
 """!
- at package icon
+ at package icons.icon
 
- at brief Icon themes
+ at brief Icon metadata
 
- at code
-from icons import Icons as Icons
- at endcode
-
 Classes:
  - MetaIcon
 
 (C) 2007-2008, 2010-2011 by the GRASS Development Team
-This program is free software under the GNU General Public
-License (>=v2). Read the file COPYING that comes with GRASS
-for details.
 
+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>
- at author Anna Kratochvilova <anna.kratochvilova fsv.cvut.cz>
+ at author Anna Kratochvilova <kratochanna gmail.com>
 """
 
 import os
 import sys
 import types
+import copy
 
-sys.path.append(os.path.join(os.getenv("GISBASE"), "etc", "wxpython", "gui_modules"))
-
 import wx
 
-from gui_modules.preferences import globalSettings as UserSettings
+from core.settings import UserSettings
 
 import grass2_icons # default icon set
 iconPathDefault = grass2_icons.iconPath
@@ -83,14 +78,15 @@
                     iconSet[key] = os.path.join(iconPathVDigit, img)
                 else:
                     iconSet[key] = os.path.join(iconPath, img)
+
 except StandardError, e:
     sys.exit(_("Unable to load icon theme. Reason: %s") % e)
 
 class MetaIcon:
     """!Handle icon metadata (image path, tooltip, ...)
     """
-    def __init__(self, img, label, desc = None):
-        self.imagepath = img
+    def __init__(self, img, label = None, desc = None):
+        self.imagepath = iconSet.get(img, wx.ART_MISSING_IMAGE)
         if not self.imagepath:
             self.type = 'unknown'
         else:
@@ -107,11 +103,9 @@
             self.description = ''
         
     def __str__(self):
-        """!Debugging"""
         return "label=%s, img=%s, type=%s" % (self.label, self.imagepath, self.type)
 
     def GetBitmap(self, size = None):
-        """!Get bitmap"""
         bmp = None
         
         if self.type == 'wx':
@@ -129,347 +123,24 @@
     
     def GetLabel(self):
         return self.label
-    
+
     def GetDesc(self):
         return self.description
     
     def GetImageName(self):
         return os.path.basename(self.imagepath)
 
-#
-# create list of icon instances
-#
-Icons = {
-    'displayWindow' : {
-        'display'    : MetaIcon(img = iconSet.get('show', wx.ART_ERROR),
-                                label = _('Display map'),
-                                desc  =  _('Re-render modified map layers only')),
-        'render'     : MetaIcon(img = iconSet.get('layer-redraw', wx.ART_ERROR),
-                                label = _('Render map'),
-                                desc = _('Force re-rendering all map layers')),
-        'erase'      : MetaIcon(img = iconSet.get('erase', wx.ART_ERROR),
-                                label = _('Erase display'),
-                                desc = _('Erase display canvas with given background color')),
-        'pointer'    : MetaIcon(img = iconSet.get('pointer', wx.ART_ERROR),
-                                label = _('Pointer')),
-        'zoomIn'     : MetaIcon(img = iconSet.get('zoom-in', wx.ART_ERROR),
-                                label = _('Zoom in'),
-                                desc = _('Drag or click mouse to zoom')),
-        'zoomOut'    : MetaIcon(img = iconSet.get('zoom-out', wx.ART_ERROR),
-                                label = _('Zoom out'),
-                                desc = _('Drag or click mouse to unzoom')),
-        'pan'        : MetaIcon(img = iconSet.get('pan', wx.ART_ERROR),
-                                label = _('Pan'),
-                                desc = _('Drag with mouse to pan')),
-        'query'      : MetaIcon(img = iconSet.get('info', wx.ART_ERROR),
-                                label = _('Query raster/vector map(s)'),
-                                desc = _('Query selected raster/vector map(s)')),
-        'zoomBack'   : MetaIcon(img = iconSet.get('zoom-last', wx.ART_ERROR),
-                                label = _('Return to previous zoom')),
-        'zoomMenu'   : MetaIcon(img = iconSet.get('zoom-more', wx.ART_ERROR),
-                                label = _('Various zoom options'),
-                                desc = _('Zoom to computational, default, saved region, ...')),
-        'zoomExtent' : MetaIcon(img = iconSet.get('zoom-extent', wx.ART_ERROR),
-                                label = _('Zoom to selected map layer(s)')),
-        'overlay'    : MetaIcon(img = iconSet.get('overlay-add', wx.ART_ERROR),
-                                label = _('Add map elements'),
-                                desc = _('Overlay elements like scale and legend onto map')),
-        'addBarscale': MetaIcon(img = iconSet.get('scalebar-add', wx.ART_ERROR),
-                                label = _('Add scalebar and north arrow')),
-        'addLegend'  : MetaIcon(img = iconSet.get('legend-add', wx.ART_ERROR),
-                                label = _('Add legend')),
-        'saveFile'   : MetaIcon(img = iconSet.get('map-export', wx.ART_ERROR),
-                                label = _('Save display to graphic file')),
-        'print'      : MetaIcon(img = iconSet.get('print', wx.ART_ERROR),
-                                label = _('Print display')),
-        'analyze'    : MetaIcon(img = iconSet.get('layer-raster-analyze', wx.ART_ERROR),
-                                label = _('Analyze map'),
-                                desc = _('Measuring, profiling, histogramming, ...')),
-        'measure'    : MetaIcon(img = iconSet.get('measure-length', wx.ART_ERROR),
-                                label = _('Measure distance')),
-        'profile'    : MetaIcon(img = iconSet.get('layer-raster-profile', wx.ART_ERROR),
-                                label = _('Profile surface map')),
-        'addText'    : MetaIcon(img = iconSet.get('text-add', wx.ART_ERROR),
-                                label = _('Add text layer')),
-        'histogram'  : MetaIcon(img = iconSet.get('layer-raster-histogram', wx.ART_ERROR),
-                                label = _('Create histogram of raster map')),
-        },
-    'layerManager' : {
-        'newdisplay'   : MetaIcon(img = iconSet.get('monitor-create', wx.ART_ERROR),
-                                  label = _('Start new map display')),
-        'workspaceNew'  : MetaIcon(img = iconSet.get('create', wx.ART_ERROR),
-                                   label = _('Create new workspace (Ctrl+N)')),
-        'workspaceOpen' : MetaIcon(img = iconSet.get('open', wx.ART_ERROR),
-                                   label = _('Open existing workspace file (Ctrl+O)')),
-        'workspaceSave' : MetaIcon(img = iconSet.get('save', wx.ART_ERROR),
-                                   label = _('Save current workspace to file (Ctrl+S)')),
-        'addMulti'      : MetaIcon(img = iconSet.get('layer-open', wx.ART_ERROR),
-                                   label = _('Add multiple raster or vector map layers (Ctrl+Shift+L)')),
-        'import'        : MetaIcon(img = iconSet.get('layer-import', wx.ART_ERROR),
-                                   label = _('Import/link raster or vector data')),
-        'rastImport' : MetaIcon(img = iconSet.get('layer-import', wx.ART_ERROR),
-                                label = _('Import raster data')),
-        'rastLink'   : MetaIcon(img = iconSet.get('layer-import', wx.ART_ERROR),
-                                label = _('Link external raster data')),
-        'vectImport' : MetaIcon(img = iconSet.get('layer-import', wx.ART_ERROR),
-                                label = _('Import vector data')),
-        'vectLink'   : MetaIcon(img = iconSet.get('layer-import', wx.ART_ERROR),
-                                label = _('Link external vector data')),
-        'addRast'    : MetaIcon(img = iconSet.get('layer-raster-add', wx.ART_ERROR),
-                                label = _('Add raster map layer (Ctrl+Shift+R)')),
-        'rastMisc'   : MetaIcon(img = iconSet.get('layer-raster-more', wx.ART_ERROR),
-                                label = _('Add various raster map layers (RGB, HIS, shaded relief...)')),
-        'addVect'    : MetaIcon(img = iconSet.get('layer-vector-add', wx.ART_ERROR),
-                                label = _('Add vector map layer (Ctrl+Shift+V)')),
-        'vectMisc'   : MetaIcon(img = iconSet.get('layer-vector-more', wx.ART_ERROR),
-                                label = _('Add various vector map layers (thematic, chart...)')),
-        'addCmd'     : MetaIcon(img = iconSet.get('layer-command-add', wx.ART_ERROR),
-                                label = _('Add command layer')),
-        'addGroup'   : MetaIcon(img = iconSet.get('layer-group-add', wx.ART_ERROR),
-                                label = _('Add group')),
-        'addOverlay' : MetaIcon(img = iconSet.get('layer-more', wx.ART_ERROR),
-                                label = _('Add grid or vector labels overlay')),
-        'delCmd'     : MetaIcon(img = iconSet.get('layer-remove', wx.ART_ERROR),
-                                label = _('Delete selected map layer')),
-        'quit'       : MetaIcon(img = iconSet.get('quit', wx.ART_ERROR),
-                                label = _('Quit')),
-        'attrTable'  : MetaIcon(img = iconSet.get('table', wx.ART_ERROR),
-                                label = _('Show attribute table')),
-        'vdigit'     : MetaIcon(img = iconSet.get('edit', wx.ART_ERROR),
-                                label = _('Edit vector maps')),
-        'addRgb'     : MetaIcon(img = iconSet.get('layer-rgb-add', wx.ART_ERROR),
-                                label = _('Add RGB map layer')),
-        'addHis'     : MetaIcon(img = iconSet.get('layer-his-add', wx.ART_ERROR),
-                                label = _('Add HIS map layer')),
-        'addShaded'  : MetaIcon(img = iconSet.get('layer-shaded-relief-add', wx.ART_ERROR),
-                                label = _('Add shaded relief map layer')),
-        'addRArrow'  : MetaIcon(img = iconSet.get('layer-aspect-arrow-add', wx.ART_ERROR),
-                                label = _('Add raster flow arrows')),
-        'addRNum'    : MetaIcon(img = iconSet.get('layer-cell-cats-add', wx.ART_ERROR),
-                                label = _('Add raster cell numbers')),
-        'addThematic': MetaIcon(img = iconSet.get('layer-vector-thematic-add', wx.ART_ERROR),
-                                label = _('Add thematic area (choropleth) map layer')),
-        'addChart'   : MetaIcon(img = iconSet.get('layer-vector-chart-add', wx.ART_ERROR),
-                                label = _('Add thematic chart layer')),
-        'addGrid'    : MetaIcon(img = iconSet.get('layer-grid-add', wx.ART_ERROR),
-                                label = _('Add grid layer')),
-        'addGeodesic': MetaIcon(img = iconSet.get('shortest-distance', wx.ART_ERROR),
-                                label = _('Add geodesic line layer')),
-        'addRhumb'   : MetaIcon(img = iconSet.get('shortest-distance', wx.ART_ERROR),
-                                label = _('Add rhumbline layer')),
-        'addLabels'  : MetaIcon(img = iconSet.get('layer-label-add', wx.ART_ERROR),
-                                label = _('Add labels')),
-        'addRast3d'  : MetaIcon(img = iconSet.get('layer-raster3d-add', wx.ART_ERROR),
-                                label = _('Add 3D raster map layer'),
-                                desc  =  _('Note that 3D raster data are rendered only in 3D view mode')),
-        'settings'   : MetaIcon(img = iconSet.get('settings', wx.ART_ERROR),
-                                label = _('Show GUI settings')),
-        'modeler'    : MetaIcon(img = iconSet.get('modeler-main', wx.ART_ERROR),
-                                label = _('Graphical Modeler')),
-        'layerOptions'  : MetaIcon(img = iconSet.get('options', wx.ART_ERROR),
-                                   label = _('Set options')),
-        'mapOutput'  : MetaIcon(img = iconSet.get('print-compose', wx.ART_ERROR),
-                                label = _('Cartographic Composer')),
-        'mapcalc'    : MetaIcon(img = iconSet.get('calculator', wx.ART_ERROR),
-                                label = _('Raster Map Calculator')),
-        },
-    'vdigit' : {
-        'addPoint'        : MetaIcon(img = iconSet.get('point-create', wx.ART_ERROR),
-                                     label = _('Digitize new point'),
-                                     desc = _('Left: new point')),
-        'addLine'         : MetaIcon(img = iconSet.get('line-create', wx.ART_ERROR),
-                                     label = _('Digitize new line'),
-                                     desc = _('Left: new point; Ctrl+Left: undo last point; Right: close line')),
-        'addBoundary'     : MetaIcon(img = iconSet.get('boundary-create', wx.ART_ERROR),
-                                     label = _('Digitize new boundary'),
-                                     desc = _('Left: new point; Ctrl+Left: undo last point; Right: close line')),
-        'addCentroid'     : MetaIcon(img = iconSet.get('centroid-create', wx.ART_ERROR),
-                                     label = _('Digitize new centroid'),
-                                     desc = _('Left: new point')),
-        'addArea'         : MetaIcon(img = iconSet.get('polygon-create', wx.ART_ERROR),
-                                     label = _('Digitize new area (composition of boundaries without category and one centroid with category)'),
-                                     desc = _('Left: new point')),
-        'addVertex'       : MetaIcon(img = iconSet.get('vertex-create', wx.ART_ERROR),
-                                     label = _('Add new vertex'),
-                                     desc = _('Left: Select; Ctrl+Left: Unselect; Right: Confirm')),
-        'deleteLine'      : MetaIcon(img = iconSet.get('line-delete', wx.ART_ERROR),
-                                     label = _('Delete feature(s)'),
-                                     desc = _('Left: Select; Ctrl+Left: Unselect; Right: Confirm')),
-        'displayAttr'     : MetaIcon(img = iconSet.get('attributes-display', wx.ART_ERROR),
-                                     label = _('Display/update attributes'),
-                                     desc = _('Left: Select')),
-        'displayCats'     : MetaIcon(img = iconSet.get('cats-display', wx.ART_ERROR),
-                                     label = _('Display/update categories'),
-                                     desc = _('Left: Select')),
-        'editLine'        : MetaIcon(img = iconSet.get('line-edit', wx.ART_ERROR),
-                                     label = _('Edit line/boundary'),
-                                     desc = _('Left: new point; Ctrl+Left: undo last point; Right: close line')),
-        'moveLine'        : MetaIcon(img = iconSet.get('line-move', wx.ART_ERROR),
-                                     label = _('Move feature(s)'),
-                                     desc = _('Left: Select; Ctrl+Left: Unselect; Right: Confirm')),
-        'moveVertex'      : MetaIcon(img = iconSet.get('vertex-move', wx.ART_ERROR),
-                                     label = _('Move vertex'),
-                                     desc = _('Left: Select; Ctrl+Left: Unselect; Right: Confirm')),
-        'removeVertex'    : MetaIcon(img = iconSet.get('vertex-delete', wx.ART_ERROR),
-                                     label = _('Remove vertex'),
-                                     desc = _('Left: Select; Ctrl+Left: Unselect; Right: Confirm')),
-        'settings'        : MetaIcon(img = iconSet.get('settings', wx.ART_ERROR),
-                                     label = _('Digitization settings')),
-        'quit'            : MetaIcon(img = iconSet.get('quit', wx.ART_ERROR),
-                                     label = _('Quit digitizer'),
-                                     desc = _('Quit digitizer and save changes')),
-        'additionalTools' : MetaIcon(img = iconSet.get('tools', wx.ART_ERROR),
-                                     label = _('Additional tools ' \
-                                                   '(copy, flip, connect, etc.)'),
-                                     desc = _('Left: Select; Ctrl+Left: Unselect; Right: Confirm')),
-        'undo'             : MetaIcon(img = iconSet.get('undo', wx.ART_ERROR),
-                                      label = _('Undo'),
-                                      desc = _('Undo previous changes')),
-        },
-    'profile' : {
-        'draw'         : MetaIcon(img = iconSet.get('show', wx.ART_ERROR),
-                                  label = _('Draw/re-draw profile')),
-        'transect'     : MetaIcon(img = iconSet.get('layer-raster-profile', wx.ART_ERROR),
-                                  label = _('Draw transect in map display window to profile')),
-        'options'      : MetaIcon(img = iconSet.get('settings', wx.ART_ERROR),
-                                  label = _('Profile options')),
-        'save'         : MetaIcon(img = iconSet.get('save', wx.ART_ERROR),
-                                  label = _('Save profile data to CSV file')),
-        'quit'         : MetaIcon(img = iconSet.get('quit', wx.ART_ERROR),
-                                  label = _('Quit Profile Analysis Tool'))
-        },
-    'georectify' : {
-        'gcpSet'    : MetaIcon(img = iconSet.get('gcp-create', wx.ART_ERROR),
-                               label = _('Set GCP'),
-                               desc = _('Define GCP (Ground Control Points)')),
-        'georectify': MetaIcon(img = iconSet.get('georectify', wx.ART_ERROR),
-                               label = _('Georectify')),
-        'gcpRms'    : MetaIcon(img = iconSet.get('gcp-rms', wx.ART_ERROR),
-                               label = _('Recalculate RMS error')),
-        'gcpSave'   : MetaIcon(img = iconSet.get('gcp-save', wx.ART_ERROR),
-                               label = _('Save GCPs to POINTS file')),
-        'gcpAdd'    : MetaIcon(img = iconSet.get('gcp-add', wx.ART_ERROR),
-                               label = _('Add new GCP')),
-        'gcpDelete' : MetaIcon(img = iconSet.get('gcp-delete', wx.ART_ERROR),
-                               label = _('Delete selected GCP')),
-        'gcpClear'  : MetaIcon(img = iconSet.get('gcp-remove', wx.ART_ERROR),
-                                label = _('Clear selected GCP')),
-        'gcpReload' : MetaIcon(img = iconSet.get('reload', wx.ART_ERROR),
-                               label = _('Reload GCPs from POINTS file')),
-        'quit'      : MetaIcon(img = iconSet.get('quit', wx.ART_ERROR),
-                               label = _('Quit georectification')),
-        'settings'  : MetaIcon(img = iconSet.get('settings', wx.ART_ERROR),
-                               label = _('Settings'),
-                               desc = _('Settings dialog for georectification tool')),
-        },
-    'nviz' : {
-        'view'    : MetaIcon(img = iconSet.get('3d-view', wx.ART_ERROR),
-                             label = _('Switch to view control page'),
-                             desc = _('Change view settings')),
-        'surface' : MetaIcon(img = iconSet.get('3d-raster', wx.ART_ERROR),
-                             label = _('Switch to surface (raster) control page'),
-                             desc = _('Change surface (loaded raster maps) settings')),
-        'vector'  : MetaIcon(img = iconSet.get('3d-vector', wx.ART_ERROR),
-                             label = _('Switch to vector (2D/3D) control page'),
-                             desc = _('Change 2D/3D vector settings')),
-        'volume'  : MetaIcon(img = iconSet.get('3d-volume', wx.ART_ERROR),
-                             label = _('Switch to volume (3D raster) control page'),
-                             desc = _('Change volume (loaded 3D raster maps) settings')),
-        'light'   : MetaIcon(img = iconSet.get('3d-light', wx.ART_ERROR),
-                             label = _('Switch to lighting control page'),
-                             desc = _('Change lighting settings')),
-        'fringe'  : MetaIcon(img = iconSet.get('3d-fringe', wx.ART_ERROR),
-                             label = _('Switch to fringe control page'),
-                             desc = _('Switch on/off fringes')),
-        'settings': MetaIcon(img = iconSet.get('settings', wx.ART_ERROR),
-                             label = _('3D view mode tools'),
-                             desc = _('Show/hide 3D view mode settings dialog')),
-        'quit'    : MetaIcon(img = iconSet.get('quit', wx.ART_ERROR),
-                             label = _('Quit 3D view mode'),
-                             desc = _('Switch back to 2D view mode')),
-        },
-    'modeler' : {
-        'new'        : MetaIcon(img = iconSet.get('create', wx.ART_ERROR),
-                                label = _('Create new model (Ctrl+N)')),
-        'open'       : MetaIcon(img = iconSet.get('open', wx.ART_ERROR),
-                                label = _('Load model from file (Ctrl+O)')),
-        'save'       : MetaIcon(img = iconSet.get('save', wx.ART_ERROR),
-                                label = _('Save current model to file (Ctrl+S)')),
-        'toImage'    : MetaIcon(img = iconSet.get('image-export', wx.ART_ERROR),
-                                label = _('Export model to image')),
-        'toPython'   : MetaIcon(img = iconSet.get('python-export', wx.ART_ERROR),
-                                label = _('Export model to Python script')),
-        'actionAdd'  : MetaIcon(img = iconSet.get('module-add', wx.ART_ERROR),
-                                label = _('Add action (GRASS module) to model')),
-        'dataAdd'    : MetaIcon(img = iconSet.get('data-add', wx.ART_ERROR),
-                                label = _('Add data item to model')),
-        'relation'   : MetaIcon(img = iconSet.get('relation-create', wx.ART_ERROR),
-                                label = _('Define relation between data and action items')),
-        'run'        : MetaIcon(img = iconSet.get('execute', wx.ART_ERROR),
-                                label = _('Run model')),
-        'validate'   : MetaIcon(img = iconSet.get('check', wx.ART_ERROR),
-                                label = _('Validate model')),
-        'settings'   : MetaIcon(img = iconSet.get('settings', wx.ART_ERROR),
-                                label = _('Show modeler settings')),
-        'properties' : MetaIcon(img = iconSet.get('options', wx.ART_ERROR),
-                                label = _('Show model properties')),
-        'variables'  : MetaIcon(img = iconSet.get('modeler-variables', wx.ART_ERROR),
-                                label = _('Manage model variables')),
-        'redraw'     : MetaIcon(img = iconSet.get('redraw', wx.ART_ERROR),
-                                label = _('Redraw model canvas')),
-        'quit'    : MetaIcon(img = iconSet.get('quit', wx.ART_ERROR),
-                             label = _('Quit Graphical Modeler')),
-        },
-    'misc' : {
-        'font' : MetaIcon(img = iconSet.get('font', wx.ART_ERROR),
-                          label = _('Select font')),
-        'help' : MetaIcon(img = iconSet.get('help', wx.ART_ERROR),
-                          label = _('Show manual')),
-        'quit' : MetaIcon(img = iconSet.get('quit', wx.ART_ERROR),
-                          label = _('Quit')),
-        },
-    'psMap' : {
-        'scriptSave' : MetaIcon(img = iconSet.get('script-save', wx.ART_ERROR),
-                                label = _('Generate text file with mapping instructions')),
-        'scriptLoad' : MetaIcon(img = iconSet.get('script-load', wx.ART_ERROR),
-                                label = _('Load text file with mapping instructions')),                           
-        'psExport'   : MetaIcon(img = iconSet.get('ps-export', wx.ART_ERROR),
-                                label = _('Generate PostScript output')),
-        'pdfExport'  : MetaIcon(img = iconSet.get('pdf-export', wx.ART_ERROR),
-                                label = _('Generate PDF output')),
-        'pageSetup'  : MetaIcon(img = iconSet.get('page-settings', wx.ART_ERROR),
-                                label = _('Page setup'),
-                                desc = _('Specify paper size, margins and orientation')),
-        'fullExtent' : MetaIcon(img = iconSet.get('zoom-extent', wx.ART_ERROR),
-                                label = _("Full extent"),
-                                desc = _("Zoom to full extent")),
-        'addMap'     : MetaIcon(img = iconSet.get('layer-add', wx.ART_ERROR),
-                                label = _("Map frame"),
-                                desc = _("Click and drag to place map frame")),
-        'addRast'    : MetaIcon(img = iconSet.get('layer-raster-add', wx.ART_ERROR),
-                                label = _("Raster map"),
-                                desc = _("Add raster map")),
-        'addVect'    : MetaIcon(img = iconSet.get('layer-vector-add', wx.ART_ERROR),
-                                label = _("Vector map"),
-                                desc = _("Add vector map")),
-        'deleteObj'  : MetaIcon(img = iconSet.get('layer-remove', wx.ART_ERROR),
-                                label = _("Delete selected object")),
-        'preview'    : MetaIcon(img = iconSet.get('execute', wx.ART_ERROR),
-                                label = _("Show preview")),
-        'quit'       : MetaIcon(img = iconSet.get('quit', wx.ART_ERROR),
-                                label = _('Quit Cartographic Composer')),
-        'addText'    : MetaIcon(img = iconSet.get('text-add', wx.ART_ERROR),
-                                label = _('Add text')),
-        'addMapinfo' : MetaIcon(img = iconSet.get('map-info', wx.ART_ERROR),
-                                label = _('Add map info')),
-        'addLegend'  : MetaIcon(img = iconSet.get('legend-add', wx.ART_ERROR),
-                                label = _('Add legend')),
-        'addScalebar' : MetaIcon(img = iconSet.get('scalebar-add', wx.ART_ERROR),
-                                 label = _('Add scale bar')),
-        }
-    }
-
-# testing ...
-if __name__ == '__main__':
-    for k, v in iconSet.iteritems():
-        print v.GetImageName()
+    def SetLabel(self, label = None, desc = None):
+        """!Set label/description for icon
+        @param label icon label (None for no change)
+        @param desc icon description (None for no change)
+        
+        @return copy of original object
+        """
+        cobj = copy.copy(self)
+        if label:
+            cobj.label = label
+        if desc:
+            cobj.description = desc
+        
+        return cobj

Modified: grass/branches/releasebranch_6_4/gui/wxpython/icons/silk_icons.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/icons/silk_icons.py	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/icons/silk_icons.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -10,7 +10,7 @@
 
 import wx
 
-import globalvar
+from core import globalvar
 
 iconPath = os.path.join(globalvar.ETCDIR, "gui", "icons", "silk")
 

Added: grass/branches/releasebranch_6_4/gui/wxpython/lmgr/frame.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/lmgr/frame.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/lmgr/frame.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1744 @@
+"""!
+ at package lmgr::frame
+
+ at brief Layer Manager - main menu, layer management toolbar, notebook
+control for display management and access to command console.
+
+Classes:
+ - frame::GMFrame
+
+(C) 2006-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton (Arizona State University)
+ at author Jachym Cepicky (Mendel University of Agriculture)
+ at author Martin Landa <landa.martin gmail.com>
+ at author Vaclav Petras <wenzeslaus gmail.com> (menu customization)
+"""
+
+import sys
+import os
+import tempfile
+import stat
+try:
+    import xml.etree.ElementTree as etree
+except ImportError:
+    import elementtree.ElementTree as etree # Python <= 2.4
+
+from core import globalvar
+import wx
+import wx.aui
+try:
+    import wx.lib.agw.flatnotebook   as FN
+except ImportError:
+    import wx.lib.flatnotebook   as FN
+
+sys.path.append(os.path.join(globalvar.ETCDIR, "python"))
+from grass.script          import core as grass
+
+from core.gcmd             import RunCommand, GError, GMessage
+from core.settings         import UserSettings
+from gui_core.preferences  import MapsetAccess, PreferencesDialog, EVT_SETTINGS_CHANGED
+from lmgr.layertree        import LayerTree, LMIcons
+from lmgr.menudata         import ManagerData
+from gui_core.widgets      import GNotebook
+from modules.mcalc_builder import MapCalcFrame
+from dbmgr.manager         import AttributeManager
+from core.workspace        import ProcessWorkspaceFile, ProcessGrcFile, WriteWorkspaceFile
+from gui_core.goutput      import GMConsole
+from gui_core.dialogs      import DxfImportDialog, GdalImportDialog, MapLayersDialog
+from gui_core.dialogs      import LocationDialog, MapsetDialog, CreateNewVector, GroupDialog
+from modules.ogc_services  import WMSDialog
+from modules.colorrules    import RasterColorTable, VectorColorTable
+from gui_core.menu         import Menu
+from gmodeler.model        import Model
+from gmodeler.frame        import ModelFrame
+from psmap.frame           import PsMapFrame
+from core.debug            import Debug
+from gui_core.ghelp        import MenuTreeWindow, AboutWindow
+from modules.extensions    import InstallExtensionWindow, UninstallExtensionWindow
+from lmgr.toolbars         import LMWorkspaceToolbar, LMDataToolbar, LMToolsToolbar
+from lmgr.toolbars         import LMMiscToolbar, LMVectorToolbar, LMNvizToolbar
+from lmgr.pyshell          import PyShellWindow
+from gui_core.forms        import GUI
+from gcp.manager           import GCPWizard
+from nviz.main             import haveNviz
+
+class GMFrame(wx.Frame):
+    """!Layer Manager frame with notebook widget for controlling GRASS
+    GIS. Includes command console page for typing GRASS (and other)
+    commands, tree widget page for managing map layers.
+    """
+    def __init__(self, parent, id = wx.ID_ANY, title = _("GRASS GIS Layer Manager"),
+                 workspace = None,
+                 size = globalvar.GM_WINDOW_SIZE, style = wx.DEFAULT_FRAME_STYLE, **kwargs):
+        self.parent    = parent
+        self.baseTitle = title
+        self.iconsize  = (16, 16)
+        
+        wx.Frame.__init__(self, parent = parent, id = id, size = size,
+                          style = style, **kwargs)
+                          
+        self.SetTitle(self.baseTitle)
+        self.SetName("LayerManager")
+
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+
+        self._auimgr = wx.aui.AuiManager(self)
+
+        # initialize variables
+        self.disp_idx      = 0            # index value for map displays and layer trees
+        self.curr_page     = None         # currently selected page for layer tree notebook
+        self.curr_pagenum  = None         # currently selected page number for layer tree notebook
+        self.workspaceFile = workspace    # workspace file
+        self.workspaceChanged = False     # track changes in workspace
+        self.georectifying = None         # reference to GCP class or None
+        self.gcpmanagement = None         # reference to GCP class or None
+        
+        # list of open dialogs
+        self.dialogs        = dict()
+        self.dialogs['preferences'] = None
+        self.dialogs['atm'] = list()
+        
+        # creating widgets
+        self._createMenuBar()
+        self.statusbar = self.CreateStatusBar(number = 1)
+        self.notebook  = self._createNoteBook()
+        self.toolbars  = { 'workspace' : LMWorkspaceToolbar(parent = self),
+                           'data'      : LMDataToolbar(parent = self),
+                           'tools'     : LMToolsToolbar(parent = self),
+                           'misc'      : LMMiscToolbar(parent = self),
+                           'vector'    : LMVectorToolbar(parent = self),
+                           'nviz'      : LMNvizToolbar(parent = self)}
+        
+        self._toolbarsData = { 'workspace' : ("toolbarWorkspace",     # name
+                                              _("Workspace Toolbar"), # caption
+                                              1),                     # row
+                               'data'      : ("toolbarData",
+                                              _("Data Toolbar"),
+                                              1),
+                               'misc'      : ("toolbarMisc",
+                                              _("Misc Toolbar"),
+                                              2),
+                               'tools'     : ("toolbarTools",
+                                              _("Tools Toolbar"),
+                                              2),
+                               'vector'    : ("toolbarVector",
+                                              _("Vector Toolbar"),
+                                              2),
+                               'nviz'      : ("toolbarNviz",
+                                              _("3D view Toolbar"),
+                                              2),                                            
+                               }
+        if sys.platform == 'win32':
+            self._toolbarsList = ('workspace', 'data',
+                                  'vector', 'tools', 'misc', 'nviz')
+        else:
+            self._toolbarsList = ('data', 'workspace',
+                                  'nviz', 'misc', 'tools', 'vector')
+        for toolbar in self._toolbarsList:
+            name, caption, row = self._toolbarsData[toolbar]
+            self._auimgr.AddPane(self.toolbars[toolbar],
+                                 wx.aui.AuiPaneInfo().
+                                 Name(name).Caption(caption).
+                                 ToolbarPane().Top().Row(row).
+                                 LeftDockable(False).RightDockable(False).
+                                 BottomDockable(False).TopDockable(True).
+                                 CloseButton(False).Layer(2).
+                                 BestSize((self.toolbars[toolbar].GetBestSize())))
+        
+        self._auimgr.GetPane('toolbarNviz').Hide()
+        # bindings
+        self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
+
+        # minimal frame size
+        self.SetMinSize((500, 400))
+
+        # AUI stuff
+        self._auimgr.AddPane(self.notebook, wx.aui.AuiPaneInfo().
+                             Left().CentrePane().BestSize((-1,-1)).Dockable(False).
+                             CloseButton(False).DestroyOnClose(True).Row(1).Layer(0))
+
+        self._auimgr.Update()
+
+        wx.CallAfter(self.notebook.SetSelectionByName, 'layers')
+        
+        # use default window layout ?
+        if UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'enabled'):
+            dim = UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'dim')
+            try:
+               x, y = map(int, dim.split(',')[0:2])
+               w, h = map(int, dim.split(',')[2:4])
+               self.SetPosition((x, y))
+               self.SetSize((w, h))
+            except:
+                pass
+        else:
+            self.Centre()
+        
+        self.Layout()
+        self.Show()
+        
+        # load workspace file if requested
+        if self.workspaceFile:
+            # load given workspace file
+            if self.LoadWorkspaceFile(self.workspaceFile):
+                self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.workspaceFile))
+            else:
+                self.workspaceFile = None
+        else:
+            # start default initial display
+            self.NewDisplay(show = False)
+
+        # show map display widnow
+        # -> OnSize() -> UpdateMap()
+        if self.curr_page and not self.curr_page.maptree.mapdisplay.IsShown():
+            self.curr_page.maptree.mapdisplay.Show()
+        
+        # redirect stderr to log area    
+        self.goutput.Redirect()
+        
+        # fix goutput's pane size (required for Mac OSX)`
+        self.goutput.SetSashPosition(int(self.GetSize()[1] * .8))
+        
+        self.workspaceChanged = False
+        
+        # start with layer manager on top
+        if self.curr_page:
+            self.curr_page.maptree.mapdisplay.Raise()
+        wx.CallAfter(self.Raise)
+        
+    def _createMenuBar(self):
+        """!Creates menu bar"""
+        self.menubar = Menu(parent = self, data = ManagerData())
+        self.SetMenuBar(self.menubar)
+        self.menucmd = self.menubar.GetCmd()
+        
+    def _createTabMenu(self):
+        """!Creates context menu for display tabs.
+        
+        Used to rename display.
+        """
+        menu = wx.Menu()
+        item = wx.MenuItem(menu, id = wx.ID_ANY, text = _("Rename Map Display"))
+        menu.AppendItem(item)
+        self.Bind(wx.EVT_MENU, self.OnRenameDisplay, item)
+        
+        return menu
+        
+    def _setCopyingOfSelectedText(self):
+        copy = UserSettings.Get(group = 'manager', key = 'copySelectedTextToClipboard', subkey = 'enabled')
+        self.goutput.SetCopyingOfSelectedText(copy)
+    
+    def IsPaneShown(self, name):
+        """!Check if pane (toolbar, ...) of given name is currently shown"""
+        if self._auimgr.GetPane(name).IsOk():
+            return self._auimgr.GetPane(name).IsShown()
+        return False
+    
+    def _createNoteBook(self):
+        """!Creates notebook widgets"""
+        self.notebook = GNotebook(parent = self, style = globalvar.FNPageDStyle)
+        # create displays notebook widget and add it to main notebook page
+        cbStyle = globalvar.FNPageStyle
+        if globalvar.hasAgw:
+            self.gm_cb = FN.FlatNotebook(self, id = wx.ID_ANY, agwStyle = cbStyle)
+        else:
+            self.gm_cb = FN.FlatNotebook(self, id = wx.ID_ANY, style = cbStyle)
+        self.gm_cb.SetTabAreaColour(globalvar.FNPageColor)
+        menu = self._createTabMenu()
+        self.gm_cb.SetRightClickMenu(menu)
+        self.notebook.AddPage(page = self.gm_cb, text = _("Map layers"), name = 'layers')
+        
+        # create 'command output' text area
+        self.goutput = GMConsole(self)
+        self.notebook.AddPage(page = self.goutput, text = _("Command console"), name = 'output')
+        self._setCopyingOfSelectedText()
+        
+        # create 'search module' notebook page
+        if not UserSettings.Get(group = 'manager', key = 'hideTabs', subkey = 'search'):
+            self.search = MenuTreeWindow(parent = self)
+            self.notebook.AddPage(page = self.search, text = _("Search module"), name = 'search')
+        else:
+            self.search = None
+        
+        # create 'python shell' notebook page
+        if not UserSettings.Get(group = 'manager', key = 'hideTabs', subkey = 'pyshell'):
+            self.pyshell = PyShellWindow(parent = self)
+            self.notebook.AddPage(page = self.pyshell, text = _("Python shell"), name = 'pyshell')
+        else:
+            self.pyshell = None
+        
+        # bindings
+        self.gm_cb.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED,    self.OnCBPageChanged)
+        self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
+        self.gm_cb.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CLOSING,    self.OnCBPageClosed)
+        
+        return self.notebook
+            
+    def AddNvizTools(self):
+        """!Add nviz notebook page"""
+        Debug.msg(5, "GMFrame.AddNvizTools()")
+        if not haveNviz:
+            return
+        
+        from nviz.main import NvizToolWindow
+        
+        # show toolbar
+        self._auimgr.GetPane('toolbarNviz').Show()
+        # reorder other toolbars
+        for pos, toolbar in enumerate(('toolbarVector', 'toolbarTools', 'toolbarMisc','toolbarNviz')):
+            self._auimgr.GetPane(toolbar).Row(2).Position(pos)
+        self._auimgr.Update()
+        
+        # create nviz tools tab
+        self.nviz = NvizToolWindow(parent = self,
+                                   display = self.curr_page.maptree.GetMapDisplay())
+        idx = self.notebook.GetPageIndexByName('layers')
+        self.notebook.InsertPage(indx = idx + 1, page = self.nviz, text = _("3D view"), name = 'nviz')
+        self.notebook.SetSelectionByName('nviz')
+        
+        
+    def RemoveNvizTools(self):
+        """!Remove nviz notebook page"""
+        # if more mapwindow3D were possible, check here if nb page should be removed
+        self.notebook.SetSelectionByName('layers')
+        self.notebook.RemovePage(self.notebook.GetPageIndexByName('nviz'))
+        del self.nviz
+        # hide toolbar
+        self._auimgr.GetPane('toolbarNviz').Hide()
+        for pos, toolbar in enumerate(('toolbarVector', 'toolbarTools', 'toolbarMisc')):
+            self._auimgr.GetPane(toolbar).Row(2).Position(pos)
+        self._auimgr.Update()
+    
+    def WorkspaceChanged(self):
+        """!Update window title"""
+        if not self.workspaceChanged:
+            self.workspaceChanged = True
+        
+        if self.workspaceFile:
+            self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.workspaceFile) + '*')
+        
+    def OnLocationWizard(self, event):
+        """!Launch location wizard"""
+        from location_wizard.wizard import LocationWizard
+        
+        gWizard = LocationWizard(parent = self,
+                                 grassdatabase = grass.gisenv()['GISDBASE'])
+        location = gWizard.location
+        
+        if location !=  None:
+            dlg = wx.MessageDialog(parent = self,
+                                   message = _('Location <%s> created.\n\n'
+                                               'Do you want to switch to the '
+                                               'new location?') % location,
+                                   caption=_("Switch to new location?"),
+                                   style = wx.YES_NO | wx.NO_DEFAULT |
+                                   wx.ICON_QUESTION | wx.CENTRE)
+            
+            ret = dlg.ShowModal()
+            dlg.Destroy()
+            if ret == wx.ID_YES:
+                if RunCommand('g.mapset', parent = self,
+                              location = location,
+                              mapset = 'PERMANENT') != 0:
+                    return
+                
+                GMessage(parent = self,
+                         message = _("Current location is <%(loc)s>.\n"
+                                     "Current mapset is <%(mapset)s>.") % \
+                             { 'loc' : location, 'mapset' : 'PERMANENT' })
+        
+    def OnSettingsChanged(self, event):
+        """!Here can be functions which have to be called after EVT_SETTINGS_CHANGED. 
+        Now only set copying of selected text to clipboard (in goutput).
+        """
+        ### self._createMenuBar() # bug when menu is re-created on the fly
+        self._setCopyingOfSelectedText()
+        
+    def OnGCPManager(self, event):
+        """!Launch georectifier module
+        """
+        GCPWizard(self)
+
+    def OnGModeler(self, event):
+        """!Launch Graphical Modeler"""
+        win = ModelFrame(parent = self)
+        win.CentreOnScreen()
+        
+        win.Show()
+        
+    def OnPsMap(self, event):
+        """!Launch Cartographic Composer
+        """
+        win = PsMapFrame(parent = self)
+        win.CentreOnScreen()
+        
+        win.Show()
+        
+    def OnDone(self, cmd, returncode):
+        """Command execution finised"""
+        if hasattr(self, "model"):
+            self.model.DeleteIntermediateData(log = self.goutput)
+            del self.model
+        self.SetStatusText('')
+        
+    def OnRunModel(self, event):
+        """!Run model"""
+        filename = ''
+        dlg = wx.FileDialog(parent = self, message =_("Choose model to run"),
+                            defaultDir = os.getcwd(),
+                            wildcard = _("GRASS Model File (*.gxm)|*.gxm"))
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
+        
+        if not filename:
+            dlg.Destroy()
+            return
+        
+        self.model = Model()
+        self.model.LoadModel(filename)
+        self.model.Run(log = self.goutput, onDone = self.OnDone, parent = self)
+        
+        dlg.Destroy()
+        
+    def OnMapsets(self, event):
+        """!Launch mapset access dialog
+        """
+        dlg = MapsetAccess(parent = self, id = wx.ID_ANY)
+        dlg.CenterOnScreen()
+        
+        if dlg.ShowModal() == wx.ID_OK:
+            ms = dlg.GetMapsets()
+            RunCommand('g.mapsets',
+                       parent = self,
+                       mapset = '%s' % ','.join(ms))
+            
+    def OnCBPageChanged(self, event):
+        """!Page in notebook (display) changed"""
+        self.curr_page   = self.gm_cb.GetCurrentPage()
+        self.curr_pagenum = self.gm_cb.GetSelection()
+        try:
+            self.curr_page.maptree.mapdisplay.SetFocus()
+            self.curr_page.maptree.mapdisplay.Raise()
+        except:
+            pass
+        
+        event.Skip()
+
+    def OnPageChanged(self, event):
+        """!Page in notebook changed"""
+        page = event.GetSelection()
+        if page == self.notebook.GetPageIndexByName('output'):
+            # remove '(...)'
+            self.notebook.SetPageText(page, _("Command console"))
+            wx.CallAfter(self.goutput.ResetFocus)
+        self.SetStatusText('', 0)
+        
+        event.Skip()
+
+    def OnCBPageClosed(self, event):
+        """!Page of notebook closed
+        Also close associated map display
+        """
+        if UserSettings.Get(group = 'manager', key = 'askOnQuit', subkey = 'enabled'):
+            maptree = self.curr_page.maptree
+            
+            if self.workspaceFile:
+                message = _("Do you want to save changes in the workspace?")
+            else:
+                message = _("Do you want to store current settings "
+                            "to workspace file?")
+            
+            # ask user to save current settings
+            if maptree.GetCount() > 0:
+                name = self.gm_cb.GetPageText(self.curr_pagenum)
+                dlg = wx.MessageDialog(self,
+                                       message = message,
+                                       caption = _("Close Map Display %s") % name,
+                                       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.workspaceFile:
+                        self.OnWorkspaceSaveAs()
+                    else:
+                        self.SaveToWorkspaceFile(self.workspaceFile)
+                elif ret == wx.ID_CANCEL:
+                    event.Veto()
+                    dlg.Destroy()
+                    return
+                dlg.Destroy()
+
+        self.gm_cb.GetPage(event.GetSelection()).maptree.Map.Clean()
+        self.gm_cb.GetPage(event.GetSelection()).maptree.Close(True)
+
+        self.curr_page = None
+
+        event.Skip()
+
+    def GetLayerTree(self):
+        """!Get current layer tree"""
+        return self.curr_page.maptree
+    
+    def GetLogWindow(self):
+        """!Get widget for command output"""
+        return self.goutput
+    
+    def GetMenuCmd(self, event):
+        """!Get GRASS command from menu item
+
+        Return command as a list"""
+        layer = None
+        
+        if event:
+            cmd = self.menucmd[event.GetId()]
+        
+        try:
+            cmdlist = cmd.split(' ')
+        except: # already list?
+            cmdlist = cmd
+        
+        # check list of dummy commands for GUI modules that do not have GRASS
+        # bin modules or scripts. 
+        if cmd in ['vcolors', 'r.mapcalc', 'r3.mapcalc']:
+            return cmdlist
+
+        try:
+            layer = self.curr_page.maptree.layer_selected
+            name = self.curr_page.maptree.GetPyData(layer)[0]['maplayer'].name
+            type = self.curr_page.maptree.GetPyData(layer)[0]['type']
+        except:
+            layer = None
+
+        if layer and len(cmdlist) == 1: # only if no paramaters given
+            if (type == 'raster' and cmdlist[0][0] == 'r' and cmdlist[0][1] != '3') or \
+                    (type == 'vector' and cmdlist[0][0] == 'v'):
+                input = GUI().GetCommandInputMapParamKey(cmdlist[0])
+                if input:
+                    cmdlist.append("%s=%s" % (input, name))
+        
+        return cmdlist
+
+    def RunMenuCmd(self, event = None, cmd = []):
+        """!Run command selected from menu"""
+        if event:
+            cmd = self.GetMenuCmd(event)
+        self.goutput.RunCmd(cmd, switchPage = False)
+
+    def OnMenuCmd(self, event = None, cmd = []):
+        """!Parse command selected from menu"""
+        if event:
+            cmd = self.GetMenuCmd(event)
+        GUI(parent = self).ParseCommand(cmd)
+        
+    def OnVDigit(self, event):
+        """!Start vector digitizer
+        """
+        if not self.curr_page:
+            self.MsgNoLayerSelected()
+            return
+        
+        tree = self.GetLayerTree()
+        layer = tree.layer_selected
+        # no map layer selected
+        if not layer:
+            self.MsgNoLayerSelected()
+            return
+        
+        # available only for vector map layers
+        try:
+            mapLayer = tree.GetPyData(layer)[0]['maplayer']
+        except:
+            mapLayer = None
+        
+        if not mapLayer or mapLayer.GetType() != 'vector':
+            GMessage(parent = self,
+                     message = _("Selected map layer is not vector."))
+            return
+        
+        if mapLayer.GetMapset() != grass.gisenv()['MAPSET']:
+            GMessage(parent = self,
+                     message = _("Editing is allowed only for vector maps from the "
+                                 "current mapset."))
+            return
+        
+        if not tree.GetPyData(layer)[0]:
+            return
+        dcmd = tree.GetPyData(layer)[0]['cmd']
+        if not dcmd:
+            return
+        
+        tree.OnStartEditing(None)
+        
+    def OnRunScript(self, event):
+        """!Run script"""
+        # open dialog and choose script file
+        dlg = wx.FileDialog(parent = self, message = _("Choose script file to run"),
+                            defaultDir = os.getcwd(),
+                            wildcard = _("Python script (*.py)|*.py|Bash script (*.sh)|*.sh"))
+        
+        filename = None
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
+        
+        if not filename:
+            return False
+        
+        if not os.path.exists(filename):
+            GError(parent = self,
+                   message = _("Script file '%s' doesn't exist. "
+                               "Operation canceled.") % filename)
+            return
+
+        # check permission
+        if not os.access(filename, os.X_OK):
+            dlg = wx.MessageDialog(self,
+                                   message = _("Script <%s> is not executable. "
+                                               "Do you want to set the permissions "
+                                               "that allows you to run this script "
+                                               "(note that you must be the owner of the file)?" % \
+                                                   os.path.basename(filename)),
+                                   caption = _("Set permission?"),
+                                   style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+            if dlg.ShowModal() != wx.ID_YES:
+                return
+            dlg.Destroy()
+            try:
+                mode = stat.S_IMODE(os.lstat(filename)[stat.ST_MODE])
+                os.chmod(filename, mode | stat.S_IXUSR)
+            except OSError:
+                GError(_("Unable to set permission. Operation canceled."), parent = self)
+                return
+        
+        # check GRASS_ADDON_PATH
+        addonPath = os.getenv('GRASS_ADDON_PATH', [])
+        if addonPath:
+            addonPath = addonPath.split(os.pathsep)
+        dirName = os.path.dirname(filename)
+        if dirName not in addonPath:
+            addonPath.append(dirName)
+            dlg = wx.MessageDialog(self,
+                                   message = _("Directory '%s' is not defined in GRASS_ADDON_PATH. "
+                                               "Do you want add this directory to GRASS_ADDON_PATH?") % \
+                                       dirName,
+                                   caption = _("Update Addons path?"),
+                                   style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+            if dlg.ShowModal() == wx.ID_YES:
+                os.environ['GRASS_ADDON_PATH'] = os.pathsep.join(addonPath)
+                RunCommand('g.gisenv', set = 'ADDON_PATH=%s' % os.environ['GRASS_ADDON_PATH'])
+        
+        self.goutput.WriteCmdLog(_("Launching script '%s'...") % filename)
+        self.goutput.RunCmd([filename], switchPage = True)
+        
+    def OnChangeLocation(self, event):
+        """Change current location"""
+        dlg = LocationDialog(parent = self)
+        if dlg.ShowModal() == wx.ID_OK:
+            location, mapset = dlg.GetValues()
+            dlg.Destroy()
+            
+            if not location or not mapset:
+                GError(parent = self,
+                       message = _("No location/mapset provided. Operation canceled."))
+                return # this should not happen
+            
+            if RunCommand('g.mapset', parent = self,
+                          location = location,
+                          mapset = mapset) != 0:
+                return # error reported
+            
+            # close workspace
+            self.OnWorkspaceClose()
+            self.OnWorkspaceNew()
+            GMessage(parent = self,
+                     message = _("Current location is <%(loc)s>.\n"
+                                 "Current mapset is <%(mapset)s>.") % \
+                         { 'loc' : location, 'mapset' : mapset })
+        
+    def OnCreateMapset(self, event):
+        """!Create new mapset"""
+        dlg = wx.TextEntryDialog(parent = self,
+                                 message = _('Enter name for new mapset:'),
+                                 caption = _('Create new mapset'))
+        
+        if dlg.ShowModal() ==  wx.ID_OK:
+            mapset = dlg.GetValue()
+            if not mapset:
+                GError(parent = self,
+                       message = _("No mapset provided. Operation canceled."))
+                return
+            
+            ret = RunCommand('g.mapset',
+                             parent = self,
+                             flags = 'c',
+                             mapset = mapset)
+            if ret == 0:
+                GMessage(parent = self,
+                         message = _("Current mapset is <%s>.") % mapset)
+            
+    def OnChangeMapset(self, event):
+        """Change current mapset"""
+        dlg = MapsetDialog(parent = self)
+        
+        if dlg.ShowModal() == wx.ID_OK:
+            mapset = dlg.GetMapset()
+            dlg.Destroy()
+            
+            if not mapset:
+                GError(parent = self,
+                       message = _("No mapset provided. Operation canceled."))
+                return
+            
+            if RunCommand('g.mapset',
+                          parent = self,
+                          mapset = mapset) == 0:
+                GMessage(parent = self,
+                         message = _("Current mapset is <%s>.") % mapset)
+        
+    def OnNewVector(self, event):
+        """!Create new vector map layer"""
+        dlg = CreateNewVector(self, log = self.goutput,
+                              cmd = (('v.edit',
+                                      { 'tool' : 'create' },
+                                      'map')))
+        
+        if not dlg:
+            return
+        
+        name = dlg.GetName(full = True)
+        if name and dlg.IsChecked('add'):
+            # add layer to map layer tree
+            self.curr_page.maptree.AddLayer(ltype = 'vector',
+                                            lname = name,
+                                            lcmd = ['d.vect', 'map=%s' % name])
+        dlg.Destroy()
+        
+    def OnAboutGRASS(self, event):
+        """!Display 'About GRASS' dialog"""
+        win = AboutWindow(self)
+        win.CentreOnScreen()
+        win.Show(True)  
+
+    def _popupMenu(self, data):
+        """!Create popup menu
+        """
+        point = wx.GetMousePosition()
+        menu = wx.Menu()
+        
+        for key, handler in data:
+            if key is None:
+                menu.AppendSeparator()
+                continue
+            item = wx.MenuItem(menu, wx.ID_ANY, LMIcons[key].GetLabel())
+            item.SetBitmap(LMIcons[key].GetBitmap(self.iconsize))
+            menu.AppendItem(item)
+            self.Bind(wx.EVT_MENU, handler, item)
+        
+        # create menu
+        self.PopupMenu(menu)
+        menu.Destroy()
+
+    def OnImportMenu(self, event):
+        """!Import maps menu (import, link)
+        """
+        self._popupMenu((('rastImport',    self.OnImportGdalLayers),
+                         ('vectImport',    self.OnImportOgrLayers)))
+        
+    def OnWorkspaceNew(self, event = None):
+        """!Create new workspace file
+
+        Erase current workspace settings first
+        """
+        Debug.msg(4, "GMFrame.OnWorkspaceNew():")
+        
+        # start new map display if no display is available
+        if not self.curr_page:
+            self.NewDisplay()
+        
+        maptree = self.curr_page.maptree
+        
+        # ask user to save current settings
+        if self.workspaceFile and self.workspaceChanged:
+            self.OnWorkspaceSave()
+        elif self.workspaceFile is None and maptree.GetCount() > 0:
+             dlg = wx.MessageDialog(self, message = _("Current workspace is not empty. "
+                                                    "Do you want to store current settings "
+                                                    "to workspace file?"),
+                                    caption = _("Create new workspace?"),
+                                    style = wx.YES_NO | wx.YES_DEFAULT | \
+                                        wx.CANCEL | wx.ICON_QUESTION)
+             ret = dlg.ShowModal()
+             if ret == wx.ID_YES:
+                 self.OnWorkspaceSaveAs()
+             elif ret == wx.ID_CANCEL:
+                 dlg.Destroy()
+                 return
+             
+             dlg.Destroy()
+        
+        # delete all items
+        maptree.DeleteAllItems()
+        
+        # add new root element
+        maptree.root = maptree.AddRoot("Map Layers")
+        self.curr_page.maptree.SetPyData(maptree.root, (None,None))
+        
+        # no workspace file loaded
+        self.workspaceFile = None
+        self.workspaceChanged = False
+        self.SetTitle(self.baseTitle)
+        
+    def OnWorkspaceOpen(self, event = None):
+        """!Open file with workspace definition"""
+        dlg = wx.FileDialog(parent = self, message = _("Choose workspace file"),
+                            defaultDir = os.getcwd(), wildcard = _("GRASS Workspace File (*.gxw)|*.gxw"))
+
+        filename = ''
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
+
+        if filename == '':
+            return
+
+        Debug.msg(4, "GMFrame.OnWorkspaceOpen(): filename=%s" % filename)
+        
+        # delete current layer tree content
+        self.OnWorkspaceClose()
+        
+        self.LoadWorkspaceFile(filename)
+
+        self.workspaceFile = filename
+        self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.workspaceFile))
+
+    def LoadWorkspaceFile(self, filename):
+        """!Load layer tree definition stored in GRASS Workspace XML file (gxw)
+
+        @todo Validate against DTD
+        
+        @return True on success
+        @return False on error
+        """
+        # dtd
+        dtdFilename = os.path.join(globalvar.ETCWXDIR, "xml", "grass-gxw.dtd")
+        
+        # parse workspace file
+        try:
+            gxwXml = ProcessWorkspaceFile(etree.parse(filename))
+        except Exception, e:
+            GError(parent = self,
+                   message = _("Reading workspace file <%s> failed.\n"
+                                    "Invalid file, unable to parse XML document.") % filename)
+            return
+        
+        busy = wx.BusyInfo(message = _("Please wait, loading workspace..."),
+                           parent = self)
+        wx.Yield()
+
+        #
+        # load layer manager window properties
+        #
+        if not UserSettings.Get(group = 'general', key = 'workspace',
+                                subkey = ['posManager', 'enabled']):
+            if gxwXml.layerManager['pos']:
+                self.SetPosition(gxwXml.layerManager['pos'])
+            if gxwXml.layerManager['size']:
+                self.SetSize(gxwXml.layerManager['size'])
+        
+        #
+        # start map displays first (list of layers can be empty)
+        #
+        displayId = 0
+        mapdisplay = list()
+        for display in gxwXml.displays:
+            mapdisp = self.NewDisplay(name = display['name'], show = False)
+            mapdisplay.append(mapdisp)
+            maptree = self.gm_cb.GetPage(displayId).maptree
+            
+            # set windows properties
+            mapdisp.SetProperties(render = display['render'],
+                                  mode = display['mode'],
+                                  showCompExtent = display['showCompExtent'],
+                                  alignExtent = display['alignExtent'],
+                                  constrainRes = display['constrainRes'],
+                                  projection = display['projection']['enabled'])
+
+            if display['projection']['enabled']:
+                if display['projection']['epsg']:
+                    UserSettings.Set(group = 'display', key = 'projection', subkey = 'epsg',
+                                     value = display['projection']['epsg'])
+                    if display['projection']['proj']:
+                        UserSettings.Set(group = 'display', key = 'projection', subkey = 'proj4',
+                                         value = display['projection']['proj'])
+            
+            # set position and size of map display
+            if not UserSettings.Get(group = 'general', key = 'workspace', subkey = ['posDisplay', 'enabled']):
+                if display['pos']:
+                    mapdisp.SetPosition(display['pos'])
+                if display['size']:
+                    mapdisp.SetSize(display['size'])
+                    
+            # set extent if defined
+            if display['extent']:
+                w, s, e, n = display['extent']
+                region = maptree.Map.region = maptree.Map.GetRegion(w = w, s = s, e = e, n = n)
+                mapdisp.GetWindow().ResetZoomHistory()
+                mapdisp.GetWindow().ZoomHistory(region['n'],
+                                                region['s'],
+                                                region['e'],
+                                                region['w'])
+                
+            mapdisp.Show()
+            
+            displayId += 1
+    
+        maptree = None 
+        selected = [] # list of selected layers
+        # 
+        # load list of map layers
+        #
+        for layer in gxwXml.layers:
+            display = layer['display']
+            maptree = self.gm_cb.GetPage(display).maptree
+            
+            newItem = maptree.AddLayer(ltype = layer['type'],
+                                       lname = layer['name'],
+                                       lchecked = layer['checked'],
+                                       lopacity = layer['opacity'],
+                                       lcmd = layer['cmd'],
+                                       lgroup = layer['group'],
+                                       lnviz = layer['nviz'],
+                                       lvdigit = layer['vdigit'])
+            
+            if layer.has_key('selected'):
+                if layer['selected']:
+                    selected.append((maptree, newItem))
+                else:
+                    maptree.SelectItem(newItem, select = False)
+            
+        for maptree, layer in selected:
+            if not maptree.IsSelected(layer):
+                maptree.SelectItem(layer, select = True)
+                maptree.layer_selected = layer
+                
+        busy.Destroy()
+            
+        for idx, mdisp in enumerate(mapdisplay):
+            mdisp.MapWindow2D.UpdateMap()
+            #nviz
+            if gxwXml.displays[idx]['viewMode'] == '3d':
+                mdisp.AddNviz()
+                self.nviz.UpdateState(view = gxwXml.nviz_state['view'],
+                                              iview = gxwXml.nviz_state['iview'],
+                                              light = gxwXml.nviz_state['light'])
+                mdisp.MapWindow3D.constants = gxwXml.nviz_state['constants']
+                for idx, constant in enumerate(mdisp.MapWindow3D.constants):
+                    mdisp.MapWindow3D.AddConstant(constant, idx + 1)
+                for page in ('view', 'light', 'fringe', 'constant', 'cplane'):
+                    self.nviz.UpdatePage(page)
+                self.nviz.UpdateSettings()
+                mapdisp.toolbars['map'].combo.SetSelection(1)
+
+
+        return True
+    
+    def OnWorkspaceLoadGrcFile(self, event):
+        """!Load map layers from GRC file (Tcl/Tk GUI) into map layer tree"""
+        dlg = wx.FileDialog(parent = self, message = _("Choose GRC file to load"),
+                            defaultDir = os.getcwd(), wildcard = _("Old GRASS Workspace File (*.grc)|*.grc"))
+
+        filename = ''
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
+
+        if filename == '':
+            return
+
+        Debug.msg(4, "GMFrame.OnWorkspaceLoadGrcFile(): filename=%s" % filename)
+
+        # start new map display if no display is available
+        if not self.curr_page:
+            self.NewDisplay()
+
+        busy = wx.BusyInfo(message = _("Please wait, loading workspace..."),
+                           parent = self)
+        wx.Yield()
+
+        maptree = None
+        for layer in ProcessGrcFile(filename).read(self):
+            maptree = self.gm_cb.GetPage(layer['display']).maptree
+            newItem = maptree.AddLayer(ltype = layer['type'],
+                                       lname = layer['name'],
+                                       lchecked = layer['checked'],
+                                       lopacity = layer['opacity'],
+                                       lcmd = layer['cmd'],
+                                       lgroup = layer['group'])
+
+            busy.Destroy()
+            
+        if maptree:
+            # reverse list of map layers
+            maptree.Map.ReverseListOfLayers()
+
+    def OnWorkspaceSaveAs(self, event = None):
+        """!Save workspace definition to selected file"""
+        dlg = wx.FileDialog(parent = self, message = _("Choose file to save current workspace"),
+                            defaultDir = os.getcwd(), wildcard = _("GRASS Workspace File (*.gxw)|*.gxw"), style = wx.FD_SAVE)
+
+        filename = ''
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
+
+        if filename == '':
+            return False
+
+        # check for extension
+        if filename[-4:] != ".gxw":
+            filename += ".gxw"
+
+        if os.path.exists(filename):
+            dlg = wx.MessageDialog(self, message = _("Workspace file <%s> already exists. "
+                                                     "Do you want to overwrite this file?") % filename,
+                                   caption = _("Save workspace"), style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+            if dlg.ShowModal() != wx.ID_YES:
+                dlg.Destroy()
+                return False
+
+        Debug.msg(4, "GMFrame.OnWorkspaceSaveAs(): filename=%s" % filename)
+
+        self.SaveToWorkspaceFile(filename)
+        self.workspaceFile = filename
+        self.SetTitle(self.baseTitle + " - " + os.path.basename(self.workspaceFile))
+
+    def OnWorkspaceSave(self, event = None):
+        """!Save file with workspace definition"""
+        if self.workspaceFile:
+            dlg = wx.MessageDialog(self, message = _("Workspace file <%s> already exists. "
+                                                   "Do you want to overwrite this file?") % \
+                                       self.workspaceFile,
+                                   caption = _("Save workspace"), style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+            if dlg.ShowModal() == wx.ID_NO:
+                dlg.Destroy()
+            else:
+                Debug.msg(4, "GMFrame.OnWorkspaceSave(): filename=%s" % self.workspaceFile)
+                self.SaveToWorkspaceFile(self.workspaceFile)
+                self.SetTitle(self.baseTitle + " - " + os.path.basename(self.workspaceFile))
+                self.workspaceChanged = False
+        else:
+            self.OnWorkspaceSaveAs()
+
+    def SaveToWorkspaceFile(self, filename):
+        """!Save layer tree layout to workspace file
+        
+        Return True on success, False on error
+        """
+        tmpfile = tempfile.TemporaryFile(mode = 'w+b')
+        try:
+            WriteWorkspaceFile(lmgr = self, file = tmpfile)
+        except StandardError, e:
+            GError(parent = self,
+                   message = _("Writing current settings to workspace file "
+                               "failed."))
+            return False
+        
+        try:
+            mfile = open(filename, "w")
+            tmpfile.seek(0)
+            for line in tmpfile.readlines():
+                mfile.write(line)
+        except IOError:
+            GError(parent = self,
+                   message = _("Unable to open file <%s> for writing.") % filename)
+            return False
+        
+        mfile.close()
+        
+        return True
+    
+    def OnWorkspaceClose(self, event = None):
+        """!Close file with workspace definition
+        
+        If workspace has been modified ask user to save the changes.
+        """
+        Debug.msg(4, "GMFrame.OnWorkspaceClose(): file=%s" % self.workspaceFile)
+        
+        self.OnDisplayCloseAll()
+        self.workspaceFile = None
+        self.workspaceChanged = False
+        self.SetTitle(self.baseTitle)
+        self.disp_idx = 0
+        self.curr_page = None
+        
+    def OnDisplayClose(self, event = None):
+        """!Close current map display window
+        """
+        if self.curr_page and self.curr_page.maptree.mapdisplay:
+            self.curr_page.maptree.mapdisplay.OnCloseWindow(event)
+        
+    def OnDisplayCloseAll(self, event = None):
+        """!Close all open map display windows
+        """
+        displays = list()
+        for page in range(0, self.gm_cb.GetPageCount()):
+            displays.append(self.gm_cb.GetPage(page).maptree.mapdisplay)
+        
+        for display in displays:
+            display.OnCloseWindow(event)
+        
+    def OnRenameDisplay(self, event):
+        """!Change Map Display name"""
+        name = self.gm_cb.GetPageText(self.curr_pagenum)
+        dlg = wx.TextEntryDialog(self, message = _("Enter new name:"),
+                                 caption = _("Rename Map Display"), defaultValue = name)
+        if dlg.ShowModal() == wx.ID_OK:
+            name = dlg.GetValue()
+            self.gm_cb.SetPageText(page = self.curr_pagenum, text = name)
+            mapdisplay = self.curr_page.maptree.mapdisplay
+            mapdisplay.SetTitle(_("GRASS GIS Map Display: %(name)s  - Location: %(loc)s") % \
+                                     { 'name' : name,
+                                       'loc' : grass.gisenv()["LOCATION_NAME"] })
+        dlg.Destroy()
+        
+    def RulesCmd(self, event):
+        """!Launches dialog for commands that need rules input and
+        processes rules
+        """
+        cmd = self.GetMenuCmd(event)
+        
+        if cmd[0] == 'r.colors':
+            ctable = RasterColorTable(self)
+        else:
+            ctable = VectorColorTable(self, attributeType = 'color')
+        ctable.CentreOnScreen()
+        ctable.Show()
+        
+    def OnXTermNoXMon(self, event):
+        """!
+        Run commands that need xterm
+        """
+        self.OnXTerm(event, need_xmon = False)
+        
+    def OnXTerm(self, event, need_xmon = True):
+        """!
+        Run commands that need interactive xmon
+
+        @param need_xmon True to start X monitor
+        """
+        # unset display mode
+        del os.environ['GRASS_RENDER_IMMEDIATE']
+        
+        if need_xmon:
+            # open next available xmon
+            xmonlist = []
+            
+            # make list of xmons that are not running
+            ret = RunCommand('d.mon',
+                             flags = 'L',
+                             read = True)
+            
+            for line in ret.split('\n'):               
+                line = line.strip()
+                if line.startswith('x') and 'not running' in line:
+                    xmonlist.append(line[0:2])
+            
+            # find available xmon
+            xmon = xmonlist[0]
+            
+            # bring up the xmon
+            cmdlist = ['d.mon', xmon]
+            p = Command(cmdlist, wait=False)
+        
+        # run the command        
+        command = self.GetMenuCmd(event)
+        command = ' '.join(command)
+        
+        gisbase = os.environ['GISBASE']
+        
+        if sys.platform == "win32":
+            runbat = os.path.join(gisbase,'etc','grass-run.bat')
+            cmdlist = ["start", runbat, runbat, command]
+        else:
+            if sys.platform == "darwin":
+                xtermwrapper = os.path.join(gisbase,'etc','grass-xterm-mac')
+            else:
+                xtermwrapper = os.path.join(gisbase,'etc','grass-xterm-wrapper')
+            
+            grassrun = os.path.join(gisbase,'etc','grass-run.sh')
+            cmdlist = [xtermwrapper, '-e', grassrun, command]
+        
+        p = Command(cmdlist, wait=False)
+        
+        # reset display mode
+        os.environ['GRASS_RENDER_IMMEDIATE'] = 'TRUE'
+        
+    def OnEditImageryGroups(self, event, cmd = None):
+        """!Show dialog for creating and editing groups.
+        """
+        dlg = GroupDialog(self)
+        dlg.CentreOnScreen()
+        dlg.Show()
+        
+    def OnInstallExtension(self, event):
+        """!Install extension from GRASS Addons SVN repository"""
+        win = InstallExtensionWindow(self, size = (650, 550))
+        win.CentreOnScreen()
+        win.Show()
+
+    def OnUninstallExtension(self, event):
+        """!Uninstall extension"""
+        win = UninstallExtensionWindow(self, size = (650, 300))
+        win.CentreOnScreen()
+        win.Show()
+
+    def OnPreferences(self, event):
+        """!General GUI preferences/settings
+        """
+        if not self.dialogs['preferences']:
+            dlg = PreferencesDialog(parent = self)
+            self.dialogs['preferences'] = dlg
+            self.dialogs['preferences'].CenterOnScreen()
+            
+            dlg.Bind(EVT_SETTINGS_CHANGED, self.OnSettingsChanged)
+        
+        self.dialogs['preferences'].ShowModal()
+        
+    def OnHelp(self, event):
+        """!Show help
+        """
+        self.goutput.RunCmd(['g.manual','-i'])
+        
+    def OnHistogram(self, event):
+        """!Init histogram display canvas and tools
+        """
+        from modules.histogram import HistogramFrame
+        win = HistogramFrame(self)
+        
+        win.CentreOnScreen()
+        win.Show()
+        win.Refresh()
+        win.Update()
+        
+    def OnProfile(self, event):
+        """!Launch profile tool
+        """
+        win = profile.ProfileFrame(parent = self)
+        
+        win.CentreOnParent()
+        win.Show()
+        win.Refresh()
+        win.Update()
+        
+    def OnMapCalculator(self, event, cmd = ''):
+        """!Init map calculator for interactive creation of mapcalc statements
+        """
+        if event:
+            try:
+                cmd = self.GetMenuCmd(event)
+            except KeyError:
+                cmd = ['r.mapcalc']
+        
+        win = MapCalcFrame(parent = self,
+                           cmd = cmd[0])
+        win.CentreOnScreen()
+        win.Show()
+        
+    def OnVectorCleaning(self, event, cmd = ''):
+        """!Init interactive vector cleaning
+        """
+        from modules.vclean import VectorCleaningFrame
+        win = VectorCleaningFrame(parent = self)
+        win.CentreOnScreen()
+        win.Show()
+        
+    def OnImportDxfFile(self, event, cmd = None):
+        """!Convert multiple DXF layers to GRASS vector map layers"""
+        dlg = DxfImportDialog(parent = self)
+        dlg.CentreOnScreen()
+        dlg.Show()
+
+    def OnImportGdalLayers(self, event, cmd = None):
+        """!Convert multiple GDAL layers to GRASS raster map layers"""
+        dlg = GdalImportDialog(parent = self)
+        dlg.CentreOnScreen()
+        dlg.Show()
+
+    def OnLinkGdalLayers(self, event, cmd = None):
+        """!Link multiple GDAL layers to GRASS raster map layers"""
+        dlg = GdalImportDialog(parent = self, link = True)
+        dlg.CentreOnScreen()
+        dlg.Show()
+        
+    def OnImportOgrLayers(self, event, cmd = None):
+        """!Convert multiple OGR layers to GRASS vector map layers"""
+        dlg = GdalImportDialog(parent = self, ogr = True)
+        dlg.CentreOnScreen()
+        dlg.Show()
+        
+    def OnLinkOgrLayers(self, event, cmd = None):
+        """!Links multiple OGR layers to GRASS vector map layers"""
+        dlg = GdalImportDialog(parent = self, ogr = True, link = True)
+        dlg.CentreOnScreen()
+        dlg.Show()
+        
+    def OnImportWMS(self, event):
+        """!Import data from OGC WMS server"""
+        dlg = WMSDialog(parent = self, service = 'wms')
+        dlg.CenterOnScreen()
+        
+        if dlg.ShowModal() == wx.ID_OK: # -> import layers
+            layers = dlg.GetLayers()
+            
+            if len(layers.keys()) > 0:
+                for layer in layers.keys():
+                    cmd = ['r.in.wms',
+                           'mapserver=%s' % dlg.GetSettings()['server'],
+                           'layers=%s' % layer,
+                           'output=%s' % layer,
+                           'format=png',
+                           '--overwrite']
+                    styles = ','.join(layers[layer])
+                    if styles:
+                        cmd.append('styles=%s' % styles)
+                    self.goutput.RunCmd(cmd, switchPage = True)
+
+                    self.curr_page.maptree.AddLayer(ltype = 'raster',
+                                                    lname = layer,
+                                                    lcmd = ['d.rast', 'map=%s' % layer],
+                                                    multiple = False)
+            else:
+                self.goutput.WriteWarning(_("Nothing to import. No WMS layer selected."))
+                
+                
+        dlg.Destroy()
+        
+    def OnShowAttributeTable(self, event, selection = None):
+        """!Show attribute table of the given vector map layer
+        """
+        if not self.curr_page:
+            self.MsgNoLayerSelected()
+            return
+        
+        tree = self.GetLayerTree()
+        layer = tree.layer_selected
+        # no map layer selected
+        if not layer:
+            self.MsgNoLayerSelected()
+            return
+        
+        # available only for vector map layers
+        try:
+            maptype = tree.GetPyData(layer)[0]['maplayer'].type
+        except:
+            maptype = None
+        
+        if not maptype or maptype != 'vector':
+            GMessage(parent = self,
+                          message = _("Selected map layer is not vector."))
+            return
+        
+        if not tree.GetPyData(layer)[0]:
+            return
+        dcmd = tree.GetPyData(layer)[0]['cmd']
+        if not dcmd:
+            return
+        
+        busy = wx.BusyInfo(message = _("Please wait, loading attribute data..."),
+                           parent = self)
+        wx.Yield()
+        
+        dbmanager = AttributeManager(parent = self, id = wx.ID_ANY,
+                                     size = wx.Size(500, 300),
+                                     item = layer, log = self.goutput,
+                                     selection = selection)
+        
+        busy.Destroy()
+        
+        # register ATM dialog
+        self.dialogs['atm'].append(dbmanager)
+        
+        # show ATM window
+        dbmanager.Show()
+        
+    def OnNewDisplayWMS(self, event = None):
+        """!Create new layer tree and map display instance"""
+        self.NewDisplayWMS()
+
+    def OnNewDisplay(self, event = None):
+        """!Create new layer tree and map display instance"""
+        self.NewDisplay()
+
+    def NewDisplay(self, name = None, show = True):
+        """!Create new layer tree, which will
+        create an associated map display frame
+
+        @param name name of new map display
+        @param show show map display window if True
+
+        @return reference to mapdisplay intance
+        """
+        Debug.msg(1, "GMFrame.NewDisplay(): idx=%d" % self.disp_idx)
+        
+        # make a new page in the bookcontrol for the layer tree (on page 0 of the notebook)
+        self.pg_panel = wx.Panel(self.gm_cb, id = wx.ID_ANY, style = wx.EXPAND)
+        if name:
+            dispName = name
+        else:
+            dispName = "Display " + str(self.disp_idx + 1)
+        self.gm_cb.AddPage(self.pg_panel, text = dispName, select = True)
+        self.curr_page = self.gm_cb.GetCurrentPage()
+        
+        # create layer tree (tree control for managing GIS layers)  and put on new notebook page
+        self.curr_page.maptree = LayerTree(self.curr_page, id = wx.ID_ANY, pos = wx.DefaultPosition,
+                                           size = wx.DefaultSize, style = wx.TR_HAS_BUTTONS |
+                                           wx.TR_LINES_AT_ROOT| wx.TR_HIDE_ROOT |
+                                           wx.TR_DEFAULT_STYLE| wx.NO_BORDER | wx.FULL_REPAINT_ON_RESIZE,
+                                           idx = self.disp_idx, lmgr = self, notebook = self.gm_cb,
+                                           auimgr = self._auimgr, showMapDisplay = show)
+        
+        # layout for controls
+        cb_boxsizer = wx.BoxSizer(wx.VERTICAL)
+        cb_boxsizer.Add(self.curr_page.maptree, proportion = 1, flag = wx.EXPAND, border = 1)
+        self.curr_page.SetSizer(cb_boxsizer)
+        cb_boxsizer.Fit(self.curr_page.maptree)
+        self.curr_page.Layout()
+        self.curr_page.maptree.Layout()
+        
+        # use default window layout
+        if UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'enabled'):
+            dim = UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'dim')
+            idx = 4 + self.disp_idx * 4
+            try:
+                x, y = map(int, dim.split(',')[idx:idx + 2])
+                w, h = map(int, dim.split(',')[idx + 2:idx + 4])
+                self.curr_page.maptree.mapdisplay.SetPosition((x, y))
+                self.curr_page.maptree.mapdisplay.SetSize((w, h))
+            except:
+                pass
+        
+        self.disp_idx += 1
+        
+        return self.curr_page.maptree.mapdisplay
+    
+    def OnAddMaps(self, event = None):
+        """!Add selected map layers into layer tree"""
+        dialog = MapLayersDialog(parent = self, title = _("Add selected map layers into layer tree"))
+        
+        if dialog.ShowModal() != wx.ID_OK:
+            dialog.Destroy()
+            return
+        
+        # start new map display if no display is available
+        if not self.curr_page:
+            self.NewDisplay()
+            
+        maptree = self.curr_page.maptree
+        
+        for layerName in dialog.GetMapLayers():
+            ltype = dialog.GetLayerType(cmd = True)
+            if ltype == 'rast':
+                cmd = ['d.rast', 'map=%s' % layerName]
+                wxType = 'raster'
+            elif ltype == 'rast3d':
+                cmd = ['d.rast3d', 'map=%s' % layerName]
+                wxType = '3d-raster'
+            elif ltype == 'vect':
+                cmd = ['d.vect', 'map=%s' % layerName]
+                wxType = 'vector'
+            else:
+                GError(parent = self,
+                       message = _("Unsupported map layer type <%s>.") % ltype)
+                return
+            
+            newItem = maptree.AddLayer(ltype = wxType,
+                                       lname = layerName,
+                                       lchecked = False,
+                                       lopacity = 1.0,
+                                       lcmd = cmd,
+                                       lgroup = None)
+        dialog.Destroy()
+        
+    def OnAddRaster(self, event):
+        """!Add raster map layer"""
+        # start new map display if no display is available
+        if not self.curr_page:
+            self.NewDisplay(show = True)
+        
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('raster')
+        
+    def OnAddRaster3D(self, event):
+        """!Add 3D raster map layer"""
+        # start new map display if no display is available
+        if not self.curr_page:
+            self.NewDisplay(show = True)
+        
+        self.AddRaster3D(event)
+        
+    def OnAddRasterMisc(self, event):
+        """!Create misc raster popup-menu"""
+        # start new map display if no display is available
+        if not self.curr_page:
+            self.NewDisplay(show = True)
+        
+        self._popupMenu((('addRast3d', self.OnAddRaster3D),
+                         (None, None),
+                         ('addRgb',    self.OnAddRasterRGB),
+                         ('addHis',    self.OnAddRasterHIS),
+                         (None, None),
+                         ('addShaded', self.OnAddRasterShaded),
+                         (None, None),
+                         ('addRArrow', self.OnAddRasterArrow),
+                         ('addRNum',   self.OnAddRasterNum)))
+        
+        # show map display
+        self.curr_page.maptree.mapdisplay.Show()
+        
+    def OnAddVector(self, event):
+        """!Add vector map to the current layer tree"""
+        # start new map display if no display is available
+        if not self.curr_page:
+            self.NewDisplay(show = True)
+        
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('vector')
+
+    def OnAddVectorMisc(self, event):
+        """!Create misc vector popup-menu"""
+        # start new map display if no display is available
+        if not self.curr_page:
+            self.NewDisplay(show = True)
+
+        self._popupMenu((('addThematic', self.OnAddVectorTheme),
+                         ('addChart',    self.OnAddVectorChart)))
+        
+        # show map display
+        self.curr_page.maptree.mapdisplay.Show()
+
+    def OnAddVectorTheme(self, event):
+        """!Add thematic vector map to the current layer tree"""
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('thememap')
+
+    def OnAddVectorChart(self, event):
+        """!Add chart vector map to the current layer tree"""
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('themechart')
+
+    def OnAddOverlay(self, event):
+        """!Create decoration overlay menu""" 
+        # start new map display if no display is available
+        if not self.curr_page:
+            self.NewDisplay(show = True)
+
+        self._popupMenu((('addGrid',     self.OnAddGrid),
+                         ('addLabels',   self.OnAddLabels),
+                         ('addGeodesic', self.OnAddGeodesic),
+                         ('addRhumb',    self.OnAddRhumb),
+                         (None, None),
+                         ('addCmd',      self.OnAddCommand)))
+        
+        # show map display
+        self.curr_page.maptree.mapdisplay.Show()
+        
+    def OnAddRaster3D(self, event):
+        """!Add 3D raster map to the current layer tree"""
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('3d-raster')
+
+    def OnAddRasterRGB(self, event):
+        """!Add RGB raster map to the current layer tree"""
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('rgb')
+
+    def OnAddRasterHIS(self, event):
+        """!Add HIS raster map to the current layer tree"""
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('his')
+
+    def OnAddRasterShaded(self, event):
+        """!Add shaded relief raster map to the current layer tree"""
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('shaded')
+
+    def OnAddRasterArrow(self, event):
+        """!Add flow arrows raster map to the current layer tree"""
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('rastarrow')
+
+    def OnAddRasterNum(self, event):
+        """!Add cell number raster map to the current layer tree"""
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('rastnum')
+
+    def OnAddCommand(self, event):
+        """!Add command line map layer to the current layer tree"""
+        # start new map display if no display is available
+        if not self.curr_page:
+            self.NewDisplay(show = True)
+
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('command')
+
+        # show map display
+        self.curr_page.maptree.mapdisplay.Show()
+
+    def OnAddGroup(self, event):
+        """!Add layer group"""
+        # start new map display if no display is available
+        if not self.curr_page:
+            self.NewDisplay(show = True)
+
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('group')
+
+        # show map display
+        self.curr_page.maptree.mapdisplay.Show()
+
+    def OnAddGrid(self, event):
+        """!Add grid map layer to the current layer tree"""
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('grid')
+
+    def OnAddGeodesic(self, event):
+        """!Add geodesic line map layer to the current layer tree"""
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('geodesic')
+
+    def OnAddRhumb(self, event):
+        """!Add rhumb map layer to the current layer tree"""
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('rhumb')
+
+    def OnAddLabels(self, event):
+        """!Add vector labels map layer to the current layer tree"""
+        # start new map display if no display is available
+        if not self.curr_page:
+            self.NewDisplay(show = True)
+
+        self.notebook.SetSelectionByName('layers')
+        self.curr_page.maptree.AddLayer('labels')
+
+        # show map display
+        self.curr_page.maptree.mapdisplay.Show()
+
+    def OnDeleteLayer(self, event):
+        """!Remove selected map layer from the current layer Tree
+        """
+        if not self.curr_page or not self.curr_page.maptree.layer_selected:
+            self.MsgNoLayerSelected()
+            return
+
+        if UserSettings.Get(group = 'manager', key = 'askOnRemoveLayer', subkey = 'enabled'):
+            layerName = ''
+            for item in self.curr_page.maptree.GetSelections():
+                name = str(self.curr_page.maptree.GetItemText(item))
+                idx = name.find('(opacity')
+                if idx > -1:
+                    layerName += '<' + name[:idx].strip(' ') + '>,\n'
+                else:
+                    layerName += '<' + name + '>,\n'
+            layerName = layerName.rstrip(',\n')
+            
+            if len(layerName) > 2: # <>
+                message = _("Do you want to remove map layer(s)\n%s\n"
+                            "from layer tree?") % layerName
+            else:
+                message = _("Do you want to remove selected map layer(s) "
+                            "from layer tree?")
+
+            dlg = wx.MessageDialog (parent = self, message = message,
+                                    caption = _("Remove map layer"),
+                                    style  =  wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+
+            if dlg.ShowModal() != wx.ID_YES:
+                dlg.Destroy()
+                return
+            
+            dlg.Destroy()
+
+        for layer in self.curr_page.maptree.GetSelections():
+            if self.curr_page.maptree.GetPyData(layer)[0]['type'] == 'group':
+                self.curr_page.maptree.DeleteChildren(layer)
+            self.curr_page.maptree.Delete(layer)
+        
+    def OnKeyDown(self, event):
+        """!Key pressed"""
+        kc = event.GetKeyCode()
+        
+        if event.ControlDown():
+            if kc == wx.WXK_TAB:
+                # switch layer list / command output
+                if self.notebook.GetSelection() == self.notebook.GetPageIndexByName('layers'):
+                    self.notebook.SetSelectionByName('output')
+                else:
+                    self.notebook.SetSelectionByName('layers')
+        
+        try:
+            ckc = chr(kc)
+        except ValueError:
+            event.Skip()
+            return
+        
+        if event.CtrlDown():
+            if kc == 'R':
+                self.OnAddRaster(None)
+            elif kc == 'V':
+                self.OnAddVector(None)
+        
+        event.Skip()
+
+    def OnCloseWindow(self, event):
+        """!Cleanup when wxGUI is quitted"""
+        if not self.curr_page:
+            self._auimgr.UnInit()
+            self.Destroy()
+            return
+        
+        maptree = self.curr_page.maptree
+        if self.workspaceChanged and \
+                UserSettings.Get(group = 'manager', key = 'askOnQuit', subkey = 'enabled'):
+            if self.workspaceFile:
+                message = _("Do you want to save changes in the workspace?")
+            else:
+                message = _("Do you want to store current settings "
+                            "to workspace file?")
+            
+            # ask user to save current settings
+            if maptree.GetCount() > 0:
+                dlg = wx.MessageDialog(self,
+                                       message = message,
+                                       caption = _("Quit GRASS GUI"),
+                                       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.workspaceFile:
+                        self.OnWorkspaceSaveAs()
+                    else:
+                        self.SaveToWorkspaceFile(self.workspaceFile)
+                elif ret == wx.ID_CANCEL:
+                    event.Veto()
+                    dlg.Destroy()
+                    return
+                dlg.Destroy()
+        
+        # don't ask any more...
+        UserSettings.Set(group = 'manager', key = 'askOnQuit', subkey = 'enabled',
+                         value = False)
+        
+        self.OnDisplayCloseAll()
+        
+        self.gm_cb.DeleteAllPages()
+        
+        self._auimgr.UnInit()
+        self.Destroy()
+        
+    def MsgNoLayerSelected(self):
+        """!Show dialog message 'No layer selected'"""
+        wx.MessageBox(parent = self,
+                      message = _("No map layer selected. Operation canceled."),
+                      caption = _("Message"),
+                      style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/lmgr/layertree.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/lmgr/layertree.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/lmgr/layertree.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1587 @@
+"""!
+ at package lmgr.layertree
+
+ at brief Utility classes for map layer management.
+
+Classes:
+ - layertree::LayerTree
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+ 
+ at author Michael Barton (Arizona State University)
+ at author Jachym Cepicky (Mendel University of Agriculture)
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import wx
+try:
+    import wx.lib.agw.customtreectrl as CT
+except ImportError:
+    import wx.lib.customtreectrl as CT
+import wx.lib.buttons  as buttons
+try:
+    import treemixin 
+except ImportError:
+    from wx.lib.mixins import treemixin
+
+from grass.script import core as grass
+
+from core                import globalvar
+from gui_core.dialogs    import SetOpacityDialog
+from gui_core.forms      import GUI
+from mapdisp.frame       import MapFrame
+from core.render         import Map
+from modules.histogram   import HistogramFrame
+from core.utils          import GetLayerNameFromCmd
+from wxplot.profile      import ProfileFrame
+from core.debug          import Debug
+from core.settings       import UserSettings
+from core.gcmd           import GWarning
+from gui_core.toolbars   import BaseIcons
+from icons.icon          import MetaIcon
+
+TREE_ITEM_HEIGHT = 25
+
+LMIcons = {
+    'rastImport' : MetaIcon(img = 'layer-import',
+                            label = _('Import raster data')),
+    'rastLink'   : MetaIcon(img = 'layer-import',
+                            label = _('Link external raster data')),
+    'rastOut'    : MetaIcon(img = 'layer-export',
+                            label = _('Set raster output format')),
+    'vectImport' : MetaIcon(img = 'layer-import',
+                            label = _('Import vector data')),
+    'vectLink'   : MetaIcon(img = 'layer-import',
+                                    label = _('Link external vector data')),
+    'vectOut'    : MetaIcon(img = 'layer-export',
+                            label = _('Set vector output format')),
+    'addCmd'     : MetaIcon(img = 'layer-command-add',
+                            label = _('Add command layer')),
+    'quit'       : MetaIcon(img = 'quit',
+                            label = _('Quit')),
+    'addRgb'     : MetaIcon(img = 'layer-rgb-add',
+                            label = _('Add RGB map layer')),
+    'addHis'     : MetaIcon(img = 'layer-his-add',
+                                    label = _('Add HIS map layer')),
+    'addShaded'  : MetaIcon(img = 'layer-shaded-relief-add',
+                            label = _('Add shaded relief map layer')),
+    'addRArrow'  : MetaIcon(img = 'layer-aspect-arrow-add',
+                            label = _('Add raster flow arrows')),
+    'addRNum'    : MetaIcon(img = 'layer-cell-cats-add',
+                            label = _('Add raster cell numbers')),
+    'addThematic': MetaIcon(img = 'layer-vector-thematic-add',
+                            label = _('Add thematic area (choropleth) map layer')),
+    'addChart'   : MetaIcon(img = 'layer-vector-chart-add',
+                            label = _('Add thematic chart layer')),
+    'addGrid'    : MetaIcon(img = 'layer-grid-add',
+                            label = _('Add grid layer')),
+    'addGeodesic': MetaIcon(img = 'shortest-distance',
+                            label = _('Add geodesic line layer')),
+    'addRhumb'   : MetaIcon(img = 'shortest-distance',
+                            label = _('Add rhumbline layer')),
+    'addLabels'  : MetaIcon(img = 'layer-label-add',
+                            label = _('Add labels')),
+    'addRast3d'  : MetaIcon(img = 'layer-raster3d-add',
+                            label = _('Add 3D raster map layer'),
+                            desc  =  _('Note that 3D raster data are rendered only in 3D view mode')),
+    'layerOptions'  : MetaIcon(img = 'options',
+                               label = _('Set options')),
+    }
+
+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 = 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
+        
+        try:
+            ctstyle |= CT.TR_ALIGN_WINDOWS
+        except AttributeError:
+            pass
+        
+        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._setGradient()
+        
+        # init associated map display
+        pos = wx.Point((self.disp_idx + 1) * 25, (self.disp_idx + 1) * 25)
+        self.mapdisplay = 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 = BaseIcons["addRast"].GetBitmap(bmpsize)
+        self.rast_icon = il.Add(trgif)
+        
+        trgif = LMIcons["addRast3d"].GetBitmap(bmpsize)
+        self.rast3d_icon = il.Add(trgif)
+        
+        trgif = LMIcons["addRgb"].GetBitmap(bmpsize)
+        self.rgb_icon = il.Add(trgif)
+        
+        trgif = LMIcons["addHis"].GetBitmap(bmpsize)
+        self.his_icon = il.Add(trgif)
+        
+        trgif = LMIcons["addShaded"].GetBitmap(bmpsize)
+        self.shaded_icon = il.Add(trgif)
+        
+        trgif = LMIcons["addRArrow"].GetBitmap(bmpsize)
+        self.rarrow_icon = il.Add(trgif)
+        
+        trgif = LMIcons["addRNum"].GetBitmap(bmpsize)
+        self.rnum_icon = il.Add(trgif)
+        
+        trgif = BaseIcons["addVect"].GetBitmap(bmpsize)
+        self.vect_icon = il.Add(trgif)
+        
+        trgif = LMIcons["addThematic"].GetBitmap(bmpsize)
+        self.theme_icon = il.Add(trgif)
+        
+        trgif = LMIcons["addChart"].GetBitmap(bmpsize)
+        self.chart_icon = il.Add(trgif)
+        
+        trgif = LMIcons["addGrid"].GetBitmap(bmpsize)
+        self.grid_icon = il.Add(trgif)
+        
+        trgif = LMIcons["addGeodesic"].GetBitmap(bmpsize)
+        self.geodesic_icon = il.Add(trgif)
+        
+        trgif = LMIcons["addRhumb"].GetBitmap(bmpsize)
+        self.rhumb_icon = il.Add(trgif)
+        
+        trgif = LMIcons["addLabels"].GetBitmap(bmpsize)
+        self.labels_icon = il.Add(trgif)
+        
+        trgif = LMIcons["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_TREE_END_LABEL_EDIT,   self.OnRenamed)
+        self.Bind(wx.EVT_KEY_UP,                self.OnKeyUp)
+        self.Bind(wx.EVT_IDLE,                  self.OnIdle)
+
+    def _setGradient(self, iType = None):
+        """!Set gradient for items
+
+        @param iType bgmap, vdigit or None
+        """
+        if iType == 'bgmap':
+            self.SetFirstGradientColour(wx.Colour(0, 100, 0))
+            self.SetSecondGradientColour(wx.Colour(0, 150, 0))
+        elif iType == 'vdigit':
+            self.SetFirstGradientColour(wx.Colour(100, 0, 0))
+            self.SetSecondGradientColour(wx.Colour(150, 0, 0))
+        else:
+            self.SetFirstGradientColour(wx.Colour(100, 100, 100))
+            self.SetSecondGradientColour(wx.Colour(150, 150, 150))
+        
+    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.GetToolbar('vdigit'):
+                vector = True
+            else:
+                vector = False
+            if self.mapdisplay.IsAutoRendered():
+                self.mapdisplay.MapWindow2D.UpdateMap(render = True, renderVector = vector)
+                if self.lmgr.IsPaneShown('toolbarNviz'): # nviz
+                    self.mapdisplay.MapWindow3D.UpdateMap(render = True)
+            
+            self.rerender = False
+        
+        event.Skip()
+        
+    def OnKeyUp(self, event):
+        """!Key pressed"""
+        key = event.GetKeyCode()
+        
+        if key == wx.WXK_DELETE and self.lmgr and \
+                not self.GetEditControl():
+            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']
+        mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        
+        Debug.msg (4, "LayerTree.OnContextMenu: layertype=%s" % \
+                       ltype)
+
+        if not hasattr (self, "popupID"):
+            self.popupID = dict()
+            for key in ('remove', 'rename', 'opacity', 'nviz', 'zoom',
+                        'region', 'export', 'attr', 'edit0', 'edit1',
+                        'bgmap', 'topo', 'meta', 'null', 'zoom1', 'region1',
+                        'color', 'hist', 'univar', 'prof', 'properties'):
+                self.popupID[key] = wx.NewId()
+        
+        self.popupMenu = wx.Menu()
+        
+        numSelected = len(self.GetSelections())
+        
+        self.popupMenu.Append(self.popupID['remove'], text = _("Remove"))
+        self.Bind(wx.EVT_MENU, self.lmgr.OnDeleteLayer, id = self.popupID['remove'])
+        
+        if ltype != "command":
+            self.popupMenu.Append(self.popupID['rename'], text = _("Rename"))
+            self.Bind(wx.EVT_MENU, self.OnRenameLayer, id = self.popupID['rename'])
+            if numSelected > 1:
+                self.popupMenu.Enable(self.popupID['rename'], False)
+        
+        # map layer items
+        if ltype not in ("group", "command"):
+            self.popupMenu.AppendSeparator()
+            self.popupMenu.Append(self.popupID['opacity'], text = _("Change opacity level"))
+            self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id = self.popupID['opacity'])
+            self.popupMenu.Append(self.popupID['properties'], text = _("Properties"))
+            self.Bind(wx.EVT_MENU, self.OnPopupProperties, id = self.popupID['properties'])
+            
+            if numSelected > 1:
+                self.popupMenu.Enable(self.popupID['opacity'], False)
+                self.popupMenu.Enable(self.popupID['properties'], False)
+            
+            if ltype in ('raster', 'vector', '3d-raster') and self.lmgr.IsPaneShown('toolbarNviz'):
+                self.popupMenu.Append(self.popupID['nviz'], _("3D view properties"))
+                self.Bind (wx.EVT_MENU, self.OnNvizProperties, id = self.popupID['nviz'])
+            
+            if ltype in ('raster', 'vector', 'rgb'):
+                self.popupMenu.Append(self.popupID['zoom'], text = _("Zoom to selected map(s)"))
+                self.Bind(wx.EVT_MENU, self.mapdisplay.OnZoomToMap, id = self.popupID['zoom'])
+                self.popupMenu.Append(self.popupID['region'], text = _("Set computational region from selected map(s)"))
+                self.Bind(wx.EVT_MENU, self.OnSetCompRegFromMap, id = self.popupID['region'])
+        
+        # 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.popupID['export'], text = _("Export"))
+            self.Bind(wx.EVT_MENU, lambda x: self.lmgr.OnMenuCmd(cmd = ['v.out.ogr',
+                                                                        'input=%s' % mapLayer.GetName()]),
+                      id = self.popupID['export'])
+            
+            self.popupMenu.AppendSeparator()
+
+            self.popupMenu.Append(self.popupID['color'], _("Set color table"))
+            self.Bind (wx.EVT_MENU, self.OnVectorColorTable, id = self.popupID['color'])
+
+            self.popupMenu.Append(self.popupID['attr'], text = _("Show attribute data"))
+            self.Bind(wx.EVT_MENU, self.lmgr.OnShowAttributeTable, id = self.popupID['attr'])
+
+            self.popupMenu.Append(self.popupID['edit0'], text = _("Start editing"))
+            self.popupMenu.Append(self.popupID['edit1'], text = _("Stop editing"))
+            self.popupMenu.Enable(self.popupID['edit1'], False)
+            self.Bind (wx.EVT_MENU, self.OnStartEditing, id = self.popupID['edit0'])
+            self.Bind (wx.EVT_MENU, self.OnStopEditing,  id = self.popupID['edit1'])
+            
+            layer = self.GetPyData(self.layer_selected)[0]['maplayer']
+            # enable editing only for vector map layers available in the current mapset
+            digitToolbar = self.mapdisplay.GetToolbar('vdigit')
+            if digitToolbar:
+                # background vector map
+                self.popupMenu.Append(self.popupID['bgmap'],
+                                      text = _("Use as background vector map for digitizer"),
+                                      kind = wx.ITEM_CHECK)
+                self.Bind(wx.EVT_MENU, self.OnSetBgMap, id = self.popupID['bgmap'])
+                if UserSettings.Get(group = 'vdigit', key = 'bgmap', subkey = 'value',
+                                    internal = True) == layer.GetName():
+                    self.popupMenu.Check(self.popupID['bgmap'], True)
+            
+            self.popupMenu.Append(self.popupID['topo'], text = _("Rebuild topology"))
+            self.Bind(wx.EVT_MENU, self.OnTopology, id = self.popupID['topo'])
+            
+            if layer.GetMapset() != grass.gisenv()['MAPSET']:
+                # only vector map in current mapset can be edited
+                self.popupMenu.Enable (self.popupID['edit0'], False)
+                self.popupMenu.Enable (self.popupID['edit1'], False)
+                self.popupMenu.Enable (self.popupID['topo'], False)
+            elif digitToolbar and digitToolbar.GetLayer():
+                # vector map already edited
+                vdigitLayer = digitToolbar.GetLayer()
+                if vdigitLayer is layer:
+                    self.popupMenu.Enable(self.popupID['edit0'],  False)
+                    self.popupMenu.Enable(self.popupID['edit1'],  True)
+                    self.popupMenu.Enable(self.popupID['remove'], False)
+                    self.popupMenu.Enable(self.popupID['bgmap'],  False)
+                    self.popupMenu.Enable(self.popupID['topo'],   False)
+                else:
+                    self.popupMenu.Enable(self.popupID['edit0'], False)
+                    self.popupMenu.Enable(self.popupID['edit1'], False)
+                    self.popupMenu.Enable(self.popupID['bgmap'], True)
+            
+            self.popupMenu.Append(self.popupID['meta'], _("Metadata"))
+            self.Bind (wx.EVT_MENU, self.OnMetadata, id = self.popupID['meta'])
+            if numSelected > 1:
+                self.popupMenu.Enable(self.popupID['attr'],   False)
+                self.popupMenu.Enable(self.popupID['edit0'],  False)
+                self.popupMenu.Enable(self.popupID['edit1'],  False)
+                self.popupMenu.Enable(self.popupID['meta'],   False)
+                self.popupMenu.Enable(self.popupID['bgmap'],  False)
+                self.popupMenu.Enable(self.popupID['topo'],   False)
+                self.popupMenu.Enable(self.popupID['export'], False)
+        
+        # raster layers (specific items)
+        elif mltype and mltype == "raster":
+            self.popupMenu.Append(self.popupID['zoom1'], text = _("Zoom to selected map(s) (ignore NULLs)"))
+            self.Bind(wx.EVT_MENU, self.mapdisplay.OnZoomToRaster, id = self.popupID['zoom1'])
+            self.popupMenu.Append(self.popupID['region1'], text = _("Set computational region from selected map(s) (ignore NULLs)"))
+            self.Bind(wx.EVT_MENU, self.OnSetCompRegFromRaster, id = self.popupID['region1'])
+            
+            self.popupMenu.AppendSeparator()
+            self.popupMenu.Append(self.popupID['export'], text = _("Export"))
+            self.Bind(wx.EVT_MENU, lambda x: self.lmgr.OnMenuCmd(cmd = ['r.out.gdal',
+                                                                        'input=%s' % mapLayer.GetName()]),
+                      id = self.popupID['export'])
+            
+            self.popupMenu.AppendSeparator()
+            self.popupMenu.Append(self.popupID['color'], _("Set color table"))
+            self.Bind (wx.EVT_MENU, self.OnRasterColorTable, id = self.popupID['color'])
+            self.popupMenu.Append(self.popupID['hist'], _("Histogram"))
+            self.Bind (wx.EVT_MENU, self.OnHistogram, id = self.popupID['hist'])
+            self.popupMenu.Append(self.popupID['univar'], _("Univariate raster statistics"))
+            self.Bind (wx.EVT_MENU, self.OnUnivariateStats, id = self.popupID['univar'])
+            self.popupMenu.Append(self.popupID['prof'], _("Profile"))
+            self.Bind (wx.EVT_MENU, self.OnProfile, id = self.popupID['prof'])
+            self.popupMenu.Append(self.popupID['meta'], _("Metadata"))
+            self.Bind (wx.EVT_MENU, self.OnMetadata, id = self.popupID['meta'])
+            
+            if numSelected > 1:
+                self.popupMenu.Enable(self.popupID['zoom1'],   False)
+                self.popupMenu.Enable(self.popupID['region1'], False)
+                self.popupMenu.Enable(self.popupID['color'],   False)
+                self.popupMenu.Enable(self.popupID['hist'],    False)
+                self.popupMenu.Enable(self.popupID['univar'],  False)
+                self.popupMenu.Enable(self.popupID['prof'],    False)
+                self.popupMenu.Enable(self.popupID['meta'],    False)
+                self.popupMenu.Enable(self.popupID['nviz'],    False)
+                self.popupMenu.Enable(self.popupID['export'],  False)
+
+        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())
+            elif mltype == 'rgb':
+                for rname in mapLayer.GetName().splitlines():
+                    rast.append(rname)
+        
+        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, compReg = False)
+        
+    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 = 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 OnRasterColorTable(self, event):
+        """!Set color table for raster map"""
+        name = self.GetPyData(self.layer_selected)[0]['maplayer'].GetName()
+        GUI(parent = self).ParseCommand(['r.colors',
+                                         'map=%s' % name])
+
+    def OnVectorColorTable(self, event):
+        """!Set color table for vector map"""
+        name = self.GetPyData(self.layer_selected)[0]['maplayer'].GetName()
+        GUI(parent = self, centreOnParent = False).ParseCommand(['v.colors',
+                                                                 'map=%s' % name])
+        
+    def OnHistogram(self, event):
+        """!Plot histogram for given raster map layer
+        """
+        mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        if not mapLayer.GetName():
+            GError(parent = self,
+                   message = _("Unable to display histogram of "
+                               "raster map. No map name defined."))
+            return
+        
+        win = HistogramFrame(parent = self)
+        
+        win.CentreOnScreen()
+        win.Show()
+        win.SetHistLayer(mapLayer.GetName())
+        win.HistWindow.UpdateHist()
+        win.Refresh()
+        win.Update()
+        
+    def OnUnivariateStats(self, event):
+        """!Univariate raster statistics"""
+        name = self.GetPyData(self.layer_selected)[0]['maplayer'].GetName()
+        self.lmgr.goutput.RunCmd(['r.univar', 'map=%s' % name], switchPage = 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.GetToolbar('vdigit'): # enable tool
+            self.mapdisplay.AddToolbar('vdigit')
+        
+        if not self.mapdisplay.toolbars['vdigit']:
+            return
+        
+        self.mapdisplay.toolbars['vdigit'].StartEditing(maplayer)
+        
+        self._setGradient('vdigit')
+        self.RefreshLine(self.layer_selected)
+        
+    def OnStopEditing(self, event):
+        """!Stop editing the current vector map layer
+        """
+        maplayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        
+        self.mapdisplay.toolbars['vdigit'].OnExit()
+        if self.lmgr:
+            self.lmgr.toolbars['tools'].Enable('vdigit', enable = True)
+        
+        self._setGradient()
+        self.RefreshLine(self.layer_selected)
+        
+    def OnSetBgMap(self, event):
+        """!Set background vector map for editing sesstion"""
+        digit = self.mapdisplay.GetWindow().digit
+        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)
+            digit.OpenBackgroundMap(mapName)
+            self._setGradient('bgmap')
+        else:
+            UserSettings.Set(group = 'vdigit', key = 'bgmap', subkey = 'value',
+                             value = '', internal = True)
+            digit.CloseBackgroundMap()
+            self._setGradient()
+        
+        self.RefreshLine(self.layer_selected)
+
+    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
+        
+        maplayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        current_opacity = maplayer.GetOpacity()
+        
+        dlg = 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)
+            self.SetItemText(self.layer_selected,
+                             self._getLayerName(self.layer_selected))
+            
+            # vector layer currently edited
+            if self.mapdisplay.GetToolbar('vdigit') and \
+                    self.mapdisplay.GetToolbar('vdigit').GetLayer() == maplayer:   
+                alpha = int(new_opacity * 255)
+                self.mapdisplay.GetWindow().digit.GetDisplay().UpdateSettings(alpha = alpha)
+                
+            # redraw map if auto-rendering is enabled
+            self.rerender = True
+            self.reorder = True
+
+    def OnNvizProperties(self, event):
+        """!Nviz-related properties (raster/vector/volume)
+
+        @todo vector/volume
+        """
+        self.lmgr.notebook.SetSelectionByName('nviz')
+        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 OnRenameLayer (self, event):
+        """!Rename layer"""
+        self.EditLabel(self.layer_selected)
+        self.GetEditControl().SetSelection(-1, -1)
+        
+    def OnRenamed(self, event):
+        """!Layer renamed"""
+        item = self.layer_selected
+        self.GetPyData(item)[0]['label'] = event.GetLabel()
+        self.SetItemText(item, self._getLayerName(item)) # not working, why?
+        
+        event.Skip()
+
+    def AddLayer(self, ltype, lname = None, lchecked = None,
+                 lopacity = 1.0, lcmd = None, lgroup = None, lvdigit = None, lnviz = None, multiple = True):
+        """!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
+        @param multiple True to allow multiple map layers in layer tree
+        """
+        if lname and not multiple:
+            # check for duplicates
+            item = self.GetFirstVisibleItem()
+            while item and item.IsOk():
+                if self.GetPyData(item)[0]['type'] == 'vector':
+                    name = self.GetPyData(item)[0]['maplayer'].GetName()
+                    if name == lname:
+                        return
+                item = self.GetNextVisible(item)
+        
+        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 = LMIcons["layerOptions"].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)
+        
+        # add text and icons for each layer ltype
+        label =  _('(double click to set properties)') + ' ' * 15
+        if ltype == 'raster':
+            self.SetItemImage(layer, self.rast_icon)
+            self.SetItemText(layer, '%s %s' % (_('raster'), label))
+        elif ltype == '3d-raster':
+            self.SetItemImage(layer, self.rast3d_icon)
+            self.SetItemText(layer, '%s %s' % (_('3D raster'), label))
+        elif ltype == 'rgb':
+            self.SetItemImage(layer, self.rgb_icon)
+            self.SetItemText(layer, '%s %s' % (_('RGB'), label))
+        elif ltype == 'his':
+            self.SetItemImage(layer, self.his_icon)
+            self.SetItemText(layer, '%s %s' % (_('HIS'), label))
+        elif ltype == 'shaded':
+            self.SetItemImage(layer, self.shaded_icon)
+            self.SetItemText(layer, '%s %s' % (_('shaded relief'), label))
+        elif ltype == 'rastnum':
+            self.SetItemImage(layer, self.rnum_icon)
+            self.SetItemText(layer, '%s %s' % (_('raster cell numbers'), label))
+        elif ltype == 'rastarrow':
+            self.SetItemImage(layer, self.rarrow_icon)
+            self.SetItemText(layer, '%s %s' % (_('raster flow arrows'), label))
+        elif ltype == 'vector':
+            self.SetItemImage(layer, self.vect_icon)
+            self.SetItemText(layer, '%s %s' % (_('vector'), label))
+        elif ltype == 'thememap':
+            self.SetItemImage(layer, self.theme_icon)
+            self.SetItemText(layer, '%s %s' % (_('thematic map'), label))
+        elif ltype == 'themechart':
+            self.SetItemImage(layer, self.chart_icon)
+            self.SetItemText(layer, '%s %s' % (_('thematic charts'), label))
+        elif ltype == 'grid':
+            self.SetItemImage(layer, self.grid_icon)
+            self.SetItemText(layer, '%s %s' % (_('grid'), label))
+        elif ltype == 'geodesic':
+            self.SetItemImage(layer, self.geodesic_icon)
+            self.SetItemText(layer, '%s %s' % (_('geodesic line'), label))
+        elif ltype == 'rhumb':
+            self.SetItemImage(layer, self.rhumb_icon)
+            self.SetItemText(layer, '%s %s' % (_('rhumbline'), label))
+        elif ltype == 'labels':
+            self.SetItemImage(layer, self.labels_icon)
+            self.SetItemText(layer, '%s %s' % (_('vector labels'), label))
+        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, found = 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,
+                                    'label'    : None,
+                                    '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,
+                                    'label'    : None,
+                                    'maplayer' : None,
+                                    'propwin'  : None}, 
+                                   None))
+        
+        # select new item
+        self.SelectItem(layer, select = True)
+        self.layer_selected = layer
+        
+        # use predefined layer name if given
+        if lname:
+            if ltype == 'group':
+                self.SetItemText(layer, lname)
+            elif ltype == 'command':
+                ctrl.SetValue(lname)
+            else:
+                self.SetItemText(layer, self._getLayerName(layer, lname))
+        
+        # updated progress bar range (mapwindow statusbar)
+        if checked is True:
+            self.mapdisplay.GetProgressBar().SetRange(len(self.Map.GetListOfLayers(l_active = True)))
+            
+        return layer
+
+    def PropertiesDialog(self, layer, show = True):
+        """!Launch the properties dialog"""
+        if 'propwin' in self.GetPyData(layer)[0] 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)
+        
+        cmd = None
+        if self.GetPyData(layer)[0]['cmd']:
+            module = GUI(parent = self, show = show, centreOnParent = False)
+            module.ParseCommand(self.GetPyData(layer)[0]['cmd'],
+                                completed = (self.GetOptData,layer,params))
+            
+            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')
+                         
+        elif ltype == '3d-raster':
+            cmd = ['d.rast3d']
+                                        
+        elif ltype == 'rgb':
+            cmd = ['d.rgb']
+            if UserSettings.Get(group='cmd', key='rasterOverlay', subkey='enabled'):
+                cmd.append('-o')
+            
+        elif ltype == 'his':
+            cmd = ['d.his']
+            
+        elif ltype == 'shaded':
+            cmd = ['d.shadedmap']
+            
+        elif ltype == 'rastarrow':
+            cmd = ['d.rast.arrow']
+            
+        elif ltype == 'rastnum':
+            cmd = ['d.rast.num']
+            
+        elif ltype == 'vector':
+            types = list()
+            for ftype in ['point', 'line', 'boundary', 'centroid', 'area', 'face']:
+                if UserSettings.Get(group = 'cmd', key = 'showType', subkey = [ftype, 'enabled']):
+                    types.append(ftype)
+            
+            cmd = ['d.vect', 'type=%s' % ','.join(types)]
+            
+        elif ltype == 'thememap':
+            # -s flag requested, otherwise only first thematic category is displayed
+            # should be fixed by C-based d.thematic.* modules
+            cmd = ['d.vect.thematic', '-s']
+            
+        elif ltype == 'themechart':
+            cmd = ['d.vect.chart']
+            
+        elif ltype == 'grid':
+            cmd = ['d.grid']
+            
+        elif ltype == 'geodesic':
+            cmd = ['d.geodesic']
+            
+        elif ltype == 'rhumb':
+            cmd = ['d.rhumbline']
+            
+        elif ltype == 'labels':
+            cmd = ['d.labels']
+        
+        if cmd:
+            GUI(parent = self, centreOnParent = False).ParseCommand(cmd,
+                                                                    completed = (self.GetOptData,layer,params))
+        
+    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.GetToolbar('vdigit'):
+            self.mapdisplay.toolbars['vdigit'].UpdateListOfLayers (updateTool = True)
+
+        # update progress bar range (mapwindow statusbar)
+        self.mapdisplay.GetProgressBar().SetRange(len(self.Map.GetListOfLayers(l_active = True)))
+
+        #
+        # nviz
+        #
+        if self.lmgr.IsPaneShown('toolbarNviz') 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 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)
+            
+        event.Skip()
+
+    def OnLayerChecked(self, event):
+        """!Enable/disable data layer"""
+        self.lmgr.WorkspaceChanged()
+        
+        item    = event.GetItem()
+        checked = item.IsChecked()
+        
+        digitToolbar = self.mapdisplay.GetToolbar('vdigit')
+        if not self.first:
+            # 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)
+        
+        self.Unselect()
+        
+        # update progress bar range (mapwindow statusbar)
+        self.mapdisplay.GetProgressBar().SetRange(len(self.Map.GetListOfLayers(l_active = True)))
+        
+        # nviz
+        if self.lmgr.IsPaneShown('toolbarNviz') 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':
+                    npoints, nlines, nfeatures, mapIs3D = self.lmgr.nviz.VectorInfo(mapLayer)
+                    if npoints > 0:
+                        self.mapdisplay.MapWindow.LoadVector(item, points = True)
+                    if nlines > 0:
+                        self.mapdisplay.MapWindow.LoadVector(item, points = False)
+
+            else: # disable
+                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
+        
+        digitToolbar = self.mapdisplay.GetToolbar('vdigit')
+        if digitToolbar:
+            mapLayer = self.GetPyData(layer)[0]['maplayer']
+            bgmap = UserSettings.Get(group = 'vdigit', key = 'bgmap', subkey = 'value',
+                                     internal = True)
+            
+            if digitToolbar.GetLayer() == mapLayer:
+                self._setGradient('vdigit')
+            elif bgmap == mapLayer.GetName():
+                self._setGradient('bgmap')
+            else:
+                self._setGradient()
+        else:
+            self._setGradient()
+        
+        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.IsAutoRendered()
+                self.mapdisplay.MapWindow.ZoomToMap(layers = [mapLayer,],
+                                                    render = render)
+        
+        # update nviz tools
+        if self.lmgr.IsPaneShown('toolbarNviz') 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]
+        
+        # delete layer at original position
+        try:
+            self.Delete(old) # entry in render.Map layers list automatically deleted by OnDeleteLayer handler
+        except AttributeError:
+            pass
+
+        # redraw map if auto-rendering is enabled
+        self.rerender = True
+        self.reorder = True
+        
+        # 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 = LMIcons["layerOptions"].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
+        
+        return newItem
+
+    def _getLayerName(self, item, lname = ''):
+        """!Get layer name string
+
+        @param lname optional layer name
+        """
+        mapLayer = self.GetPyData(item)[0]['maplayer']
+        if not lname:
+            lname  = self.GetPyData(item)[0]['label']
+        opacity  = int(mapLayer.GetOpacity(float = True) * 100)
+        if not lname:
+            dcmd    = self.GetPyData(item)[0]['cmd']
+            lname, found = GetLayerNameFromCmd(dcmd, layerType = mapLayer.GetType(),
+                                               fullyQualified = True)
+            if not found:
+                return None
+        
+        if opacity < 100:
+            return lname + ' (%s %d' % (_('opacity:'), opacity) + '%)'
+        
+        return lname
+                
+    def GetOptData(self, dcmd, layer, params, propwin):
+        """!Process layer data (when changes in propertiesdialog are applied)"""
+        # set layer text to map name
+        if dcmd:
+            self.GetPyData(layer)[0]['cmd'] = dcmd
+            mapText  = self._getLayerName(layer)
+            mapName, found = GetLayerNameFromCmd(dcmd)
+            mapLayer = self.GetPyData(layer)[0]['maplayer']
+            self.SetItemText(layer, mapName)
+            
+            if not mapText or not found:
+                propwin.Hide()
+                GWarning(parent = self,
+                         message = _("Map <%s> not found.") % mapName)
+                return
+            
+        # update layer data
+        if params:
+            self.SetPyData(layer, (self.GetPyData(layer)[0], params))
+        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)
+
+        # update nviz session        
+        if self.lmgr.IsPaneShown('toolbarNviz') and dcmd:
+            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', '3d-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, found = GetLayerNameFromCmd(cmdlist, fullyQualified = True)
+                if not found:
+                    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.GetToolbar('vdigit'):
+            self.mapdisplay.GetToolbar('vdigit').UpdateListOfLayers(updateTool = True)
+        
+        # redraw map if auto-rendering is enabled
+        self.rerender = True
+        self.reorder = True
+        
+    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/lmgr/layertree.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Added: grass/branches/releasebranch_6_4/gui/wxpython/lmgr/menudata.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/lmgr/menudata.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/lmgr/menudata.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,70 @@
+"""!
+ at package lmrg.menudata
+
+ at brief Complex list for menu entries for wxGUI
+
+Classes:
+ - menudata::MenuData
+
+Usage:
+ at code
+python menudata.py [action] [manager|modeler]
+ at endcode
+
+where <i>action</i>:
+ - strings (default)
+ - tree
+ - commands
+ - dump
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton (Arizona State University)
+ at author Yann Chemin <yann.chemin gmail.com>
+ at author Martin Landa <landa.martin gmail.com>
+ at author Glynn Clements
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import os
+import sys
+
+from core.globalvar import ETCWXDIR
+from core.menudata  import MenuData
+
+class ManagerData(MenuData):
+    def __init__(self, filename = None):
+        if not filename:
+            gisbase = os.getenv('GISBASE')
+	    filename = os.path.join(ETCWXDIR, 'xml', 'menudata.xml')
+        
+        MenuData.__init__(self, filename)
+        
+    def GetModules(self):
+        """!Create dictionary of modules used to search module by
+        keywords, description, etc."""
+        modules = dict()
+        
+        for node in self.tree.getiterator():
+            if node.tag == 'menuitem':
+                module = description = ''
+                keywords = []
+                for child in node.getchildren():
+                    if child.tag == 'help':
+                        description = child.text
+                    if child.tag == 'command':
+                        module = child.text
+                    if child.tag == 'keywords':
+                        if child.text:
+                            keywords = child.text.split(',')
+                    
+                if module:
+                    modules[module] = { 'desc': description,
+                                        'keywords' : keywords }
+                    if len(keywords) < 1:
+                        print >> sys.stderr, "WARNING: Module <%s> has no keywords" % module
+                
+        return modules


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/lmgr/pyshell.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/lmgr/pyshell.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/lmgr/pyshell.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,112 @@
+"""!
+ at package lmgr.pyshell
+
+ at brief wxGUI Interactive Python Shell for Layer Manager
+
+Classes:
+ - pyshell::PyShellWindow
+
+ at todo Run pyshell and evaluate code in a separate instance of python &
+design the widget communicate back and forth with it
+
+(C) 2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import sys
+
+import wx
+from wx.py.shell   import Shell as PyShell
+from wx.py.version import VERSION
+
+import grass.script as grass
+
+class PyShellWindow(wx.Panel):
+    """!Python Shell Window"""
+    def __init__(self, parent, id = wx.ID_ANY, **kwargs):
+        self.parent = parent # GMFrame
+        
+        wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
+        
+        self.intro = _("Welcome to wxGUI Interactive Python Shell %s") % VERSION + "\n\n" + \
+            _("Type %s for more GRASS scripting related information.") % "\"help(grass)\"" + "\n" + \
+            _("Type %s to add raster or vector to the layer tree.") % "\"AddLayer()\"" + "\n\n"
+        self.shell = PyShell(parent = self, id = wx.ID_ANY,
+                             introText = self.intro, locals = {'grass' : grass,
+                                                               'AddLayer' : self.AddLayer})
+        
+        sys.displayhook = self._displayhook
+        
+        self.btnClear = wx.Button(self, wx.ID_CLEAR)
+        self.btnClear.Bind(wx.EVT_BUTTON, self.OnClear)
+        self.btnClear.SetToolTipString(_("Delete all text from the shell"))
+                
+        self._layout()
+        
+    def _displayhook(self, value):
+        print value # do not modify __builtin__._
+        
+    def _layout(self):
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        sizer.Add(item = self.shell, proportion = 1,
+                  flag = wx.EXPAND)
+        
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(item = self.btnClear, proportion = 0,
+                     flag = wx.EXPAND | wx.RIGHT, border = 5)
+        sizer.Add(item = btnSizer, proportion = 0,
+                  flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+        
+        sizer.Fit(self)
+        sizer.SetSizeHints(self)
+        
+        self.SetSizer(sizer)
+        
+        self.Fit()
+        self.SetAutoLayout(True)        
+        self.Layout()
+
+    def AddLayer(self, name, ltype = 'auto'):
+        """!Add selected map to the layer tree
+
+        @param name name of raster/vector map to be added
+        @param type map type ('raster', 'vector', 'auto' for autodetection)
+        """
+        fname = None
+        if ltype == 'raster' or ltype != 'vector':
+            # check for raster
+            fname = grass.find_file(name, element = 'cell')['fullname']
+            if fname:
+                ltype = 'raster'
+                lcmd = 'd.rast'
+        
+        if not fname and (ltype == 'vector' or ltype != 'raster'):
+            # if not found check for vector
+            fname = grass.find_file(name, element = 'vector')['fullname']
+            if fname:
+                ltype = 'vector'
+                lcmd = 'd.vect'
+        
+        if not fname:
+            return _("Raster or vector map <%s> not found") % (name)
+        
+        self.parent.GetLayerTree().AddLayer(ltype = ltype,
+                                            lname = fname,
+                                            lchecked = True,
+                                            lcmd = [lcmd, 'map=%s' % fname])
+        if ltype == 'raster':
+            return _('Raster map <%s> added') % fname
+        
+        return _('Vector map <%s> added') % fname
+    
+    def OnClear(self, event):
+        """!Delete all text from the shell
+        """
+        self.shell.clear()
+        self.shell.showIntro(self.intro)
+        self.shell.prompt()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/lmgr/toolbars.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/lmgr/toolbars.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/lmgr/toolbars.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,266 @@
+"""!
+ at package lmgr.toolbars
+
+ at brief wxGUI Layer Manager - toolbars
+
+Classes:
+ - toolbars::LMWorkspaceToolbar
+ - toolbars::LMDataToolbar
+ - toolbars::LMToolsToolbar
+ - toolbars::LMMiscToolbar
+ - toolbars::LMVectorToolbar
+ - toolbars::LMNvizToolbar
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import os
+import sys
+
+from core               import globalvar
+from core.gcmd          import RunCommand
+from nviz.preferences   import NvizPreferencesDialog
+from gui_core.toolbars  import BaseToolbar, BaseIcons
+from icons.icon         import MetaIcon
+
+class LMWorkspaceToolbar(BaseToolbar):
+    """!Layer Manager `workspace` toolbar
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        """!Toolbar data
+        """
+        icons = {
+            'newdisplay'    : MetaIcon(img = 'monitor-create',
+                                       label = _('Start new map display')),
+            'workspaceNew'  : MetaIcon(img = 'create',
+                                       label = _('Create new workspace (Ctrl+N)')),
+            'workspaceOpen' : MetaIcon(img = 'open',
+                                       label = _('Open existing workspace file (Ctrl+O)')),
+            'workspaceSave' : MetaIcon(img = 'save',
+                                       label = _('Save current workspace to file (Ctrl+S)')),
+            }
+        return self._getToolbarData((('newdisplay', icons["newdisplay"],
+                                      self.parent.OnNewDisplay),
+                                     (None, ),
+                                     ('workspaceNew', icons["workspaceNew"],
+                                      self.parent.OnWorkspaceNew),
+                                     ('workspaceOpen', icons["workspaceOpen"],
+                                      self.parent.OnWorkspaceOpen),
+                                     ('workspaceSave', icons["workspaceSave"],
+                                      self.parent.OnWorkspaceSave),
+                                     ))
+
+class LMDataToolbar(BaseToolbar):
+    """!Layer Manager `data` toolbar
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        """!Toolbar data
+        """
+        icons = {
+            'addMulti'   : MetaIcon(img = 'layer-open',
+                                    label = _('Add multiple raster or vector map layers (Ctrl+Shift+L)')),
+            'addRast'    : BaseIcons['addRast'].SetLabel(_("Add raster map layer (Ctrl+Shift+R)")),
+            'rastMisc'   : MetaIcon(img = 'layer-raster-more',
+                                    label = _('Add various raster map layers (RGB, HIS, shaded relief...)')),
+            'addVect'    : BaseIcons['addVect'].SetLabel(_("Add vector map layer (Ctrl+Shift+V)")),
+            'vectMisc'   : MetaIcon(img = 'layer-vector-more',
+                                    label = _('Add various vector map layers (thematic, chart...)')),
+            'addGroup'   : MetaIcon(img = 'layer-group-add',
+                                    label = _('Add group')),
+            'addOverlay' : MetaIcon(img = 'layer-more',
+                                    label = _('Add grid or vector labels overlay')),
+            'delCmd'     : MetaIcon(img = 'layer-remove',
+                                    label = _('Delete selected map layer')),
+            }
+        
+        return self._getToolbarData((('addMulti', icons["addMulti"],
+                                      self.parent.OnAddMaps),
+                                     ('addrast', icons["addRast"],
+                                      self.parent.OnAddRaster),
+                                     ('rastmisc', icons["rastMisc"],
+                                      self.parent.OnAddRasterMisc),
+                                     ('addvect', icons["addVect"],
+                                      self.parent.OnAddVector),
+                                     ('vectmisc', icons["vectMisc"],
+                                      self.parent.OnAddVectorMisc),
+                                     ('addgrp',  icons["addGroup"],
+                                      self.parent.OnAddGroup),
+                                     ('addovl',  icons["addOverlay"],
+                                      self.parent.OnAddOverlay),
+                                     ('delcmd',  icons["delCmd"],
+                                      self.parent.OnDeleteLayer),
+                                     ))
+
+class LMToolsToolbar(BaseToolbar):
+    """!Layer Manager `tools` toolbar
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        """!Toolbar data
+        """
+        icons = {
+            'import'  : MetaIcon(img = 'layer-import',
+                                 label = _('Import/link raster or vector data')),
+            'mapcalc' : MetaIcon(img = 'calculator',
+                                 label = _('Raster Map Calculator')),
+            'modeler' : MetaIcon(img = 'modeler-main',
+                                 label = _('Graphical Modeler')),
+            'georectify' : MetaIcon(img = 'georectify',
+                                 label = _('Georectifier')),
+            'composer': MetaIcon(img = 'print-compose',
+                                 label = _('Cartographic Composer')),
+            }
+        
+        return self._getToolbarData((('importMap', icons["import"],
+                                      self.parent.OnImportMenu),
+                                     (None, ),
+                                     ('mapCalc', icons["mapcalc"],
+                                      self.parent.OnMapCalculator),
+                                     ('georect', icons["georectify"],
+                                      self.parent.OnGCPManager),
+                                     ('modeler', icons["modeler"],
+                                      self.parent.OnGModeler),
+                                     ('mapOutput', icons['composer'],
+                                      self.parent.OnPsMap)
+                                     ))
+
+class LMMiscToolbar(BaseToolbar):
+    """!Layer Manager `misc` toolbar
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        """!Toolbar data
+        """
+        icons = {
+            'settings'   : BaseIcons['settings'].SetLabel(_('GUI settings')),
+            'help'       : BaseIcons['help'].SetLabel(_('GRASS manual')),
+            }
+        
+        return self._getToolbarData((('settings', icons["settings"],
+                                      self.parent.OnPreferences),
+                                     ('help', icons["help"],
+                                      self.parent.OnHelp),
+                                     ))
+
+class LMVectorToolbar(BaseToolbar):
+    """!Layer Manager `vector` toolbar
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+
+    def _toolbarData(self):
+        """!Toolbar data
+        """
+        icons = {
+            'vdigit'     : MetaIcon(img = 'edit',
+                                    label = _('Edit vector maps')),
+            'attrTable'  : MetaIcon(img = 'table',
+                                    label = _('Show attribute table')),
+            }
+        
+        return self._getToolbarData((('vdigit', icons["vdigit"],
+                                      self.parent.OnVDigit),
+                                     ('attribute', icons["attrTable"],
+                                      self.parent.OnShowAttributeTable),
+                                     ))
+
+class LMNvizToolbar(BaseToolbar):
+    """!Nviz toolbar
+    """
+    def __init__(self, parent):
+        self.lmgr = parent
+        
+        BaseToolbar.__init__(self, parent)
+        
+        # only one dialog can be open
+        self.settingsDialog   = None
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        icons = {
+            'cmd'    : MetaIcon(img = 'script-save',
+                                label = _('Generate command for m.nviz.image'),
+                                desc = _('Generate command for m.nviz.image based on current state')),
+            'settings' : MetaIcon(img = '3d-settings',
+                                  label = _('3D view mode settings'),
+                                  desc = _('Show 3D view mode settings dialog')),
+            'help'   : MetaIcon(img = '3d-help',
+                                label = _('Show 3D view mode manual')),
+            }
+        
+        return self._getToolbarData((("nvizCmd", icons['cmd'],
+                                      self.OnNvizCmd),
+                                     (None, ),
+                                     ("settings", icons["settings"],
+                                      self.OnSettings),
+                                     ("help", icons["help"],
+                                      self.OnHelp))
+                                    )
+        
+    def OnNvizCmd(self, event):
+        """!Show m.nviz.image command"""
+        self.lmgr.GetLayerTree().GetMapDisplay().GetWindow().OnNvizCmd()
+        
+    def OnHelp(self, event):
+        """!Show 3D view mode help"""
+        if not self.lmgr:
+            RunCommand('g.manual',
+                       entry = 'wxGUI.Nviz')
+        else:
+            log = self.lmgr.GetLogWindow()
+            log.RunCmd(['g.manual',
+                        'entry=wxGUI.Nviz'])
+        
+    def OnSettings(self, event):
+        """!Show nviz notebook page"""
+        if not self.settingsDialog:
+            self.settingsDialog = NvizPreferencesDialog(parent = self.parent)
+        self.settingsDialog.Show()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/location_wizard/base.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/location_wizard/base.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/location_wizard/base.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,45 @@
+"""!
+ at package location_wizard.base
+
+ at brief Location wizard - base classes
+
+Classes:
+ - base::BaseClass
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>   
+"""
+
+import wx
+
+class BaseClass(wx.Object):
+    """!Base class providing basic methods"""
+    def __init__(self):
+        pass
+
+    def MakeLabel(self, text = "", style = wx.ALIGN_LEFT, parent = None):
+        """!Make aligned label"""
+        if not parent:
+            parent = self
+        return wx.StaticText(parent = parent, id = wx.ID_ANY, label = text,
+                             style = style)
+
+    def MakeTextCtrl(self, text = '', size = (100,-1), style = 0, parent = None):
+        """!Generic text control"""
+        if not parent:
+            parent = self
+        return wx.TextCtrl(parent = parent, id = wx.ID_ANY, value = text,
+                           size = size, style = style)
+
+    def MakeButton(self, text, id = wx.ID_ANY, size = (-1,-1), parent = None):
+        """!Generic button"""
+        if not parent:
+            parent = self
+        return wx.Button(parent = parent, id = id, label = text,
+                         size = size)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/location_wizard/dialogs.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/location_wizard/dialogs.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/location_wizard/dialogs.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,613 @@
+"""!
+ at package location_wizard.dialogs
+
+ at brief Location wizard - dialogs
+
+Classes:
+ - dialogs::RegionDef
+ - dialogs::TransList
+ - dialogs::SelectTransformDialog
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>   
+"""
+import os
+import sys
+
+import wx
+import wx.lib.scrolledpanel as scrolled
+
+from core                 import globalvar
+from core.gcmd            import RunCommand
+from location_wizard.base import BaseClass
+
+from grass.script import core as grass
+
+class RegionDef(BaseClass, wx.Frame):
+    """!Page for setting default region extents and resolution
+    """
+    def __init__(self, parent, id = wx.ID_ANY,
+                 title = _("Set default region extent and resolution"), location = None):
+        wx.Frame.__init__(self, parent, id, title, size = (650,300))
+        panel = wx.Panel(self, id = wx.ID_ANY)
+        
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        
+        self.parent = parent
+        self.location = location
+        
+        #
+        # default values
+        #
+        # 2D
+        self.north = 1.0
+        self.south = 0.0
+        self.east = 1.0
+        self.west = 0.0
+        self.nsres = 1.0
+        self.ewres = 1.0
+        # 3D
+        self.top = 1.0
+        self.bottom = 0.0
+        #         self.nsres3 = 1.0
+        #         self.ewres3 = 1.0
+        self.tbres  = 1.0
+        
+        #
+        # inputs
+        #
+        # 2D
+        self.tnorth = self.MakeTextCtrl(text = str(self.north), size = (150, -1), parent = panel)
+        self.tsouth = self.MakeTextCtrl(str(self.south), size = (150, -1), parent = panel)
+        self.twest = self.MakeTextCtrl(str(self.west), size = (150, -1), parent = panel)
+        self.teast = self.MakeTextCtrl(str(self.east), size = (150, -1), parent = panel)
+        self.tnsres = self.MakeTextCtrl(str(self.nsres), size = (150, -1), parent = panel)
+        self.tewres = self.MakeTextCtrl(str(self.ewres), size = (150, -1), parent = panel)
+        
+        #
+        # labels
+        #
+        self.lrows  = self.MakeLabel(parent = panel)
+        self.lcols  = self.MakeLabel(parent = panel)
+        self.lcells = self.MakeLabel(parent = panel)
+        
+        #
+        # buttons
+        #
+        self.bset = self.MakeButton(text = _("&Set region"), id = wx.ID_OK, parent = panel)
+        self.bcancel = wx.Button(panel, id = wx.ID_CANCEL)
+        self.bset.SetDefault()
+        
+        #
+        # image
+        #
+        self.img = wx.Image(os.path.join(globalvar.ETCIMGDIR, "qgis_world.png"),
+                            wx.BITMAP_TYPE_PNG).ConvertToBitmap()
+        
+        #
+        # set current working environment to PERMANENT mapset
+        # in selected location in order to set default region (WIND)
+        #
+        envval = {}
+        ret = RunCommand('g.gisenv',
+                         read = True)
+        if ret:
+            for line in ret.splitlines():
+                key, val = line.split('=')
+                envval[key] = val
+            self.currlocation = envval['LOCATION_NAME'].strip("';")
+            self.currmapset = envval['MAPSET'].strip("';")
+            if self.currlocation != self.location or self.currmapset != 'PERMANENT':
+                RunCommand('g.gisenv',
+                           set = 'LOCATION_NAME=%s' % self.location)
+                RunCommand('g.gisenv',
+                           set = 'MAPSET=PERMANENT')
+        else:
+            dlg = wx.MessageBox(parent = self,
+                                message = _('Invalid location selected.'),
+                                caption = _("Error"), style = wx.ID_OK | wx.ICON_ERROR)
+            return
+        
+        #
+        # get current region settings
+        #
+        region = {}
+        ret = RunCommand('g.region',
+                         read = True,
+                         flags = 'gp3')
+        if ret:
+            for line in ret.splitlines():
+                key, val = line.split('=')
+                region[key] = float(val)
+        else:
+            dlg = wx.MessageBox(parent = self,
+                                message = _("Invalid region"),
+                                caption = _("Error"), style = wx.ID_OK | wx.ICON_ERROR)
+            dlg.ShowModal()
+            dlg.Destroy()
+            return
+        
+        #
+        # update values
+        # 2D
+        self.north = float(region['n'])
+        self.south = float(region['s'])
+        self.east = float(region['e'])
+        self.west = float(region['w'])
+        self.nsres = float(region['nsres'])
+        self.ewres = float(region['ewres'])
+        self.rows = int(region['rows'])
+        self.cols = int(region['cols'])
+        self.cells = int(region['cells'])
+        # 3D
+        self.top = float(region['t'])
+        self.bottom = float(region['b'])
+        #         self.nsres3 = float(region['nsres3'])
+        #         self.ewres3 = float(region['ewres3'])
+        self.tbres = float(region['tbres'])
+        self.depth = int(region['depths'])
+        self.cells3 = int(region['cells3'])
+        
+        #
+        # 3D box collapsable
+        #
+        self.infoCollapseLabelExp = _("Click here to show 3D settings")
+        self.infoCollapseLabelCol = _("Click here to hide 3D settings")
+        self.settings3D = wx.CollapsiblePane(parent = panel,
+                                             label = self.infoCollapseLabelExp,
+                                             style = wx.CP_DEFAULT_STYLE |
+                                             wx.CP_NO_TLW_RESIZE | wx.EXPAND)
+        self.MakeSettings3DPaneContent(self.settings3D.GetPane())
+        self.settings3D.Collapse(False) # FIXME
+        self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnSettings3DPaneChanged, self.settings3D)
+        
+        #
+        # set current region settings
+        #
+        self.tnorth.SetValue(str(self.north))
+        self.tsouth.SetValue(str(self.south))
+        self.twest.SetValue(str(self.west))
+        self.teast.SetValue(str(self.east))
+        self.tnsres.SetValue(str(self.nsres))
+        self.tewres.SetValue(str(self.ewres))
+        self.ttop.SetValue(str(self.top))
+        self.tbottom.SetValue(str(self.bottom))
+        #         self.tnsres3.SetValue(str(self.nsres3))
+        #         self.tewres3.SetValue(str(self.ewres3))
+        self.ttbres.SetValue(str(self.tbres))
+        self.lrows.SetLabel(_("Rows: %d") % self.rows)
+        self.lcols.SetLabel(_("Cols: %d") % self.cols)
+        self.lcells.SetLabel(_("Cells: %d") % self.cells)
+        
+        #
+        # bindings
+        #
+        self.Bind(wx.EVT_BUTTON, self.OnSetButton, self.bset)
+        self.Bind(wx.EVT_BUTTON, self.OnCancel, self.bcancel)
+        self.tnorth.Bind(wx.EVT_TEXT,   self.OnValue)
+        self.tsouth.Bind(wx.EVT_TEXT,   self.OnValue)
+        self.teast.Bind(wx.EVT_TEXT,    self.OnValue)
+        self.twest.Bind(wx.EVT_TEXT,    self.OnValue)
+        self.tnsres.Bind(wx.EVT_TEXT,   self.OnValue)
+        self.tewres.Bind(wx.EVT_TEXT,   self.OnValue)
+        self.ttop.Bind(wx.EVT_TEXT,     self.OnValue)
+        self.tbottom.Bind(wx.EVT_TEXT,  self.OnValue)
+        #         self.tnsres3.Bind(wx.EVT_TEXT,  self.OnValue)
+        #         self.tewres3.Bind(wx.EVT_TEXT,  self.OnValue)
+        self.ttbres.Bind(wx.EVT_TEXT,   self.OnValue)
+        
+        self.__DoLayout(panel)
+        self.SetMinSize(self.GetBestSize())
+        self.minWindowSize = self.GetMinSize()
+    
+    def MakeSettings3DPaneContent(self, pane):
+        """!Create 3D region settings pane"""
+        border = wx.BoxSizer(wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 0, hgap = 0)
+
+        # inputs
+        self.ttop = wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.top),
+                                size = (150, -1))
+        self.tbottom = wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.bottom),
+                                size = (150, -1))
+        self.ttbres = wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.tbres),
+                                size = (150, -1))
+        #         self.tnsres3 = wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.nsres3),
+        #                                    size = (150, -1))
+        #         self.tewres3  =  wx.TextCtrl(parent = pane, id = wx.ID_ANY, value = str(self.ewres3),
+        #                                    size = (150, -1))
+
+        #labels
+        self.ldepth = wx.StaticText(parent = pane, label = _("Depth: %d") % self.depth)
+        self.lcells3  =  wx.StaticText(parent = pane, label = _("3D Cells: %d") % self.cells3)
+
+        # top
+        gridSizer.Add(item = wx.StaticText(parent = pane, label = _("Top")),
+                      flag = wx.ALIGN_CENTER |
+                      wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
+                      pos = (0, 1))
+        gridSizer.Add(item = self.ttop,
+                      flag = wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.ALL, border = 5, pos = (1, 1))
+        # bottom
+        gridSizer.Add(item = wx.StaticText(parent = pane, label = _("Bottom")),
+                      flag = wx.ALIGN_CENTER |
+                      wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
+                      pos = (0, 2))
+        gridSizer.Add(item = self.tbottom,
+                      flag = wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.ALL, border = 5, pos = (1, 2))
+        # tbres
+        gridSizer.Add(item = wx.StaticText(parent = pane, label = _("T-B resolution")),
+                      flag = wx.ALIGN_CENTER | 
+                      wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
+                      pos = (0, 3))
+        gridSizer.Add(item = self.ttbres,
+                      flag = wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.ALL, border = 5, pos = (1, 3))
+
+        # res
+        #         gridSizer.Add(item = wx.StaticText(parent = pane, label = _("3D N-S resolution")),
+        #                       flag = wx.ALIGN_CENTER |
+        #                       wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
+        #                       pos = (2, 1))
+        #         gridSizer.Add(item = self.tnsres3,
+        #                       flag = wx.ALIGN_CENTER_HORIZONTAL |
+        #                       wx.ALL, border = 5, pos = (3, 1))
+        #         gridSizer.Add(item = wx.StaticText(parent = pane, label = _("3D E-W resolution")),
+        #                       flag = wx.ALIGN_CENTER |
+        #                       wx.LEFT | wx.RIGHT | wx.TOP, border = 5,
+        #                       pos = (2, 3))
+        #         gridSizer.Add(item = self.tewres3,
+        #                       flag = wx.ALIGN_CENTER_HORIZONTAL |
+        #                       wx.ALL, border = 5, pos = (3, 3))
+
+        # rows/cols/cells
+        gridSizer.Add(item = self.ldepth,
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
+                      wx.ALL, border = 5, pos = (2, 1))
+
+        gridSizer.Add(item = self.lcells3,
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
+                      wx.ALL, border = 5, pos = (2, 2))
+
+        border.Add(item = gridSizer, proportion = 1,
+                   flag = wx.ALL | wx.ALIGN_CENTER | wx.EXPAND, border = 5)
+
+        pane.SetSizer(border)
+        border.Fit(pane)
+
+    def OnSettings3DPaneChanged(self, event):
+        """!Collapse 3D settings box"""
+
+        if self.settings3D.IsExpanded():
+            self.settings3D.SetLabel(self.infoCollapseLabelCol)
+            self.Layout()
+            self.SetSize(self.GetBestSize())
+            self.SetMinSize(self.GetSize())
+        else:
+            self.settings3D.SetLabel(self.infoCollapseLabelExp)
+            self.Layout()
+            self.SetSize(self.minWindowSize)
+            self.SetMinSize(self.minWindowSize)
+
+        self.SendSizeEvent()
+
+    def __DoLayout(self, panel):
+        """!Window layout"""
+        frameSizer = wx.BoxSizer(wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 0, hgap = 0)
+        settings3DSizer = wx.BoxSizer(wx.VERTICAL)
+        buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+        # north
+        gridSizer.Add(item = self.MakeLabel(text = _("North"), parent = panel),
+                      flag = wx.ALIGN_BOTTOM | wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.TOP | wx.LEFT | wx.RIGHT, border = 5, pos = (0, 2))
+        gridSizer.Add(item = self.tnorth,
+                      flag = wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALL, border = 5, pos = (1, 2))
+        # west
+        gridSizer.Add(item = self.MakeLabel(text = _("West"), parent = panel),
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.LEFT | wx.TOP | wx.BOTTOM, border = 5, pos = (2, 0))
+        gridSizer.Add(item = self.twest,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALL, border = 5,  pos = (2, 1))
+
+        gridSizer.Add(item = wx.StaticBitmap(panel, wx.ID_ANY, self.img, (-1, -1),
+                                           (self.img.GetWidth(), self.img.GetHeight())),
+                      flag = wx.ALIGN_CENTER |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALL, border = 5, pos = (2, 2))
+
+        # east
+        gridSizer.Add(item = self.teast,
+                      flag = wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALL, border = 5,  pos = (2, 3))
+        gridSizer.Add(item = self.MakeLabel(text = _("East"), parent = panel),
+                      flag = wx.ALIGN_LEFT |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.RIGHT | wx.TOP | wx.BOTTOM, border = 5, pos = (2, 4))
+        # south
+        gridSizer.Add(item = self.tsouth,
+                      flag = wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALL, border = 5, pos = (3, 2))
+        gridSizer.Add(item = self.MakeLabel(text = _("South"), parent = panel),
+                      flag = wx.ALIGN_TOP | wx.ALIGN_CENTER_HORIZONTAL |
+                      wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5, pos = (4, 2))
+        # ns-res
+        gridSizer.Add(item = self.MakeLabel(text = _("N-S resolution"), parent = panel),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
+                      wx.TOP | wx.LEFT | wx.RIGHT, border = 5, pos = (5, 1))
+        gridSizer.Add(item = self.tnsres,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALL, border = 5,  pos = (6, 1))
+        # ew-res
+        gridSizer.Add(item = self.MakeLabel(text = _("E-W resolution"), parent = panel),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
+                      wx.TOP | wx.LEFT | wx.RIGHT, border = 5, pos = (5, 3))
+        gridSizer.Add(item = self.tewres,
+                      flag = wx.ALIGN_RIGHT |
+                      wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALL, border = 5,  pos = (6, 3))
+        # rows/cols/cells
+        gridSizer.Add(item = self.lrows,
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
+                      wx.ALL, border = 5, pos = (7, 1))
+
+        gridSizer.Add(item = self.lcells,
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
+                      wx.ALL, border = 5, pos = (7, 2))
+
+        gridSizer.Add(item = self.lcols,
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER |
+                      wx.ALL, border = 5, pos = (7, 3))
+
+        # 3D
+        settings3DSizer.Add(item = self.settings3D,
+                            flag = wx.ALL,
+                            border = 5)
+
+        # buttons
+        buttonSizer.Add(item = self.bcancel, proportion = 1,
+                        flag = wx.ALIGN_RIGHT |
+                        wx.ALIGN_CENTER_VERTICAL |
+                        wx.ALL, border = 10)
+        buttonSizer.Add(item = self.bset, proportion = 1,
+                        flag = wx.ALIGN_CENTER |
+                        wx.ALIGN_CENTER_VERTICAL |
+                        wx.ALL, border = 10)
+
+        frameSizer.Add(item = gridSizer, proportion = 1,
+                       flag = wx.ALL | wx.ALIGN_CENTER, border = 5)
+        frameSizer.Add(item = settings3DSizer, proportion = 0,
+                       flag = wx.ALL | wx.ALIGN_CENTER, border = 5)
+        frameSizer.Add(item = buttonSizer, proportion = 0,
+                       flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
+
+        self.SetAutoLayout(True)
+        panel.SetSizer(frameSizer)
+        frameSizer.Fit(panel)
+        self.Layout()
+
+    def OnValue(self, event):
+        """!Set given value"""
+        try:
+            if event.GetId() == self.tnorth.GetId():
+                self.north = float(event.GetString())
+            elif event.GetId() == self.tsouth.GetId():
+                self.south = float(event.GetString())
+            elif event.GetId() == self.teast.GetId():
+                self.east = float(event.GetString())
+            elif event.GetId() == self.twest.GetId():
+                self.west = float(event.GetString())
+            elif event.GetId() == self.tnsres.GetId():
+                self.nsres = float(event.GetString())
+            elif event.GetId() == self.tewres.GetId():
+                self.ewres = float(event.GetString())
+            elif event.GetId() == self.ttop.GetId():
+                self.top = float(event.GetString())
+            elif event.GetId() == self.tbottom.GetId():
+                self.bottom = float(event.GetString())
+            #             elif event.GetId() == self.tnsres3.GetId():
+            #                 self.nsres3 = float(event.GetString())
+            #             elif event.GetId() == self.tewres3.GetId():
+            #                 self.ewres3 = float(event.GetString())
+            elif event.GetId() == self.ttbres.GetId():
+                self.tbres = float(event.GetString())
+
+            self.__UpdateInfo()
+
+        except ValueError, e:
+            if len(event.GetString()) > 0 and event.GetString() != '-':
+                dlg = wx.MessageBox(parent = self,
+                                    message = _("Invalid value: %s") % e,
+                                    caption = _("Error"),
+                                    style = wx.OK | wx.ICON_ERROR)
+                # reset values
+                self.tnorth.SetValue(str(self.north))
+                self.tsouth.SetValue(str(self.south))
+                self.teast.SetValue(str(self.east))
+                self.twest.SetValue(str(self.west))
+                self.tnsres.SetValue(str(self.nsres))
+                self.tewres.SetValue(str(self.ewres))
+                self.ttop.SetValue(str(self.top))
+                self.tbottom.SetValue(str(self.bottom))
+                self.ttbres.SetValue(str(self.tbres))
+                # self.tnsres3.SetValue(str(self.nsres3))
+                # self.tewres3.SetValue(str(self.ewres3))
+
+        event.Skip()
+
+    def __UpdateInfo(self):
+        """!Update number of rows/cols/cells"""
+        self.rows = int((self.north - self.south) / self.nsres)
+        self.cols = int((self.east - self.west) / self.ewres)
+        self.cells = self.rows * self.cols
+
+        self.depth = int((self.top - self.bottom) / self.tbres)
+        self.cells3 = self.rows * self.cols * self.depth
+
+        # 2D
+        self.lrows.SetLabel(_("Rows: %d") % self.rows)
+        self.lcols.SetLabel(_("Cols: %d") % self.cols)
+        self.lcells.SetLabel(_("Cells: %d") % self.cells)
+        # 3D
+        self.ldepth.SetLabel(_("Depth: %d" % self.depth))
+        self.lcells3.SetLabel(_("3D Cells: %d" % self.cells3))
+
+    def OnSetButton(self, event = None):
+        """!Set default region"""
+        ret = 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()
+        
+class TransList(wx.VListBox):
+    """!Creates a multiline listbox for selecting datum transforms"""
+        
+    def OnDrawItem(self, dc, rect, n):
+        if self.GetSelection() == n:
+            c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
+        else:
+            c = self.GetForegroundColour()
+        dc.SetFont(self.GetFont())
+        dc.SetTextForeground(c)
+        dc.DrawLabel(self._getItemText(n), rect,
+                     wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL)
+
+    def OnMeasureItem(self, n):
+        height = 0
+        if self._getItemText(n) == None:
+            return
+        for line in self._getItemText(n).splitlines():
+            w, h = self.GetTextExtent(line)
+            height += h
+        return height + 5
+
+    def _getItemText(self, item):
+        global transformlist
+        transitem = transformlist[item]
+        if transitem.strip() !='':
+            return transitem
+
+class SelectTransformDialog(wx.Dialog):
+    """!Dialog for selecting datum transformations"""
+    def __init__(self, parent, transforms, title = _("Select datum transformation"),
+                 pos = wx.DefaultPosition, size = wx.DefaultSize, 
+                 style = wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER):
+
+        wx.Dialog.__init__(self, parent, wx.ID_ANY, title, pos, size, style)
+
+        global transformlist
+        self.CentreOnParent()
+        
+        # default transform number
+        self.transnum = 0
+        
+        panel = scrolled.ScrolledPanel(self, wx.ID_ANY)
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        #
+        # set panel sizer
+        #
+        panel.SetSizer(sizer)
+        panel.SetupScrolling()
+
+        #
+        # dialog body
+        #
+        bodyBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                               label = " %s " % _("Select from list of datum transformations"))
+        bodySizer = wx.StaticBoxSizer(bodyBox)       
+        
+        # add no transform option
+        transforms = '---\n\n0\nDo not apply any datum transformations\n\n' + transforms
+        
+        transformlist = transforms.split('---')
+        tlistlen = len(transformlist)
+        
+        # calculate size for transform list
+        height = 0
+        width = 0
+        for line in transforms.splitlines():
+            w, h = self.GetTextExtent(line)
+            height += h
+            width = max(width, w)
+            
+        height = height + 5
+        if height > 400: height = 400
+        width = width + 5
+        if width > 400: width = 400
+
+        #
+        # VListBox for displaying and selecting transformations
+        #
+        self.translist = TransList(panel, id = -1, size = (width, height), style = wx.SUNKEN_BORDER)
+        self.translist.SetItemCount(tlistlen)
+        self.translist.SetSelection(2)
+        self.translist.SetFocus()
+        
+        self.Bind(wx.EVT_LISTBOX, self.ClickTrans, self.translist)
+
+        bodySizer.Add(item = self.translist, proportion = 1, flag = wx.ALIGN_CENTER|wx.ALL|wx.EXPAND)
+
+        #
+        # buttons
+        #
+        btnsizer = wx.StdDialogButtonSizer()
+
+        btn = wx.Button(parent = panel, id = wx.ID_OK)
+        btn.SetDefault()
+        btnsizer.AddButton(btn)
+
+        btn = wx.Button(parent = panel, id = wx.ID_CANCEL)
+        btnsizer.AddButton(btn)
+        btnsizer.Realize()
+
+        sizer.Add(item = bodySizer, proportion = 1,
+                  flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+        sizer.Add(item = btnsizer, proportion = 0,
+                  flag =  wx.ALL | wx.ALIGN_RIGHT, border = 5)
+
+        sizer.Fit(panel)
+
+        self.SetSize(self.GetBestSize())
+        self.Layout()
+        
+    def ClickTrans(self, event):
+        """!Get the number of the datum transform to use in g.proj"""
+        self.transnum = event.GetSelection()
+        self.transnum = self.transnum - 1
+    
+    def GetTransform(self):
+        """!Get the number of the datum transform to use in g.proj"""
+        self.transnum = self.translist.GetSelection()
+        self.transnum = self.transnum - 1
+        return self.transnum


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/location_wizard/wizard.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/location_wizard/wizard.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/location_wizard/wizard.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,2050 @@
+"""!
+ at package location_wizard.wizard
+
+ at brief Location wizard - creates a new GRASS Location. User can choose
+from multiple methods.
+
+Classes:
+ - wizard::TitledPage
+ - wizard::DatabasePage
+ - wizard::CoordinateSystemPage
+ - wizard::ProjectionsPage
+ - wizard::ItemList
+ - wizard::ProjParamsPage
+ - wizard::DatumPage
+ - wizard::EllipsePage
+ - wizard::GeoreferencedFilePage
+ - wizard::WKTPage
+ - wizard::EPSGPage
+ - wizard::CustomPage
+ - wizard::SummaryPage
+ - wizard::LocationWizard
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>   
+"""
+import os
+import locale
+
+import wx
+import wx.lib.mixins.listctrl as listmix
+import wx.wizard as wiz
+import wx.lib.scrolledpanel as scrolled
+
+from core                    import globalvar
+from core                    import utils
+from core.gcmd               import RunCommand, GError, GMessage, GWarning
+from location_wizard.base    import BaseClass
+from location_wizard.dialogs import RegionDef, SelectTransformDialog
+
+from grass.script import core as grass
+
+global coordsys
+global north
+global south
+global east
+global west
+global resolution
+global wizerror
+global translist
+
+class TitledPage(BaseClass, wiz.WizardPageSimple):
+    """!Class to make wizard pages. Generic methods to make labels,
+    text entries, and buttons.
+    """
+    def __init__(self, parent, title):
+
+        self.page = wiz.WizardPageSimple.__init__(self, parent)
+
+        # page title
+        self.title = wx.StaticText(parent = self, id = wx.ID_ANY, label = title)
+        self.title.SetFont(wx.Font(13, wx.SWISS, wx.NORMAL, wx.BOLD))
+
+        # main sizers
+        self.pagesizer = wx.BoxSizer(wx.VERTICAL)
+        self.sizer = wx.GridBagSizer(vgap = 0, hgap = 0)
+        
+    def DoLayout(self):
+        """!Do page layout"""
+        self.pagesizer.Add(item = self.title, proportion = 0,
+                           flag = wx.ALIGN_CENTRE | wx.ALL,
+                           border = 5)
+        self.pagesizer.Add(item = wx.StaticLine(self, -1), proportion = 0,
+                           flag = wx.EXPAND | wx.ALL,
+                           border = 0)
+        self.pagesizer.Add(item = self.sizer, proportion = 1,
+                           flag = wx.EXPAND)
+        
+        self.SetAutoLayout(True)
+        self.SetSizer(self.pagesizer)
+        self.Layout()
+
+class DatabasePage(TitledPage):
+    """!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"))
+
+        self.grassdatabase  = grassdatabase
+        self.location       = ''
+        self.locTitle       = ''
+        
+        # buttons
+        self.bbrowse = self.MakeButton(_("Browse"))
+
+        # text controls
+        self.tgisdbase = self.MakeTextCtrl(grassdatabase, size = (300, -1))
+        self.tlocation = self.MakeTextCtrl("newLocation", size = (300, -1))
+        self.tlocTitle = self.MakeTextCtrl(size = (400, -1))
+        
+        # layout
+        self.sizer.AddGrowableCol(3)
+        self.sizer.Add(item = self.MakeLabel(_("GIS Data Directory:")),
+                       flag = wx.ALIGN_RIGHT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5,
+                       pos = (1, 1))
+        self.sizer.Add(item = self.tgisdbase,
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5,
+                       pos = (1, 2))
+        self.sizer.Add(item = self.bbrowse,
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5,
+                       pos = (1, 3))
+        
+        self.sizer.Add(item = self.MakeLabel("%s:" % _("Project Location")),
+                       flag = wx.ALIGN_RIGHT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5,
+                       pos = (2, 1))
+        self.sizer.Add(item = self.tlocation,
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5,
+                       pos = (2, 2))
+
+        self.sizer.Add(item = self.MakeLabel("%s:" % _("Location Title")),
+                       flag = wx.ALIGN_RIGHT |
+                       wx.ALIGN_TOP | wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5,
+                       pos = (3, 1))
+        self.sizer.Add(item = self.tlocTitle,
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5,
+                       pos = (3, 2), span  =  (1, 2))
+        
+        # bindings
+        self.Bind(wx.EVT_BUTTON,                self.OnBrowse, self.bbrowse)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
+        self.tgisdbase.Bind(wx.EVT_TEXT,        self.OnChangeName)
+        self.tlocation.Bind(wx.EVT_TEXT,        self.OnChangeName)
+        
+    def OnChangeName(self, event):
+        """!Name for new location was changed"""
+        nextButton = wx.FindWindowById(wx.ID_FORWARD)
+        if len(event.GetString()) > 0:
+            if not nextButton.IsEnabled():
+                nextButton.Enable()
+        else:
+            nextButton.Disable()
+
+        event.Skip()
+
+    def OnBrowse(self, event):
+        """!Choose GRASS data directory"""
+        dlg = wx.DirDialog(self, _("Choose GRASS data directory:"),
+                           os.getcwd(), wx.DD_DEFAULT_STYLE)
+        if dlg.ShowModal() == wx.ID_OK:
+            self.grassdatabase = dlg.GetPath()
+            self.tgisdbase.SetValue(self.grassdatabase)
+            
+        dlg.Destroy()
+
+    def OnPageChanging(self, event = None):
+        error = None
+        if os.path.isdir(os.path.join(self.tgisdbase.GetValue(), self.tlocation.GetValue())):
+            error = _("Location already exists in GRASS Database.")
+
+        if error:
+            GError(parent = self,
+                   message="%s <%s>.%s%s" % (_("Unable to create location"),
+                                             str(self.tlocation.GetValue()),
+                                             os.linesep,
+                                             error))
+            event.Veto()
+            return
+
+        self.location      = self.tlocation.GetValue()
+        self.grassdatabase = self.tgisdbase.GetValue()
+        self.locTitle      = self.tlocTitle.GetValue()
+        if os.linesep in self.locTitle or \
+                len(self.locTitle) > 255:
+            GWarning(parent = self,
+                     message = _("Title of the location is limited only to one line and "
+                                 "256 characters. The rest of the text will be ignored."))
+            self.locTitle = self.locTitle.split(os.linesep)[0][:255]
+            
+class CoordinateSystemPage(TitledPage):
+    """!Wizard page for choosing method for location creation"""
+    def __init__(self, wizard, parent):
+        TitledPage.__init__(self, wizard, _("Choose method for creating a new location"))
+        
+        self.parent = parent
+        global coordsys
+        
+        # toggles
+        self.radio1 = wx.RadioButton(parent = self, id = wx.ID_ANY,
+                                     label = _("Select coordinate system parameters from a list"),
+                                     style  =  wx.RB_GROUP)
+        self.radio2 = wx.RadioButton(parent = self, id = wx.ID_ANY,
+                                     label = _("Select EPSG code of spatial reference system"))
+        self.radio3 = wx.RadioButton(parent = self, id = wx.ID_ANY,
+                                     label = _("Read projection and datum terms from a "
+                                             "georeferenced data file"))
+        self.radio4 = wx.RadioButton(parent = self, id = wx.ID_ANY,
+                                     label = _("Read projection and datum terms from a "
+                                             "WKT or PRJ file"))
+        self.radio5 = wx.RadioButton(parent = self, id = wx.ID_ANY,
+                                     label = _("Specify projection and datum terms using custom "
+                                             "PROJ.4 parameters"))
+        self.radio6 = wx.RadioButton(parent = self, id = wx.ID_ANY,
+                                     label = _("Create a generic Cartesian coordinate system (XY)"))
+        
+        # layout
+        self.sizer.AddGrowableCol(1)
+        self.sizer.SetVGap(10)
+        self.sizer.Add(item = self.radio1,
+                       flag = wx.ALIGN_LEFT, pos = (1, 1))
+        self.sizer.Add(item = self.radio2,
+                       flag = wx.ALIGN_LEFT, pos = (2, 1))
+        self.sizer.Add(item = self.radio3,
+                       flag = wx.ALIGN_LEFT, pos = (3, 1))
+        self.sizer.Add(item = self.radio4,
+                       flag = wx.ALIGN_LEFT, pos = (4, 1))
+        self.sizer.Add(item = self.radio5,
+                       flag = wx.ALIGN_LEFT, pos = (5, 1))
+        self.sizer.Add(item = self.radio6,
+                       flag = wx.ALIGN_LEFT, pos = (6, 1))
+
+        # 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.radio3.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio4.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio5.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id = self.radio6.GetId())
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED,  self.OnEnterPage)
+        
+    def OnEnterPage(self, event):
+        global coordsys
+        
+        if not coordsys:
+            coordsys = "proj"
+            self.radio1.SetValue(True)
+        else:
+            if coordsys == 'proj':
+                self.radio1.SetValue(True)
+            if coordsys == "epsg":
+                self.radio2.SetValue(True)
+            if coordsys == "file":
+                self.radio3.SetValue(True)
+            if coordsys == "wkt":
+                self.radio4.SetValue(True)
+            if coordsys == "custom":
+                self.radio5.SetValue(True)
+            if coordsys == "xy":
+                self.radio6.SetValue(True)
+        
+        if event.GetDirection():
+            if coordsys == 'proj':
+                self.SetNext(self.parent.projpage)
+                self.parent.sumpage.SetPrev(self.parent.datumpage)
+            if coordsys == "epsg":
+                self.SetNext(self.parent.epsgpage)
+                self.parent.sumpage.SetPrev(self.parent.epsgpage)
+            if coordsys == "file":
+                self.SetNext(self.parent.filepage)
+                self.parent.sumpage.SetPrev(self.parent.filepage)
+            if coordsys == "wkt":
+                self.SetNext(self.parent.wktpage)
+                self.parent.sumpage.SetPrev(self.parent.wktpage)
+            if coordsys == "custom":
+                self.SetNext(self.parent.custompage)
+                self.parent.sumpage.SetPrev(self.parent.custompage)
+            if coordsys == "xy":
+                self.SetNext(self.parent.sumpage)
+                self.parent.sumpage.SetPrev(self.parent.csystemspage)
+        
+        if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled():
+            wx.FindWindowById(wx.ID_FORWARD).Enable()
+    
+    def SetVal(self, event):
+        """!Choose method"""
+        global coordsys
+        if event.GetId() == self.radio1.GetId():
+            coordsys = "proj"
+            self.SetNext(self.parent.projpage)
+            self.parent.sumpage.SetPrev(self.parent.datumpage)
+        elif event.GetId() == self.radio2.GetId():
+            coordsys = "epsg"
+            self.SetNext(self.parent.epsgpage)
+            self.parent.sumpage.SetPrev(self.parent.epsgpage)
+        elif event.GetId() == self.radio3.GetId():
+            coordsys = "file"
+            self.SetNext(self.parent.filepage)
+            self.parent.sumpage.SetPrev(self.parent.filepage)
+        elif event.GetId() == self.radio4.GetId():
+            coordsys = "wkt"
+            self.SetNext(self.parent.wktpage)
+            self.parent.sumpage.SetPrev(self.parent.wktpage)
+        elif event.GetId() == self.radio5.GetId():
+            coordsys = "custom"
+            self.SetNext(self.parent.custompage)
+            self.parent.sumpage.SetPrev(self.parent.custompage)
+        elif event.GetId() == self.radio6.GetId():
+            coordsys = "xy"
+            self.SetNext(self.parent.sumpage)
+            self.parent.sumpage.SetPrev(self.parent.csystemspage)
+
+class ProjectionsPage(TitledPage):
+    """!Wizard page for selecting projection (select coordinate system option)"""
+    def __init__(self, wizard, parent):
+        TitledPage.__init__(self, wizard, _("Choose projection"))
+
+        self.parent = parent
+        self.proj = ''
+        self.projdesc = ''
+        self.p4proj = ''
+
+        # text input
+        self.tproj = self.MakeTextCtrl("", size = (200,-1))
+        
+        # search box
+        self.searchb = wx.SearchCtrl(self, size = (200,-1),
+                                     style = wx.TE_PROCESS_ENTER)
+
+        # projection list
+        self.projlist = ItemList(self, data = self.parent.projdesc.items(),
+                                 columns = [_('Code'), _('Description')])
+        self.projlist.resizeLastColumn(30) 
+
+        # layout
+        self.sizer.AddGrowableCol(3)
+        self.sizer.Add(item = self.MakeLabel(_("Projection code:")),
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (1, 1))
+        self.sizer.Add(item = self.tproj,
+                       flag = wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL,
+                       border = 5, pos = (1, 2))
+
+        self.sizer.Add(item = self.MakeLabel(_("Search in description:")),
+                       flag = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL,
+                       border = 5, pos = (2, 1))
+        self.sizer.Add(item = self.searchb,
+                       flag = wx.ALIGN_RIGHT | wx.EXPAND | wx.ALL,
+                       border = 5, pos = (2, 2))
+        
+        self.sizer.AddGrowableRow(3)
+        self.sizer.Add(item = self.projlist,
+                       flag = wx.EXPAND |
+                       wx.ALIGN_LEFT |
+                       wx.ALL, border = 5, pos = (3, 1), span = (1, 3))
+
+        # events
+        self.tproj.Bind(wx.EVT_TEXT, self.OnText)
+        self.tproj.Bind(wx.EVT_TEXT_ENTER, self.OnText)
+        self.searchb.Bind(wx.EVT_TEXT_ENTER, self.OnSearch)
+        self.projlist.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED,  self.OnEnterPage)
+        
+    def OnPageChanging(self,event):
+        if event.GetDirection() and self.proj not in self.parent.projections.keys():
+            event.Veto()
+
+    def OnText(self, event):
+        """!Projection name changed"""
+        self.proj = event.GetString().lower()
+        self.p4proj = ''
+        nextButton = wx.FindWindowById(wx.ID_FORWARD)
+        if self.proj not in self.parent.projections.keys() and nextButton.IsEnabled():
+            nextButton.Enable(False)
+        
+        if self.proj in self.parent.projections.keys():
+            if self.proj == 'stp':
+                wx.MessageBox('Currently State Plane projections must be selected using the '
+                              'text-based setup (g.setproj), or entered by EPSG code or '
+                              'custom PROJ.4 terms.',
+                              'Warning', wx.ICON_WARNING)
+                self.proj = ''
+                self.tproj.SetValue(self.proj)
+                nextButton.Enable(False)
+                return
+            elif self.proj.lower() == 'll':
+                self.p4proj = '+proj=longlat'
+            else:
+                self.p4proj = '+proj=' + self.proj.lower()
+            self.projdesc = self.parent.projections[self.proj][0]
+            nextButton.Enable()
+
+    def OnEnterPage(self, event):
+        if len(self.proj) == 0:
+            # disable 'next' button by default
+            wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+        else:
+            wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+        event.Skip()
+    
+    def OnSearch(self, event):
+        """!Search projection by desc"""
+        str = event.GetString()
+        try:
+            self.proj, self.projdesc = self.projlist.Search(index = [0,1], pattern = event.GetString())
+        except:
+            self.proj = self.projdesc = ''
+            
+        event.Skip()
+
+    def OnItemSelected(self, event):
+        """!Projection selected"""
+        index = event.m_itemIndex
+
+        # set values
+        self.proj = self.projlist.GetItem(index, 0).GetText().lower()
+        self.tproj.SetValue(self.proj)
+        
+        event.Skip()
+
+class ItemList(wx.ListCtrl,
+               listmix.ListCtrlAutoWidthMixin,
+               listmix.ColumnSorterMixin):
+    """!Generic list (for projections, ellipsoids, etc.)"""
+
+    def __init__(self, parent, columns, data = None):
+        wx.ListCtrl.__init__(self, parent = parent, id = wx.ID_ANY,
+                             style = wx.LC_REPORT |
+                             wx.LC_VIRTUAL | 
+                             wx.LC_HRULES |
+                             wx.LC_VRULES |
+                             wx.LC_SINGLE_SEL |
+                             wx.LC_SORT_ASCENDING, size = (550, 125))
+
+        # original data or None
+        self.sourceData = data
+        
+        #
+        # insert columns
+        #
+        i = 0
+        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:
+                self.SetColumnWidth(i, 80)
+        
+        #
+        # listmix
+        #
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+        listmix.ColumnSorterMixin.__init__(self, self.GetColumnCount())
+            
+        #
+        # add some attributes
+        #
+        self.attr1 = wx.ListItemAttr()
+        self.attr1.SetBackgroundColour(wx.Colour(238,238,238))
+        self.attr2 = wx.ListItemAttr()
+        self.attr2.SetBackgroundColour("white")
+        self.il = wx.ImageList(16, 16)
+        self.sm_up = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_UP,   wx.ART_TOOLBAR,
+                                                          (16,16)))
+        self.sm_dn = self.il.Add(wx.ArtProvider_GetBitmap(wx.ART_GO_DOWN, wx.ART_TOOLBAR,
+                                                          (16,16)))
+        self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
+
+        #
+        # sort by first column
+        #
+        if self.sourceData:
+            self.SortListItems(col = 0, ascending = True)
+
+        #
+        # bindings
+        #
+        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColumnClick)
+
+    def Populate(self, data = None, update = False):
+        """!Populate list"""
+        self.itemDataMap  = {}
+        self.itemIndexMap = []
+        
+        if data is None:
+            data = self.sourceData
+        elif update:
+            self.sourceData = data
+
+        try:
+            data.sort()
+            self.DeleteAllItems()
+            row = 0
+            for value in data:
+                self.itemDataMap[row] = [value[0]]
+                for i in range(1, len(value)):
+                     self.itemDataMap[row].append(value[i])
+                self.itemIndexMap.append(row)
+                row += 1
+
+            self.SetItemCount(row)
+            
+            # set column width
+            self.SetColumnWidth(0, 80)
+            self.SetColumnWidth(1, 300)
+            
+            self.SendSizeEvent()
+            
+        except StandardError, e:
+            wx.MessageBox(parent = self,
+                          message = _("Unable to read list: %s") % e,
+                          caption = _("Error"), style = wx.OK | wx.ICON_ERROR)
+
+    def OnColumnClick(self, event):
+        """!Sort by column"""
+        self._col = event.GetColumn()
+
+        # remove duplicated arrow symbol from column header
+        # FIXME: should be done automatically
+        info = wx.ListItem()
+        info.m_mask = wx.LIST_MASK_TEXT | wx.LIST_MASK_IMAGE
+        info.m_image = -1
+        for column in range(self.GetColumnCount()):
+            info.m_text = self.GetColumn(column).GetText()
+            self.SetColumn(column, info)
+
+        event.Skip()
+
+    def GetSortImages(self):
+        """!Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py"""
+        return (self.sm_dn, self.sm_up)
+
+    def OnGetItemText(self, item, col):
+        """!Get item text"""
+        index = self.itemIndexMap[item]
+        s = str(self.itemDataMap[index][col])
+        return s
+
+    def OnGetItemAttr(self, item):
+        """!Get item attributes"""
+        index = self.itemIndexMap[item]
+        if ( index % 2) == 0:
+            return self.attr2
+        else:
+            return self.attr1
+
+    def SortItems(self, sorter = cmp):
+        """!Sort items"""
+        items = list(self.itemDataMap.keys())
+        items.sort(self.Sorter)
+        self.itemIndexMap = items
+
+        # redraw the list
+        self.Refresh()
+        
+    def Sorter(self, key1, key2):
+        colName = self.GetColumn(self._col).GetText()
+        ascending = self._colSortFlag[self._col]
+        # convert always string
+        item1 = self.itemDataMap[key1][self._col]
+        item2 = self.itemDataMap[key2][self._col]
+
+        if type(item1) == type('') or type(item2) == type(''):
+            cmpVal = locale.strcoll(str(item1), str(item2))
+        else:
+            cmpVal = cmp(item1, item2)
+
+
+        # 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 GetListCtrl(self):
+        """!Used by listmix.ColumnSorterMixin"""
+        return self
+
+    def Search (self, index, pattern):
+        """!Search projection by description
+        Return first found item or None
+        """
+        if pattern == '':
+            self.Populate(self.sourceData)
+            return []
+
+        data = []
+        pattern = pattern.lower()
+        for i in range(len(self.sourceData)):
+            for idx in index:
+                try:
+                    value = str(self.sourceData[i][idx]).lower()
+                    if pattern in value:
+                        data.append(self.sourceData[i])
+                        break
+                except UnicodeDecodeError:
+                    # osgeo4w problem (should be fixed)
+                    pass
+
+        self.Populate(data)
+        if len(data) > 0:
+            return data[0]
+        else:
+            return []
+
+class ProjParamsPage(TitledPage):
+    """!Wizard page for selecting method of setting coordinate system
+    parameters (select coordinate system option)
+    """
+    def __init__(self, wizard, parent):
+        TitledPage.__init__(self, wizard, _("Choose projection parameters"))
+        global coordsys
+        
+        self.parent = parent
+        self.panel = None
+        self.prjParamSizer = None
+        
+        self.pparam = dict()
+        
+        self.p4projparams = ''
+        self.projdesc = ''
+
+        self.sizer.AddGrowableCol(1)
+        self.sizer.AddGrowableRow(1)
+
+        radioSBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                                 label = " %s " % _("Select datum or ellipsoid (next page)"))
+        radioSBSizer = wx.StaticBoxSizer(radioSBox)
+        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)
+        self.radio2 = wx.RadioButton(parent = self, id = wx.ID_ANY,
+                                     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)  
+        
+        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(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChange)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
+        
+    def OnParamEntry(self, event):
+        """!Parameter value changed"""
+        id  = event.GetId()
+        val = event.GetString()
+        
+        if id not in self.pparam:
+            event.Skip()
+            return
+
+        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):
+        """!Go to next page"""
+        if event.GetDirection():
+            self.p4projparams = ''
+            for id, param in self.pparam.iteritems():
+                if param['type'] == 'bool':
+                    if param['value'] == False:
+                        continue
+                    else:
+                        self.p4projparams += (' +' + param['proj4'])
+                else:
+                    if param['value'] is None:
+                        wx.MessageBox(parent = self,
+                                      message = _('You must enter a value for %s') % param['desc'],
+                                      caption = _('Error'), style = wx.ICON_ERROR | wx.CENTRE)
+                        event.Veto()
+                    else:
+                        self.p4projparams += (' +' + param['proj4'] + '=' + str(param['value']))
+
+    def OnEnterPage(self,event):
+        """!Page entered"""
+        self.projdesc = self.parent.projections[self.parent.projpage.proj][0]
+        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(parent = self, id = wx.ID_ANY)
+            self.panel.SetupScrolling()
+            
+            self.prjParamSizer = wx.GridBagSizer(vgap = 0, hgap = 0) 
+            
+            self.sizer.Add(item = paramSBSizer, pos = (1, 1),
+                           flag = wx.EXPAND)
+            paramSBSizer.Add(item = self.panel, proportion = 1, 
+                             flag = wx.ALIGN_CENTER | wx.EXPAND)
+            
+            paramSBSizer.Fit(self.panel)
+            self.panel.SetSizer(self.prjParamSizer)
+                    
+        if event.GetDirection(): 
+            self.prjParamSizer.Clear(True)
+            self.paramSBox.SetLabel(_(" Enter parameters for %s projection ") % self.projdesc)
+            self.pparam = dict()
+            row = 0
+            for paramgrp in self.parent.projections[self.parent.projpage.proj][1]:
+                # get parameters
+                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 param['type'] == 'bool':
+                    param['value'] = 0
+                elif param['type'] == 'zone': 
+                    param['value'] = 30 
+                    param['desc'] += ' (1-60)'
+                else:
+                    param['value'] = paramgrp[2]
+                
+                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:
+                    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':
+                        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 = win, pos = (row, 2),
+                                       flag = wx.ALIGN_LEFT | 
+                                       wx.ALIGN_CENTER_VERTICAL |
+                                       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)
+    and datum transformation parameters (select coordinate system option)
+    """
+
+    def __init__(self, wizard, parent):
+        TitledPage.__init__(self, wizard, _("Specify geodetic datum"))
+
+        self.parent = parent
+        self.datum = ''
+        self.datumdesc = ''
+        self.ellipse = ''
+        self.datumparams = ''
+        self.proj4params = ''
+
+        # text input
+        self.tdatum = self.MakeTextCtrl("", size = (200,-1))
+
+        # search box
+        self.searchb = wx.SearchCtrl(self, size = (200,-1),
+                                     style = wx.TE_PROCESS_ENTER)
+
+        # create list control for datum/elipsoid list
+        data = []
+        for key in self.parent.datums.keys():
+            data.append([key, self.parent.datums[key][0], self.parent.datums[key][1]])
+        self.datumlist = ItemList(self,
+                                  data = data,
+                                  columns = [_('Code'), _('Ellipsoid'), _('Description')])
+        self.datumlist.resizeLastColumn(10) 
+        
+        # layout
+        self.sizer.AddGrowableCol(4)
+        self.sizer.Add(item = self.MakeLabel(_("Datum code:")),
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (1, 1))
+        self.sizer.Add(item = self.tdatum,
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (1, 2))
+
+        self.sizer.Add(item = self.MakeLabel(_("Search in description:")),
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (2, 1))
+        self.sizer.Add(item = self.searchb,
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (2, 2))
+
+        self.sizer.AddGrowableRow(3)
+        self.sizer.Add(item = self.datumlist,
+                       flag = wx.EXPAND |
+                       wx.ALIGN_LEFT |
+                       wx.ALL, border = 5, pos = (3, 1), span = (1, 4))
+
+        # events
+        self.datumlist.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnDatumSelected)
+        self.searchb.Bind(wx.EVT_TEXT_ENTER, self.OnDSearch)
+        self.tdatum.Bind(wx.EVT_TEXT, self.OnDText)
+        self.tdatum.Bind(wx.EVT_TEXT_ENTER, self.OnDText)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
+
+        # do page layout
+        # self.DoLayout()
+
+    def OnPageChanging(self, event):
+        self.proj4params = ''
+        proj = self.parent.projpage.p4proj
+                
+        if event.GetDirection():
+            if self.datum not in self.parent.datums:
+                event.Veto()
+            else:
+                # check for datum tranforms            
+#                proj4string = self.parent.CreateProj4String() + ' +datum=%s' % self.datum
+                ret = RunCommand('g.proj',
+                                 read = True,
+                                 proj4 = '%s +datum=%s' % (proj, self.datum), 
+                                 datumtrans = '-1')
+                if ret != '':
+                    dtrans = ''
+                    # open a dialog to select datum transform number
+                    dlg = SelectTransformDialog(self.parent.parent, transforms=ret)
+                    
+                    if dlg.ShowModal() == wx.ID_OK:
+                        dtrans = dlg.GetTransform()
+                        if dtrans == '':
+                            dlg.Destroy()
+                            event.Veto()
+                            return 'Datum transform is required.'
+                    else:
+                        dlg.Destroy()
+                        event.Veto()
+                        return 'Datum transform is required.'
+                    
+                    self.parent.datumtrans = dtrans
+                
+            self.GetNext().SetPrev(self)
+            self.parent.ellipsepage.ellipse = self.ellipse
+            self.parent.ellipsepage.ellipseparams = self.parent.ellipsoids[self.ellipse][1]
+
+    def OnEnterPage(self,event):
+        self.parent.datumtrans = None
+        if event.GetDirection():
+            if len(self.datum) == 0:
+                # disable 'next' button by default when entering from previous page
+                wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+            else:
+                wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+        event.Skip()
+
+    def OnDText(self, event):
+        """!Datum code changed"""
+        self.datum = event.GetString()
+
+        nextButton = wx.FindWindowById(wx.ID_FORWARD)
+        if len(self.datum) == 0 or self.datum not in self.parent.datums:
+            nextButton.Enable(False)
+        else:
+            self.ellipse = self.parent.datums[self.datum][0]
+            self.datumdesc = self.parent.datums[self.datum][1]
+            self.datumparams = self.parent.datums[self.datum][2]
+            try:
+                self.datumparams.remove('dx=0.0')
+            except:
+                pass
+            try:
+                self.datumparams.remove('dy=0.0')
+            except:
+                pass
+            try:
+                self.datumparams.remove('dz=0.0')
+            except:
+                pass
+            
+            nextButton.Enable(True)
+            
+        self.Update()    
+        event.Skip()
+
+    def OnDSearch(self, event):
+        """!Search geodetic datum by desc"""
+        str =  self.searchb.GetValue()
+        try:
+            self.datum, self.ellipsoid, self.datumdesc = self.datumlist.Search(index = [0,1,2], pattern = str)
+        except:
+            self.datum = self.datumdesc = self.ellipsoid = ''
+
+        event.Skip()
+
+    def OnDatumSelected(self, event):
+        """!Datum selected"""
+        index = event.m_itemIndex
+        item = event.GetItem()
+
+        self.datum = self.datumlist.GetItem(index, 0).GetText()
+        self.tdatum.SetValue(self.datum)
+        
+        event.Skip()
+
+class EllipsePage(TitledPage):
+    """!Wizard page for selecting ellipsoid (select coordinate system option)"""
+
+    def __init__(self, wizard, parent):
+        TitledPage.__init__(self, wizard, _("Specify ellipsoid"))
+
+        self.parent = parent
+        
+        self.ellipse = ''
+        self.ellipsedesc = ''
+        self.ellipseparams = ''
+        self.proj4params = ''
+
+        # text input
+        self.tellipse = self.MakeTextCtrl("", size = (200,-1))
+
+        # search box
+        self.searchb = wx.SearchCtrl(self, size = (200,-1),
+                                     style = wx.TE_PROCESS_ENTER)
+
+        # create list control for ellipse list
+        data = []
+        # extract code, desc
+        for key in self.parent.ellipsoids.keys():
+            data.append([key, self.parent.ellipsoids[key][0]])
+
+        self.ellipselist = ItemList(self, data = data,
+                                    columns = [_('Code'), _('Description')])
+        self.ellipselist.resizeLastColumn(30)                             
+
+        # layout
+        self.sizer.AddGrowableCol(4)
+        self.sizer.Add(item = self.MakeLabel(_("Ellipsoid code:")),
+                       flag = wx.ALIGN_RIGHT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (1, 1))
+        self.sizer.Add(item = self.tellipse,
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (1, 2))
+        self.sizer.Add(item = self.MakeLabel(_("Search in description:")),
+                       flag = wx.ALIGN_RIGHT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (2, 1))
+        self.sizer.Add(item = self.searchb,
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (2, 2))
+
+        self.sizer.AddGrowableRow(3)
+        self.sizer.Add(item = self.ellipselist,
+                       flag = wx.EXPAND |
+                       wx.ALIGN_LEFT |
+                       wx.ALL, border = 5, pos = (3, 1), span = (1, 4))
+
+        # events
+        self.ellipselist.Bind(wx.EVT_LIST_ITEM_SELECTED,    self.OnItemSelected)
+        self.tellipse.Bind(wx.EVT_TEXT, self.OnText)
+        self.tellipse.Bind(wx.EVT_TEXT_ENTER, self.OnText)
+        self.searchb.Bind(wx.EVT_TEXT_ENTER,    self.OnSearch)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
+
+    def OnEnterPage(self,event):
+        if len(self.ellipse) == 0:
+            # disable 'next' button by default
+            wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+        else:
+            wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+        event.Skip()
+
+    def OnPageChanging(self, event):
+        if event.GetDirection() and self.ellipse not in self.parent.ellipsoids:
+            event.Veto()
+
+        self.proj4params = ''
+        self.GetNext().SetPrev(self)
+        self.parent.datumpage.datumparams = ''
+        # self.GetNext().SetPrev(self) (???)
+
+    def OnText(self, event):
+        """!Ellipspoid code changed"""
+        self.ellipse = event.GetString()
+        nextButton = wx.FindWindowById(wx.ID_FORWARD)
+        if len(self.ellipse) == 0 or self.ellipse not in self.parent.ellipsoids:
+            nextButton.Enable(False)
+            self.ellipsedesc = ''
+            self.ellipseparams = ''
+            self.proj4params = ''
+        elif self.ellipse in self.parent.ellipsoids:
+            self.ellipsedesc = self.parent.ellipsoids[self.ellipse][0]
+            self.ellipseparams = self.parent.ellipsoids[self.ellipse][1]
+            nextButton.Enable(True)
+
+    def OnSearch(self, event):
+        """!Search ellipsoid by desc"""
+        try:
+            self.ellipse, self.ellipsedesc = \
+                self.ellipselist.Search(index=[0,1], pattern=event.GetString())
+            self.ellipseparams = self.parent.ellipsoids[self.ellipse][1]
+        except:
+            self.ellipse = self.ellipsedesc = self.ellipseparams = ''
+
+        event.Skip()
+
+    def OnItemSelected(self,event):
+        """!Ellipsoid selected"""
+        index = event.m_itemIndex
+        item = event.GetItem()
+
+        self.ellipse = self.ellipselist.GetItem(index, 0).GetText()
+        self.tellipse.SetValue(self.ellipse)
+        
+        event.Skip()
+
+class GeoreferencedFilePage(TitledPage):
+    """!Wizard page for selecting georeferenced file to use
+    for setting coordinate system parameters"""
+
+    def __init__(self, wizard, parent):
+        TitledPage.__init__(self, wizard, _("Select georeferenced file"))
+
+        self.georeffile = ''
+
+        # create controls
+        self.lfile= self.MakeLabel(_("Georeferenced file:"))
+        self.tfile = self.MakeTextCtrl(size = (300,-1))
+        self.bbrowse = self.MakeButton(_("Browse"))
+
+        # do layout
+        self.sizer.AddGrowableCol(3)
+        self.sizer.Add(item = self.lfile, flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTRE_VERTICAL |
+                       wx.ALL, border = 5, pos = (1, 1))
+        self.sizer.Add(item = self.tfile, flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTRE_VERTICAL |
+                       wx.ALL, border = 5, pos = (1, 2))
+        self.sizer.Add(item = self.bbrowse, flag = wx.ALIGN_LEFT |
+                       wx.ALL, border = 5, pos = (1, 3))
+
+        self.bbrowse.Bind(wx.EVT_BUTTON, self.OnBrowse)
+        self.tfile.Bind(wx.EVT_TEXT, self.OnText)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
+
+        # do page layout
+        # self.DoLayout()
+
+    def OnEnterPage(self, event):
+        if len(self.georeffile) == 0:
+            # disable 'next' button by default
+            wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+        else:
+            wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+        event.Skip()
+
+    def OnPageChanging(self, event):
+        if event.GetDirection() and not os.path.isfile(self.georeffile):
+            event.Veto()
+        self.GetNext().SetPrev(self)
+
+        event.Skip()
+
+    def OnText(self, event):
+        """!File changed"""
+        self.georeffile = event.GetString()
+        nextButton = wx.FindWindowById(wx.ID_FORWARD)
+        if len(self.georeffile) > 0 and os.path.isfile(self.georeffile):
+            if not nextButton.IsEnabled():
+                nextButton.Enable(True)
+        else:
+            if nextButton.IsEnabled():
+                nextButton.Enable(False)
+
+        event.Skip()
+
+    def OnBrowse(self, event):
+        """!Choose file"""
+        dlg = wx.FileDialog(self,
+                            _("Select georeferenced file"),
+                            os.getcwd(), "", "*.*", wx.OPEN)
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            self.tfile.SetValue(path)
+        dlg.Destroy()
+
+        event.Skip()
+
+class WKTPage(TitledPage):
+    """!Wizard page for selecting WKT file to use
+    for setting coordinate system parameters"""
+
+    def __init__(self, wizard, parent):
+        TitledPage.__init__(self, wizard, _("Select WKT file"))
+
+        self.wktfile = ''
+
+        # create controls
+        self.lfile= self.MakeLabel(_("WKT file:"))
+        self.tfile = self.MakeTextCtrl(size = (300,-1))
+        self.bbrowse = self.MakeButton(_("Browse"))
+
+        # do layout
+        self.sizer.AddGrowableCol(3)
+        self.sizer.Add(item = self.lfile, flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTRE_VERTICAL |
+                       wx.ALL, border = 5, pos = (1, 1))
+        self.sizer.Add(item = self.tfile, flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTRE_VERTICAL |
+                       wx.ALL, border = 5, pos = (1, 2))
+        self.sizer.Add(item = self.bbrowse, flag = wx.ALIGN_LEFT |
+                       wx.ALL, border = 5, pos = (1, 3))
+
+        self.bbrowse.Bind(wx.EVT_BUTTON, self.OnBrowse)
+        self.tfile.Bind(wx.EVT_TEXT, self.OnText)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
+
+    def OnEnterPage(self, event):
+        if len(self.wktfile) == 0:
+            # disable 'next' button by default
+            wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+        else:
+            wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+        event.Skip()
+
+    def OnPageChanging(self, event):
+        if event.GetDirection() and not os.path.isfile(self.wktfile):
+            event.Veto()
+        self.GetNext().SetPrev(self)
+
+        event.Skip()
+
+    def OnText(self, event):
+        """!File changed"""
+        self.wktfile = event.GetString()
+        nextButton = wx.FindWindowById(wx.ID_FORWARD)
+        if len(self.wktfile) > 0 and os.path.isfile(self.wktfile):
+            if not nextButton.IsEnabled():
+                nextButton.Enable(True)
+        else:
+            if nextButton.IsEnabled():
+                nextButton.Enable(False)
+
+        event.Skip()
+
+    def OnBrowse(self, event):
+        """!Choose file"""
+        dlg = wx.FileDialog(self,
+                            _("Select WKT file"),
+                            os.getcwd(), "", "*.*", wx.OPEN)
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            self.tfile.SetValue(path)
+        dlg.Destroy()
+
+        event.Skip()
+
+class EPSGPage(TitledPage):
+    """!Wizard page for selecting EPSG code for
+    setting coordinate system parameters"""
+
+    def __init__(self, wizard, parent):
+        TitledPage.__init__(self, wizard, _("Choose EPSG Code"))
+        self.parent = parent
+        self.epsgCodeDict = {}
+        self.epsgcode = None
+        self.epsgdesc = ''
+        self.epsgparams = ''
+
+        # labels
+        self.lfile = self.MakeLabel(_("Path to the EPSG-codes file:"),
+                                    style = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL)
+        self.lcode = self.MakeLabel(_("EPSG code:"),
+                                    style = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL)
+        # text input
+        epsgdir = utils.PathJoin(os.environ["GRASS_PROJSHARE"], 'epsg')
+        self.tfile = self.MakeTextCtrl(text = epsgdir, size = (200,-1),
+                                       style = wx.TE_PROCESS_ENTER)
+        self.tcode = self.MakeTextCtrl(size = (200,-1))
+
+        # buttons
+        self.bbrowse = self.MakeButton(_("Browse"))
+        
+        # search box
+        self.searchb = wx.SearchCtrl(self, size = (200,-1),
+                                     style = wx.TE_PROCESS_ENTER)
+
+        self.epsglist = ItemList(self, data = None,
+                                 columns = [_('Code'), _('Description'), _('Parameters')])
+
+        # layout
+        self.sizer.AddGrowableCol(3)
+        self.sizer.Add(item = self.lfile,
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (1, 1), span = (1, 2))
+        self.sizer.Add(item = self.tfile,
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (1, 3))
+        self.sizer.Add(item = self.bbrowse,
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (1, 4))
+        self.sizer.Add(item = self.lcode,
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (2, 1), span = (1, 2))
+        self.sizer.Add(item = self.tcode,
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (2, 3))
+        self.sizer.Add(item = self.searchb,
+                       flag = wx.ALIGN_LEFT |
+                       wx.ALIGN_CENTER_VERTICAL |
+                       wx.ALL, border = 5, pos = (3, 3))
+        
+        self.sizer.AddGrowableRow(4)
+        self.sizer.Add(item = self.epsglist,
+                       flag = wx.ALIGN_LEFT | wx.EXPAND, pos = (4, 1),
+                       span = (1, 4))
+
+        # events
+        self.bbrowse.Bind(wx.EVT_BUTTON, self.OnBrowse)
+        self.tfile.Bind(wx.EVT_TEXT_ENTER, self.OnBrowseCodes)
+        self.tcode.Bind(wx.EVT_TEXT, self.OnText)
+        self.tcode.Bind(wx.EVT_TEXT_ENTER, self.OnText)
+        self.epsglist.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
+        self.searchb.Bind(wx.EVT_TEXT_ENTER, self.OnSearch)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
+
+    def OnEnterPage(self, event):
+        self.parent.datumtrans = None
+        if event.GetDirection():
+            if not self.epsgcode:
+                # disable 'next' button by default
+                wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+            else:
+                wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+        # load default epsg database file
+        self.OnBrowseCodes(None)
+        
+        event.Skip()
+
+    def OnPageChanging(self, event):
+        if event.GetDirection():
+            if not self.epsgcode:
+                event.Veto()
+                return
+            else:              
+                # check for datum transforms
+                ret = RunCommand('g.proj',
+                                 read = True,
+                                 epsg = self.epsgcode,
+                                 datumtrans = '-1')
+                
+                if ret != '':
+                    dtrans = ''
+                    # open a dialog to select datum transform number
+                    dlg = SelectTransformDialog(self.parent.parent, transforms = ret)
+                    
+                    if dlg.ShowModal() == wx.ID_OK:
+                        dtrans = dlg.GetTransform()
+                        if dtrans == '':
+                            dlg.Destroy()
+                            event.Veto()
+                            return 'Datum transform is required.'
+                    else:
+                        dlg.Destroy()
+                        event.Veto()
+                        return 'Datum transform is required.'
+                    
+                    self.parent.datumtrans = dtrans
+            self.GetNext().SetPrev(self)
+
+    def OnText(self, event):
+        self.epsgcode = event.GetString()
+        try:
+            self.epsgcode = int(self.epsgcode)
+        except:
+            self.epsgcode = None
+            
+        nextButton = wx.FindWindowById(wx.ID_FORWARD)
+
+        if self.epsgcode and self.epsgcode in self.epsgCodeDict.keys():
+            self.epsgdesc = self.epsgCodeDict[self.epsgcode][0]
+            self.epsgparams = self.epsgCodeDict[self.epsgcode][1]
+            if not nextButton.IsEnabled():
+                nextButton.Enable(True)
+        else:
+            self.epsgcode = None # not found
+            if nextButton.IsEnabled():
+                nextButton.Enable(False)
+            self.epsgdesc = self.epsgparams = ''
+        
+    def OnSearch(self, event):
+        value =  self.searchb.GetValue()
+        
+        if value == '':
+            self.epsgcode = None
+            self.epsgdesc = self.epsgparams = ''
+            self.tcode.SetValue('')
+            self.searchb.SetValue('')
+            self.OnBrowseCodes(None)
+        else:    
+            try:
+                self.epsgcode, self.epsgdesc, self.epsgparams = \
+                        self.epsglist.Search(index=[0,1,2], pattern=value)
+            except (IndexError, ValueError): # -> no item found
+                self.epsgcode = None
+                self.epsgdesc = self.epsgparams = ''
+                self.tcode.SetValue('')
+                self.searchb.SetValue('')
+
+        event.Skip()
+        
+    def OnBrowse(self, event):
+        """!Define path for EPSG code file"""
+        path = os.path.dirname(self.tfile.GetValue())
+        if not path:
+            path = os.getcwd()
+        
+        dlg = wx.FileDialog(parent = self, message = _("Choose EPSG codes file"),
+                            defaultDir = path, defaultFile = "", wildcard = "*", style = wx.OPEN)
+        
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            self.tfile.SetValue(path)
+            self.OnBrowseCodes(None)
+        
+        dlg.Destroy()
+
+        event.Skip()
+
+    def OnItemSelected(self, event):
+        """!EPSG code selected from the list"""
+        index = event.m_itemIndex
+        item = event.GetItem()
+
+        self.epsgcode = int(self.epsglist.GetItem(index, 0).GetText())
+        self.epsgdesc = self.epsglist.GetItem(index, 1).GetText()
+        self.tcode.SetValue(str(self.epsgcode))
+
+        event.Skip()
+        
+    def OnBrowseCodes(self, event, search = None):
+        """!Browse EPSG codes"""
+        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(list(), update = True)
+            return
+        
+        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):
+    """!Wizard page for entering custom PROJ.4 string
+    for setting coordinate system parameters"""
+
+    def __init__(self, wizard, parent):
+        TitledPage.__init__(self, wizard,
+                            _("Choose method of specifying georeferencing parameters"))
+        global coordsys
+        self.customstring = ''
+        self.parent = parent
+
+        # widgets
+        self.text_proj4string = self.MakeTextCtrl(size = (400, 200),
+                                                  style = wx.TE_MULTILINE)
+        self.label_proj4string = self.MakeLabel(_("Enter PROJ.4 parameters string:"))
+
+        # layout
+        self.sizer.AddGrowableCol(2)
+        self.sizer.Add(self.label_proj4string,
+                       flag = wx.ALIGN_LEFT | wx.ALL,
+                       border = 5, pos = (1, 1))
+        self.sizer.AddGrowableRow(2)
+        self.sizer.Add(self.text_proj4string,
+                       flag = wx.ALIGN_LEFT | wx.ALL | wx.EXPAND, 
+                       border = 5, pos = (2, 1), span = (1, 2))
+
+        self.text_proj4string.Bind(wx.EVT_TEXT, self.GetProjstring)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
+
+    def OnEnterPage(self, event):
+        if len(self.customstring) == 0:
+            # disable 'next' button by default
+            wx.FindWindowById(wx.ID_FORWARD).Enable(False)
+        else:
+            wx.FindWindowById(wx.ID_FORWARD).Enable(True)
+
+    def OnPageChanging(self, event):
+        if event.GetDirection():
+        # check for datum tranforms            
+            ret, out, err = 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 len(dtrans) == 0:
+                        dlg.Destroy()
+                        event.Veto()
+                        return _('Datum transform is required.')
+                else:
+                    dlg.Destroy()
+                    event.Veto()
+                    return _('Datum transform is required.')
+                
+                self.parent.datumtrans = dtrans
+        
+        self.GetNext().SetPrev(self)
+            
+    def GetProjstring(self, event):
+        """!Change proj string"""
+        # TODO: check PROJ.4 syntax
+        self.customstring = event.GetString()
+        nextButton = wx.FindWindowById(wx.ID_FORWARD)
+        if len(self.customstring) == 0:
+            if nextButton.IsEnabled():
+                nextButton.Enable(False)
+        else:
+            if not nextButton.IsEnabled():
+                nextButton.Enable()
+
+class SummaryPage(TitledPage):
+    """!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
+
+        self.panelTitle = scrolled.ScrolledPanel(parent = self, id = wx.ID_ANY)
+        self.panelProj4string = scrolled.ScrolledPanel(parent = self, id = wx.ID_ANY)
+        self.panelProj = scrolled.ScrolledPanel(parent = self, id = wx.ID_ANY)
+
+        # labels
+        self.ldatabase    = self.MakeLabel()
+        self.llocation    = self.MakeLabel()
+        self.llocTitle    = self.MakeLabel(parent = self.panelTitle)
+        self.lprojection  = self.MakeLabel(parent = self.panelProj)
+        self.lproj4string = self.MakeLabel(parent = self.panelProj4string)
+        
+        self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage)
+        
+        # do sub-page layout
+        self._doLayout()
+        
+    def _doLayout(self):
+        """!Do page layout"""
+        self.sizer.AddGrowableCol(1)
+        self.sizer.AddGrowableRow(3, 1)
+        self.sizer.AddGrowableRow(4, 1)
+        self.sizer.AddGrowableRow(5, 5)
+
+        titleSizer = wx.BoxSizer(wx.VERTICAL)
+        titleSizer.Add(item = self.llocTitle, proportion = 1,
+                       flag = wx.EXPAND | wx.ALL, border = 5)
+        self.panelTitle.SetSizer(titleSizer)        
+        
+        projSizer = wx.BoxSizer(wx.VERTICAL)
+        projSizer.Add(item = self.lprojection, proportion = 1,
+                       flag = wx.EXPAND | wx.ALL, border = 5)
+        self.panelProj.SetSizer(projSizer)        
+        
+        proj4stringSizer = wx.BoxSizer(wx.VERTICAL)
+        proj4stringSizer.Add(item = self.lproj4string, proportion = 1,
+                       flag = wx.EXPAND | wx.ALL, border = 5)
+        self.panelProj4string.SetSizer(proj4stringSizer)
+
+        self.panelProj4string.SetupScrolling()
+        self.panelProj.SetupScrolling(scroll_y = False)
+        self.panelTitle.SetupScrolling(scroll_y = False)
+
+        self.sizer.Add(item = self.MakeLabel(_("GRASS Database:")),
+                       flag = wx.ALIGN_LEFT | wx.ALL,
+                       border = 5, pos = (1, 0))
+        self.sizer.Add(item = self.ldatabase, 
+                       flag = wx.ALIGN_LEFT | wx.ALL,
+                       border = 5, pos = (1, 1))
+        self.sizer.Add(item = self.MakeLabel(_("Location Name:")),
+                       flag = wx.ALIGN_LEFT | wx.ALL,
+                       border = 5, pos = (2, 0))
+        self.sizer.Add(item = self.llocation,
+                       flag = wx.ALIGN_LEFT | wx.ALL,
+                       border = 5, pos = (2, 1))
+        self.sizer.Add(item = self.MakeLabel(_("Location Title:")),
+                       flag = wx.ALIGN_LEFT | wx.ALL,
+                       border = 5, pos = (3, 0))
+        self.sizer.Add(item = self.panelTitle,
+                       flag = wx.ALIGN_LEFT | wx.ALL | wx.EXPAND,
+                       border = 0, pos = (3, 1))
+        self.sizer.Add(item = self.MakeLabel(_("Projection:")),
+                       flag = wx.ALIGN_LEFT | wx.ALL,
+                       border = 5, pos = (4, 0))
+        self.sizer.Add(item = self.panelProj,
+                       flag = wx.ALIGN_LEFT | wx.ALL | wx.EXPAND,
+                       border = 0, pos = (4, 1))
+        self.sizer.Add(item = self.MakeLabel(_("PROJ.4 definition:")),
+                       flag = wx.ALIGN_LEFT | wx.ALL,
+                       border = 5, pos = (5, 0))
+        self.sizer.Add(item = self.panelProj4string,
+                       flag = wx.ALIGN_LEFT | wx.ALL | wx.EXPAND,
+                       border = 0, pos = (5, 1))
+   
+    def OnEnterPage(self, event):
+        """!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()
+        epsgcode = self.parent.epsgpage.epsgcode
+        dtrans = self.parent.datumtrans
+        
+        global coordsys
+        if coordsys in ('proj', 'epsg'):
+            if coordsys == 'proj':
+                ret, projlabel, err = RunCommand('g.proj',
+                                                 flags = 'jf',
+                                                 proj4 = proj4string,
+                                                 datumtrans = dtrans,
+                                                 location = location,
+                                                 getErrorMsg = True,
+                                                 read = True)
+            elif coordsys == 'epsg':
+                ret, projlabel, err = RunCommand('g.proj',
+                                                 flags = 'jf',
+                                                 epsg = epsgcode,
+                                                 datumtrans = dtrans,
+                                                 location = location,
+                                                 getErrorMsg = True,
+                                                 read = True)
+
+            finishButton = wx.FindWindowById(wx.ID_FORWARD)
+            if ret == 0:
+                self.lproj4string.SetLabel(projlabel.replace(' ', os.linesep))
+                finishButton.Enable(True)
+            else:
+                GError(err, parent = self)
+                self.lproj4string.SetLabel('')
+                finishButton.Enable(False)
+        
+        projdesc = self.parent.projpage.projdesc
+        ellipsedesc = self.parent.ellipsepage.ellipsedesc
+        datumdesc = self.parent.datumpage.datumdesc
+        self.ldatabase.SetLabel(database)
+        self.llocation.SetLabel(location)
+        self.llocTitle.SetLabel(self.parent.startpage.locTitle)
+        
+        label = ''
+        if coordsys == 'epsg':
+            label = 'EPSG code %s (%s)' % (self.parent.epsgpage.epsgcode, self.parent.epsgpage.epsgdesc)
+        elif coordsys == 'file':
+            label = 'matches file %s' % self.parent.filepage.georeffile
+            self.lproj4string.SetLabel("")
+        elif coordsys == 'wkt':
+            label = 'matches file %s' % self.parent.wktpage.wktfile
+            self.lproj4string.SetLabel("")
+        elif coordsys == 'proj':
+            label = ('%s, %s %s' % (projdesc, datumdesc, ellipsedesc))
+        elif coordsys == 'xy':
+            label = ('XY coordinate system (not projected).')
+            self.lproj4string.SetLabel("")
+        elif coordsys == 'custom':
+            label = _("custom")
+            self.lproj4string.SetLabel(('%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()
+        else:
+            dlg.Destroy()
+            event.Skip()
+
+class LocationWizard(wx.Object):
+    """!Start wizard here and finish wizard here
+    """
+    def __init__(self, parent, grassdatabase):
+        self.__cleanUp()
+        
+        global coordsys
+        self.parent = parent
+        
+        #
+        # define wizard image
+        #
+        imagePath = os.path.join(globalvar.ETCIMGDIR, "loc_wizard_qgis.png")
+        wizbmp = wx.Image(imagePath, wx.BITMAP_TYPE_PNG)
+        wizbmp = wizbmp.ConvertToBitmap()
+        
+        #
+        # get georeferencing information from tables in $GISBASE/etc
+        #
+        self.__readData()
+        
+        #
+        # datum transform number and list of datum transforms
+        #
+        self.datumtrans = None
+        self.proj4string = ''
+        
+        #
+        # define wizard pages
+        #
+        self.wizard = wiz.Wizard(parent, id = wx.ID_ANY, title = _("Define new GRASS Location"),
+                                 bitmap = wizbmp, style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
+        self.startpage = DatabasePage(self.wizard, self, grassdatabase)
+        self.csystemspage = CoordinateSystemPage(self.wizard, self)
+        self.projpage = ProjectionsPage(self.wizard, self)
+        self.datumpage = DatumPage(self.wizard, self)
+        self.paramspage = ProjParamsPage(self.wizard,self)
+        self.epsgpage = EPSGPage(self.wizard, self)
+        self.filepage = GeoreferencedFilePage(self.wizard, self)
+        self.wktpage = WKTPage(self.wizard, self)
+        self.ellipsepage = EllipsePage(self.wizard, self)
+        self.custompage = CustomPage(self.wizard, self)
+        self.sumpage = SummaryPage(self.wizard, self)
+
+        #
+        # set the initial order of the pages
+        # (should follow the epsg line)
+        #
+        self.startpage.SetNext(self.csystemspage)
+
+        self.csystemspage.SetPrev(self.startpage)
+        self.csystemspage.SetNext(self.sumpage)
+
+        self.projpage.SetPrev(self.csystemspage)
+        self.projpage.SetNext(self.paramspage)
+
+        self.paramspage.SetPrev(self.projpage)
+        self.paramspage.SetNext(self.datumpage)
+
+        self.datumpage.SetPrev(self.paramspage)
+        self.datumpage.SetNext(self.sumpage)
+
+        self.ellipsepage.SetPrev(self.paramspage)
+        self.ellipsepage.SetNext(self.sumpage)
+
+        self.epsgpage.SetPrev(self.csystemspage)
+        self.epsgpage.SetNext(self.sumpage)
+
+        self.filepage.SetPrev(self.csystemspage)
+        self.filepage.SetNext(self.sumpage)
+
+        self.wktpage.SetPrev(self.csystemspage)
+        self.wktpage.SetNext(self.sumpage)
+
+        self.custompage.SetPrev(self.csystemspage)
+        self.custompage.SetNext(self.sumpage)
+
+        self.sumpage.SetPrev(self.csystemspage)
+        
+        #
+        # do pages layout
+        #
+        self.startpage.DoLayout()
+        self.csystemspage.DoLayout()
+        self.projpage.DoLayout()
+        self.datumpage.DoLayout()
+        self.paramspage.DoLayout()
+        self.epsgpage.DoLayout()
+        self.filepage.DoLayout()
+        self.wktpage.DoLayout()
+        self.ellipsepage.DoLayout()
+        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
+        
+        # location created in different GIS database?
+        self.altdb = False
+
+        #
+        # run wizard...
+        #
+        if self.wizard.RunWizard(self.startpage):
+            msg = self.OnWizFinished()
+            if not msg:
+                self.wizard.Destroy()
+                self.location = self.startpage.location
+                
+                if self.altdb == False: 
+                    dlg = wx.MessageDialog(parent = self.parent,
+                                           message = _("Do you want to set the default "
+                                                     "region extents and resolution now?"),
+                                           caption = _("Location <%s> created") % self.location,
+                                           style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
+                    dlg.CenterOnScreen()
+                    if dlg.ShowModal() == wx.ID_YES:
+                        dlg.Destroy()
+                        defineRegion = RegionDef(self.parent, location = self.location)
+                        defineRegion.CenterOnScreen()
+                        defineRegion.Show()
+                    else:
+                        dlg.Destroy()
+            else: # -> error
+                self.wizard.Destroy()
+                GError(parent = self.parent,
+                       message = "%s" % _("Unable to create new location. "
+                                          "Location <%(loc)s> not created.\n\n"
+                                          "Details: %(err)s") % \
+                           { 'loc' : self.startpage.location,
+                             'err' : msg })
+        else: # -> canceled
+            self.wizard.Destroy()
+            GMessage(parent = self.parent,
+                     message = _("Location wizard canceled. "
+                                 "Location not created."))
+            
+        self.__cleanUp()
+        
+    def __cleanUp(self):
+        global coordsys
+        global north
+        global south
+        global east
+        global west
+        global resolution
+        global wizerror
+        global translist
+        
+        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"""
+
+        # read projection and parameters
+        f = open(os.path.join(globalvar.ETCDIR, "proj-parms.table"), "r")
+        self.projections = {}
+        self.projdesc = {}
+        for line in f.readlines():
+            line = line.strip()
+            try:
+                proj, projdesc, params = line.split(':')
+                paramslist = params.split(';')
+                plist = []
+                for p in paramslist:
+                    if p == '': continue
+                    p1, pdefault = p.split(',')
+                    pterm, pask = p1.split('=')
+                    p = [pterm.strip(), pask.strip(), pdefault.strip()]
+                    plist.append(p)
+                self.projections[proj.lower().strip()] = (projdesc.strip(), plist)
+                self.projdesc[proj.lower().strip()] = projdesc.strip()
+            except:
+                continue
+        f.close()
+
+        # read datum definitions
+        f = open(os.path.join(globalvar.ETCDIR, "datum.table"), "r")
+        self.datums = {}
+        paramslist = []
+        for line in f.readlines():
+            line = line.expandtabs(1)
+            line = line.strip()
+            if line == '' or line[0] == "#":
+                continue
+            datum, info = line.split(" ", 1)
+            info = info.strip()
+            datumdesc, params = info.split(" ", 1)
+            datumdesc = datumdesc.strip('"')
+            paramlist = params.split()
+            ellipsoid = paramlist.pop(0)
+            self.datums[datum] = (ellipsoid, datumdesc.replace('_', ' '), paramlist)
+        f.close()
+
+        # read ellipsiod definitions
+        f = open(os.path.join(globalvar.ETCDIR, "ellipse.table"), "r")
+        self.ellipsoids = {}
+        for line in f.readlines():
+            line = line.expandtabs(1)
+            line = line.strip()
+            if line == '' or line[0] == "#":
+                continue
+            ellipse, rest = line.split(" ", 1)
+            rest = rest.strip('" ')
+            desc, params = rest.split('"', 1)
+            desc = desc.strip('" ')
+            paramslist = params.split()
+            self.ellipsoids[ellipse] = (desc, paramslist)
+        f.close()
+        
+        # read projection parameter description and parsing table
+        f = open(os.path.join(globalvar.ETCDIR, "proj-desc.table"), "r")
+        self.paramdesc = {}
+        for line in f.readlines():
+            line = line.strip()
+            try:
+                pparam, datatype, proj4term, desc = line.split(':')
+                self.paramdesc[pparam] = (datatype, proj4term, desc)
+            except:
+                continue
+        f.close()
+
+    def OnWizFinished(self):
+        """!Wizard finished, create new location
+
+        @return error message on error
+        @return None on success
+        """
+        database = self.startpage.grassdatabase
+        location = self.startpage.location
+        
+        # location already exists?
+        if os.path.isdir(os.path.join(database,location)):
+            GError(parent = self.wizard,
+                   message = "%s <%s>: %s" % \
+                       (_("Unable to create new location"),
+                        os.path.join(database, location),
+                        _("Location already exists in GRASS Database.")))
+            return None
+        
+        # current GISDbase or a new one?
+        current_gdb = grass.gisenv()['GISDBASE']
+        if current_gdb != database:
+            # change to new GISDbase or create new one
+            if os.path.isdir(database) != True:
+                # create new directory
+                os.mkdir(database)
+            
+            # change to new GISDbase directory
+            RunCommand('g.gisenv',
+                       parent = self.wizard,
+                       set = 'GISDBASE=%s' % database)
+            
+            wx.MessageBox(parent = self.wizard,
+                          message = _("Location <%(loc)s> will be created "
+                                    "in GIS data directory <%(dir)s>. "
+                                    "You will need to change the default GIS "
+                                    "data directory in the GRASS startup screen.") % \
+                              { 'loc' : location, 'dir' : database},
+                          caption = _("New GIS data directory"), 
+                          style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+            
+            # location created in alternate GISDbase
+            self.altdb = True
+        
+        global coordsys
+        try:
+            if coordsys == "xy":
+                grass.create_location(dbase = self.startpage.grassdatabase,
+                                      location = self.startpage.location,
+                                      desc = self.startpage.locTitle)
+            elif coordsys == "proj":
+                grass.create_location(dbase = self.startpage.grassdatabase,
+                                      location = self.startpage.location,
+                                      proj4 = self.CreateProj4String(),
+                                      datum = self.datumtrans,
+                                      desc = self.startpage.locTitle)
+            elif coordsys == 'custom':
+                grass.create_location(dbase = self.startpage.grassdatabase,
+                                      location = self.startpage.location,
+                                      proj4 = self.custompage.customstring,
+                                      desc = self.startpage.locTitle)
+            elif coordsys == "epsg":
+                if not self.epsgpage.epsgcode:
+                    return _('EPSG code missing.')
+                
+                grass.create_location(dbase = self.startpage.grassdatabase,
+                                      location = self.startpage.location,
+                                      epsg = self.epsgpage.epsgcode,
+                                      datum = self.datumtrans,
+                                      desc = self.startpage.locTitle)
+            elif coordsys == "file":
+                if not self.filepage.georeffile or \
+                        not os.path.isfile(self.filepage.georeffile):
+                    return _("File <%s> not found." % self.filepage.georeffile)
+                
+                grass.create_location(dbase = self.startpage.grassdatabase,
+                                      location = self.startpage.location,
+                                      filename = self.filepage.georeffile,
+                                      desc = self.startpage.locTitle)
+            elif coordsys == "wkt":
+                if not self.wktpage.wktfile or \
+                        not os.path.isfile(self.wktpage.wktfile):
+                    return _("File <%s> not found." % self.wktpage.wktfile)
+                
+                grass.create_location(dbase = self.startpage.grassdatabase,
+                                      location = self.startpage.location,
+                                      wkt = self.wktpage.wktfile,
+                                      desc = self.startpage.locTitle)
+        
+        except grass.ScriptError, e:
+            return e.value
+        
+        return None
+    
+    def CreateProj4String(self):
+        """!Constract PROJ.4 string"""
+        location = self.startpage.location
+        proj = self.projpage.p4proj
+        projdesc = self.projpage.projdesc
+        proj4params = self.paramspage.p4projparams
+                
+        datum = self.datumpage.datum
+        if self.datumpage.datumdesc:
+            datumdesc = self.datumpage.datumdesc +' - ' + self.datumpage.ellipse
+        else:
+            datumdesc = ''
+        datumparams = self.datumpage.datumparams        
+        ellipse = self.ellipsepage.ellipse
+        ellipsedesc = self.ellipsepage.ellipsedesc
+        ellipseparams = self.ellipsepage.ellipseparams
+                
+        #
+        # creating PROJ.4 string
+        #
+        proj4string = '%s %s' % (proj, proj4params)
+                            
+        # set ellipsoid parameters
+        if ellipse != '':
+            proj4string = '%s +ellps=%s' % (proj4string, ellipse)
+        for item in ellipseparams:
+            if item[:4] == 'f=1/':
+                item = ' +rf=' + item[4:]
+            else:
+                item = ' +' + item
+            proj4string = '%s %s' % (proj4string, item)
+            
+        # set datum and transform parameters if relevant
+        if datum != '':
+            proj4string = '%s +datum=%s' % (proj4string, datum)
+        if datumparams:
+            for item in datumparams:
+                proj4string = '%s +%s' % (proj4string,item)
+
+        proj4string = '%s +no_defs' % proj4string
+        
+        return proj4string


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/frame.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/frame.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/frame.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1276 @@
+"""!
+ at package mapdisp.frame
+
+ at brief Map display with toolbar for various display management
+functions, and additional toolbars (vector digitizer, 3d view).
+
+Can be used either from Layer Manager or as d.mon backend.
+
+Classes:
+ - mapdisp::MapFrame
+
+(C) 2006-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+ at author Vaclav Petras <wenzeslaus gmail.com> (MapFrameBase)
+ at author Anna Kratochvilova <kratochanna gmail.com> (MapFrameBase)
+"""
+
+import os
+import sys
+import math
+import copy
+
+from core import globalvar
+import wx
+import wx.aui
+
+sys.path.append(os.path.join(globalvar.ETCWXDIR, "icons"))
+sys.path.append(os.path.join(globalvar.ETCDIR,   "python"))
+
+from core               import globalvar
+from core.render        import EVT_UPDATE_PRGBAR
+from vdigit.toolbars    import VDigitToolbar
+from mapdisp.toolbars   import MapToolbar, NvizIcons
+from mapdisp.gprint     import PrintOptions
+from core.gcmd          import GError, GMessage, RunCommand
+from dbmgr.dialogs      import DisplayAttributesDialog
+from core.utils         import ListOfCatsToRange, GetLayerNameFromCmd
+from gui_core.dialogs   import GetImageHandlers, ImageSizeDialog, DecorationDialog, TextLayerDialog
+from core.debug         import Debug
+from core.settings      import UserSettings
+from gui_core.mapdisp   import MapFrameBase
+from mapdisp.mapwindow  import BufferedWindow
+from modules.histogram  import HistogramFrame
+from wxplot.profile     import ProfileFrame
+
+from mapdisp import statusbar as sb
+
+from grass.script import core as grass
+
+haveCtypes = False
+
+class MapFrame(MapFrameBase):
+    """!Main frame for map display window. Drawing takes place in
+    child double buffered drawing window.
+    """
+    def __init__(self, parent = None, title = _("GRASS GIS - Map display"),
+                 toolbars = ["map"], tree = None, notebook = None, lmgr = None,
+                 page = None, Map = None, auimgr = None, name = 'MapWindow', **kwargs):
+        """!Main map display window with toolbars, statusbar and
+        BufferedWindow (map canvas)
+        
+        @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
+        @param page notebook page with layer tree
+        @param Map instance of render.Map
+        @param auimgs AUI manager
+        @param name frame name
+        @param kwargs wx.Frame attributes
+        """
+        MapFrameBase.__init__(self, parent = parent, title = title, toolbars = toolbars,
+                              Map = Map, auimgr = auimgr, name = name, **kwargs)
+        
+        self._layerManager = lmgr   # Layer Manager object
+        self.tree       = tree      # Layer Manager layer tree object
+        self.page       = page      # Notebook page holding the layer tree
+        self.layerbook  = notebook  # Layer Manager layer tree notebook
+        #
+        # Add toolbars
+        #
+        for toolb in toolbars:
+            self.AddToolbar(toolb)
+        
+        #
+        # Add statusbar
+        #
+        
+        # items for choice
+        self.statusbarItems = [sb.SbCoordinates,
+                               sb.SbRegionExtent,
+                               sb.SbCompRegionExtent,
+                               sb.SbShowRegion,
+                               sb.SbAlignExtent,
+                               sb.SbResolution,
+                               sb.SbDisplayGeometry,
+                               sb.SbMapScale,
+                               sb.SbGoTo,
+                               sb.SbProjection]
+                            
+        self.statusbarItemsHiddenInNviz = (sb.SbAlignExtent,
+                                           sb.SbDisplayGeometry,
+                                           sb.SbShowRegion,
+                                           sb.SbResolution,
+                                           sb.SbMapScale)
+        
+        # create statusbar and its manager
+        statusbar = self.CreateStatusBar(number = 4, style = 0)
+        statusbar.SetStatusWidths([-5, -2, -1, -1])
+        self.statusbarManager = sb.SbManager(mapframe = self, statusbar = statusbar)
+        
+        # fill statusbar manager
+        self.statusbarManager.AddStatusbarItemsByClass(self.statusbarItems, mapframe = self, statusbar = statusbar)
+        self.statusbarManager.AddStatusbarItem(sb.SbMask(self, statusbar = statusbar, position = 2))
+        self.statusbarManager.AddStatusbarItem(sb.SbRender(self, statusbar = statusbar, position = 3))
+        
+        self.statusbarManager.Update()
+
+        #
+        # Init map display (buffered DC & set default cursor)
+        #
+        self.MapWindow2D = BufferedWindow(self, id = wx.ID_ANY,
+                                          Map = self.Map, tree = self.tree, lmgr = self._layerManager)
+        # default is 2D display mode
+        self.MapWindow = self.MapWindow2D
+        self.MapWindow.SetCursor(self.cursors["default"])
+        # used by vector digitizer
+        self.MapWindowVDigit = None
+        # used by Nviz (3D display mode)
+        self.MapWindow3D = None 
+
+        #
+        # initialize region values
+        #
+        self._initMap(map = self.Map) 
+
+        #
+        # Bind various events
+        #
+        self.Bind(wx.EVT_ACTIVATE, self.OnFocus)
+        self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
+        self.Bind(EVT_UPDATE_PRGBAR, self.OnUpdateProgress)
+        
+        #
+        # Update fancy gui style
+        #
+        self._mgr.AddPane(self.MapWindow, wx.aui.AuiPaneInfo().CentrePane().
+                          Dockable(False).BestSize((-1,-1)).Name('2d').
+                          CloseButton(False).DestroyOnClose(True).
+                          Layer(0))
+        self._mgr.Update()
+
+        #
+        # Init print module and classes
+        #
+        self.printopt = PrintOptions(self, self.MapWindow)
+        
+        #
+        # Init zoom history
+        #
+        self.MapWindow.ZoomHistory(self.Map.region['n'],
+                                   self.Map.region['s'],
+                                   self.Map.region['e'],
+                                   self.Map.region['w'])
+
+        #
+        # Re-use dialogs
+        #
+        self.dialogs = {}
+        self.dialogs['attributes'] = None
+        self.dialogs['category'] = None
+        self.dialogs['barscale'] = None
+        self.dialogs['legend'] = None
+
+        self.decorationDialog = None # decoration/overlays
+        
+    def GetMapWindow(self):
+        return self.MapWindow
+    
+    def _addToolbarVDigit(self):
+        """!Add vector digitizer toolbar
+        """
+        from vdigit.main import haveVDigit
+        
+        if not haveVDigit:
+            from vdigit import errorMsg
+            msg = _("Unable to start wxGUI vector digitizer.\nDo you want to start "
+                    "TCL/TK digitizer (v.digit) instead?\n\n"
+                    "Details: %s" % errorMsg)
+            
+            self.toolbars['map'].combo.SetValue(_("2D view"))
+            dlg = wx.MessageDialog(parent = self,
+                                   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'].GetName()
+                self._layerManager.goutput.RunCmd(['v.digit', 'map=%s' % mapName],
+                                                  switchPage = False)
+            dlg.Destroy()
+            
+            self.toolbars['map'].combo.SetValue(_("2D view"))
+            return
+        
+        if self._layerManager:
+            log = self._layerManager.goutput
+        else:
+            log = None
+        
+        if not self.MapWindowVDigit:
+            from vdigit.mapwindow import VDigitWindow
+            self.MapWindowVDigit = VDigitWindow(self, id = wx.ID_ANY,
+                                                Map = self.Map, tree = self.tree,
+                                                lmgr = self._layerManager)
+            self.MapWindowVDigit.Show()
+            self._mgr.AddPane(self.MapWindowVDigit, wx.aui.AuiPaneInfo().CentrePane().
+                          Dockable(False).BestSize((-1,-1)).Name('vdigit').
+                          CloseButton(False).DestroyOnClose(True).
+                          Layer(0))
+        
+        self.MapWindow = self.MapWindowVDigit
+        
+        if self._mgr.GetPane('2d').IsShown():
+            self._mgr.GetPane('2d').Hide()
+        elif self._mgr.GetPane('3d').IsShown():
+            self._mgr.GetPane('3d').Hide()
+        self._mgr.GetPane('vdigit').Show()
+        self.toolbars['vdigit'] = VDigitToolbar(parent = self, mapcontent = self.Map,
+                                                layerTree = self.tree,
+                                                log = log)
+        self.MapWindowVDigit.SetToolbar(self.toolbars['vdigit'])
+        
+        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'].GetBestSize())))
+        # 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)
+
+    def AddNviz(self):
+        """!Add 3D view mode window
+        """
+        from nviz.main import haveNviz, GLWindow
+        
+        # check for GLCanvas and OpenGL
+        if not haveNviz:
+            self.toolbars['map'].combo.SetValue(_("2D view"))
+            GError(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))
+            return
+        
+        # disable 3D mode for other displays
+        for page in range(0, self._layerManager.gm_cb.GetPageCount()):
+            if self._layerManager.gm_cb.GetPage(page) != self._layerManager.curr_page:
+                if '3D' in self._layerManager.gm_cb.GetPage(page).maptree.mapdisplay.toolbars['map'].combo.GetString(1):
+                    self._layerManager.gm_cb.GetPage(page).maptree.mapdisplay.toolbars['map'].combo.Delete(1)
+        self.toolbars['map'].Enable2D(False)
+        # add rotate tool to map toolbar
+        self.toolbars['map'].InsertTool((('rotate', NvizIcons['rotate'],
+                                          self.OnRotate, wx.ITEM_CHECK, 7),)) # 7 is position
+        self.toolbars['map'].InsertTool((('flyThrough', NvizIcons['flyThrough'],
+                                          self.OnFlyThrough, wx.ITEM_CHECK, 8),)) 
+        self.toolbars['map'].ChangeToolsDesc(mode2d = False)
+        # update status bar
+        
+        self.statusbarManager.HideStatusbarChoiceItemsByClass(self.statusbarItemsHiddenInNviz)
+        self.statusbarManager.SetMode(0)
+        
+        # erase map window
+        self.MapWindow.EraseMap()
+        
+        self._layerManager.goutput.WriteCmdLog(_("Starting 3D view mode..."),
+                                               switchPage = False)
+        self.SetStatusText(_("Please wait, loading data..."), 0)
+        
+        # create GL window
+        if not self.MapWindow3D:
+            self.MapWindow3D = GLWindow(self, id = wx.ID_ANY,
+                                        Map = self.Map, tree = self.tree, lmgr = self._layerManager)
+            self.MapWindow = self.MapWindow3D
+            self.MapWindow.SetCursor(self.cursors["default"])
+            
+            # add Nviz notebookpage
+            self._layerManager.AddNvizTools()
+            
+            # switch from MapWindow to MapWindowGL
+            self._mgr.GetPane('2d').Hide()
+            self._mgr.AddPane(self.MapWindow3D, wx.aui.AuiPaneInfo().CentrePane().
+                              Dockable(False).BestSize((-1,-1)).Name('3d').
+                              CloseButton(False).DestroyOnClose(True).
+                              Layer(0))
+            
+            self.MapWindow3D.OnPaint(None) # -> LoadData
+            self.MapWindow3D.Show()
+            self.MapWindow3D.ResetViewHistory()            
+            self.MapWindow3D.UpdateView(None)
+        else:
+            self.MapWindow = self.MapWindow3D
+            os.environ['GRASS_REGION'] = self.Map.SetRegion(windres = True)
+            self.MapWindow3D.GetDisplay().Init()
+            del os.environ['GRASS_REGION']
+            
+            # switch from MapWindow to MapWindowGL
+            self._mgr.GetPane('2d').Hide()
+            self._mgr.GetPane('3d').Show()
+            
+            # add Nviz notebookpage
+            self._layerManager.AddNvizTools()
+            self.MapWindow3D.ResetViewHistory()
+            for page in ('view', 'light', 'fringe', 'constant', 'cplane', 'animation'):
+                self._layerManager.nviz.UpdatePage(page)
+                
+        self.MapWindow3D.overlays = self.MapWindow2D.overlays
+        self.MapWindow3D.textdict = self.MapWindow2D.textdict
+        # update overlays needs to be called after because getClientSize
+        # is called during update and it must give reasonable values
+        wx.CallAfter(self.MapWindow3D.UpdateOverlays)
+        
+        self.SetStatusText("", 0)
+        self._mgr.Update()
+    
+    def RemoveNviz(self):
+        """!Restore 2D view"""
+        self.toolbars['map'].RemoveTool(self.toolbars['map'].rotate)
+        self.toolbars['map'].RemoveTool(self.toolbars['map'].flyThrough)
+        # update status bar
+        self.statusbarManager.ShowStatusbarChoiceItemsByClass(self.statusbarItemsHiddenInNviz)
+        self.statusbarManager.SetMode(UserSettings.Get(group = 'display',
+                                                       key = 'statusbarMode',
+                                                       subkey = 'selection'))
+        self.SetStatusText(_("Please wait, unloading data..."), 0)
+        self._layerManager.goutput.WriteCmdLog(_("Switching back to 2D view mode..."),
+                                               switchPage = False)
+        self.MapWindow3D.OnClose(event = None)
+        # switch from MapWindowGL to MapWindow
+        self._mgr.GetPane('2d').Show()
+        self._mgr.GetPane('3d').Hide()
+        
+        self.MapWindow = self.MapWindow2D
+        # remove nviz notebook page
+        self._layerManager.RemoveNvizTools()
+        
+        self.MapWindow2D.overlays = self.MapWindow3D.overlays
+        self.MapWindow2D.textdict = self.MapWindow3D.textdict
+        self.MapWindow.UpdateMap()
+        self._mgr.Update()
+        
+    def AddToolbar(self, name):
+        """!Add defined toolbar to the window
+        
+        Currently known toolbars are:
+         - 'map'     - basic map toolbar
+         - 'vdigit'  - vector digitizer
+         - 'gcpdisp' - GCP Manager Display
+        """
+        # default toolbar
+        if name == "map":
+            self.toolbars['map'] = MapToolbar(self, self.Map)
+            
+            self._mgr.AddPane(self.toolbars['map'],
+                              wx.aui.AuiPaneInfo().
+                              Name("maptoolbar").Caption(_("Map Toolbar")).
+                              ToolbarPane().Top().Name('mapToolbar').
+                              LeftDockable(False).RightDockable(False).
+                              BottomDockable(False).TopDockable(True).
+                              CloseButton(False).Layer(2).
+                              BestSize((self.toolbars['map'].GetBestSize())))
+            
+        # vector digitizer
+        elif name == "vdigit":
+            self._addToolbarVDigit()
+        
+        self._mgr.Update()
+        
+    def RemoveToolbar (self, name):
+        """!Removes defined toolbar from the window
+
+        @todo Only hide, activate by calling AddToolbar()
+        """
+        # cannot hide main toolbar
+        if name == "map":
+            return
+        
+        self._mgr.DetachPane(self.toolbars[name])
+        self.toolbars[name].Destroy()
+        self.toolbars.pop(name)
+        
+        if name == 'vdigit':
+            self._mgr.GetPane('vdigit').Hide()
+            self._mgr.GetPane('2d').Show()
+            self.MapWindow = self.MapWindow2D
+            
+        self.toolbars['map'].combo.SetValue(_("2D view"))
+        self.toolbars['map'].Enable2D(True)
+        
+        self._mgr.Update()
+    
+    def IsPaneShown(self, name):
+        """!Check if pane (toolbar, mapWindow ...) of given name is currently shown"""
+        if self._mgr.GetPane(name).IsOk():
+            return self._mgr.GetPane(name).IsShown()
+        return False
+    
+    def OnUpdateProgress(self, event):
+        """!Update progress bar info
+        """
+        self.GetProgressBar().SetValue(event.value)
+        
+        event.Skip()
+        
+    def OnFocus(self, event):
+        """!Change choicebook page to match display.
+        """
+        # change bookcontrol page to page associated with display
+        if self.page:
+            pgnum = self.layerbook.GetPageIndex(self.page)
+            if pgnum > -1:
+                self.layerbook.SetSelection(pgnum)
+                self._layerManager.curr_page = self.layerbook.GetCurrentPage()
+        
+        event.Skip()
+        
+    def OnRender(self, event):
+        """!Re-render map composition (each map layer)
+        """
+        # delete tmp map layers (queries)
+        qlayer = self.Map.GetListOfLayers(l_name = globalvar.QUERYLAYER)
+        for layer in qlayer:
+            self.Map.DeleteLayer(layer)
+        
+        # delete tmp lines
+        if self.MapWindow.mouse["use"] in ("measure",
+                                           "profile"):
+            self.MapWindow.polycoords = []
+            self.MapWindow.ClearLines()
+        
+        # deselect features in vdigit
+        if self.GetToolbar('vdigit'):
+            if self.MapWindow.digit:
+                self.MapWindow.digit.GetDisplay().SetSelected([])
+            self.MapWindow.UpdateMap(render = True, renderVector = True)
+        else:
+            self.MapWindow.UpdateMap(render = True)
+        
+        # update statusbar
+        self.StatusbarUpdate()
+
+    def OnPointer(self, event):
+        """!Pointer button clicked
+        """
+        if self.GetMapToolbar():
+            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.GetToolbar('vdigit'):
+            # digitization tool activated
+            self.MapWindow.SetCursor(self.cursors["cross"])
+
+            # reset mouse['box'] if needed
+            if self.toolbars['vdigit'].GetAction() in ['addLine']:
+                if self.toolbars['vdigit'].GetAction('type') in ['point', 'centroid']:
+                    self.MapWindow.mouse['box'] = 'point'
+                else: # line, boundary
+                    self.MapWindow.mouse['box'] = 'line'
+            elif self.toolbars['vdigit'].GetAction() in ['addVertex', 'removeVertex', 'splitLine',
+                                                         'editLine', 'displayCats', 'queryMap',
+                                                         'copyCats']:
+                self.MapWindow.mouse['box'] = 'point'
+            else: # moveLine, deleteLine
+                self.MapWindow.mouse['box'] = 'box'
+        
+        else:
+            self.MapWindow.SetCursor(self.cursors["default"])
+            
+    def OnRotate(self, event):
+        """!Rotate 3D view
+        """
+        if self.GetMapToolbar():
+            self.toolbars['map'].OnTool(event)
+            self.toolbars['map'].action['desc'] = ''
+        
+        self.MapWindow.mouse['use'] = "rotate"
+        
+        # change the cursor
+        self.MapWindow.SetCursor(self.cursors["hand"])
+        
+    def OnFlyThrough(self, event):
+        """!Fly-through mode
+        """
+        if self.toolbars['map']:
+            self.toolbars['map'].OnTool(event)
+            self.toolbars['map'].action['desc'] = ''
+        
+        self.MapWindow.mouse['use'] = "fly"
+        
+        # change the cursor
+        self.MapWindow.SetCursor(self.cursors["hand"])
+        self.MapWindow.SetFocus()
+        
+    def OnZoomRegion(self, event):
+        """
+        Zoom to region
+        """
+        self.Map.getRegion()
+        self.Map.getResolution()
+        self.UpdateMap()
+        # event.Skip()
+
+    def OnAlignRegion(self, event):
+        """
+        Align region
+        """
+        if not self.Map.alignRegion:
+            self.Map.alignRegion = True
+        else:
+            self.Map.alignRegion = False
+        # event.Skip()        
+        
+    def SaveToFile(self, event):
+        """!Save map to image
+        """
+        if self.IsPaneShown('3d'):
+            filetype = "PPM file (*.ppm)|*.ppm|TIF file (*.tif)|*.tif"
+            ltype = [{ 'ext' : 'ppm', 'type' : 'ppm' },
+                     { 'ext' : 'tif', 'type' : 'tif' }]
+        else:
+            img = self.MapWindow.img
+            if not img:
+                GMessage(parent = self,
+                         message = _("Nothing to render (empty map). Operation canceled."))
+                return
+            filetype, ltype = GetImageHandlers(img)
+        
+        # get size
+        dlg = 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:
+            path = dlg.GetPath()
+            if not path:
+                dlg.Destroy()
+                return
+            
+            base, ext = os.path.splitext(path)
+            fileType = ltype[dlg.GetFilterIndex()]['type']
+            extType  = ltype[dlg.GetFilterIndex()]['ext']
+            if ext != extType:
+                path = base + '.' + extType
+            
+            self.MapWindow.SaveToFile(path, fileType,
+                                      width, height)
+            
+        dlg.Destroy()
+
+    def PrintMenu(self, event):
+        """
+        Print options and output menu for map display
+        """
+        point = wx.GetMousePosition()
+        printmenu = wx.Menu()
+        # Add items to the menu
+        setup = wx.MenuItem(printmenu, wx.ID_ANY, _('Page setup'))
+        printmenu.AppendItem(setup)
+        self.Bind(wx.EVT_MENU, self.printopt.OnPageSetup, setup)
+
+        preview = wx.MenuItem(printmenu, wx.ID_ANY, _('Print preview'))
+        printmenu.AppendItem(preview)
+        self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
+
+        doprint = wx.MenuItem(printmenu, wx.ID_ANY, _('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 OnCloseWindow(self, event):
+        """!Window closed.
+        Also close associated layer tree page
+        """
+        pgnum = None
+        self.Map.Clean()
+        
+        # close edited map and 3D tools properly
+        if self.GetToolbar('vdigit'):
+            maplayer = self.toolbars['vdigit'].GetLayer()
+            if maplayer:
+                self.toolbars['vdigit'].OnExit()
+        if self.IsPaneShown('3d'):
+            self.RemoveNviz()
+        
+        if not self._layerManager:
+            self.Destroy()
+        elif self.page:
+            pgnum = self.layerbook.GetPageIndex(self.page)
+            if pgnum > -1:
+                self.layerbook.DeletePage(pgnum)
+        
+    def QueryMap(self, x, y):
+        """!Query raster or vector map layers by r/v.what
+        
+        @param x,y coordinates
+        """
+        # set query snap distance for v.what at map unit 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))
+        
+        if not self.IsStandalone():
+            num = 0
+            for layer in self.tree.GetSelections():
+                ltype = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
+                if ltype in ('raster', 'rgb', 'his',
+                             'vector', 'thememap', 'themechart'):
+                    num += 1
+            
+            if num < 1:
+                GMessage(parent = self,
+                         message = _('No raster or vector map layer selected for querying.'))
+                return
+        
+        rast = list()
+        vect = list()
+        rcmd = ['r.what', '--v']
+        vcmd = ['v.what', '--v']
+        
+        if self.IsStandalone():
+            pass
+        else:
+            for layer in self.tree.GetSelections():
+                ltype = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
+                dcmd = self.tree.GetPyData(layer)[0]['cmd']
+                name, found = GetLayerNameFromCmd(dcmd)
+                
+                if not found:
+                    continue
+                if ltype == 'raster':
+                    rast.append(name)
+                elif ltype in ('rgb', 'his'):
+                    for iname in name.split('\n'):
+                        rast.append(iname)
+                elif ltype in ('vector', 'thememap', 'themechart'):
+                    vect.append(name)
+        # rasters are not queried this way in 3D, we don't want them now
+        if self.IsPaneShown('3d'):
+            rast = list()
+        # 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 rast:
+            rcmd.append('-f')
+            rcmd.append('-n')
+            rcmd.append('input=%s' % ','.join(rast))
+            rcmd.append('east_north=%f,%f' % (float(east), float(north)))
+        
+        if vect:
+            # check for vector maps open to be edited
+            digitToolbar = self.toolbars['vdigit']
+            if digitToolbar:
+                lmap = digitToolbar.GetLayer().GetName()
+                for name in vect:
+                    if lmap == name:
+                        self._layerManager.goutput.WriteWarning(_("Vector map <%s> "
+                                                                  "opened for editing - skipped.") % map)
+                        vect.remove(name)
+            
+            if len(vect) < 1:
+                self._layerManager.goutput.WriteCmdLog(_("Nothing to query."))
+                return
+            
+            vcmd.append('-a')
+            vcmd.append('map=%s' % ','.join(vect))
+            vcmd.append('east_north=%f,%f' % (float(east), float(north)))
+            vcmd.append('distance=%f' % float(qdist))
+        
+        Debug.msg(1, "QueryMap(): raster=%s vector=%s" % (','.join(rast),
+                                                          ','.join(vect)))
+        # parse query command(s)
+        if not self.IsStandalone():
+            if rast:
+                self._layerManager.goutput.RunCmd(rcmd,
+                                                  compReg = False,
+                                                  onDone  =  self._QueryMapDone)
+            if vect:
+                self._layerManager.goutput.RunCmd(vcmd,
+                                                  onDone = self._QueryMapDone)
+        else:
+            if rast:
+                RunCommand(rcmd)
+            if vect:
+                RunCommand(vcmd)
+        
+    def _QueryMapDone(self, cmd, 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 'GRASS_REGION' in os.environ:
+                del os.environ["GRASS_REGION"]
+        elif 'GRASS_REGION' in os.environ:
+            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':
+            GMessage(parent = self,
+                     message = _("No map layer selected for querying."))
+            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.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].GetMapset() != \
+                grass.gisenv()['MAPSET']:
+            mode = 'display'
+        else:
+            mode = 'update'
+        
+        if self.dialogs['attributes'] is None:
+            dlg = DisplayAttributesDialog(parent = self.MapWindow,
+                                          map = mapName,
+                                          query = ((east, north), qdist),
+                                          pos = posWindow,
+                                          action = mode)
+            self.dialogs['attributes'] = dlg
+        
+        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),
+                                                        action = mode)
+            else:
+                self.dialogs['attributes'].UpdateDialog(query = ((east, north), qdist),
+                                                        action = mode)
+        if not self.dialogs['attributes'].IsFound():
+            self._layerManager.goutput.WriteLog(_('Nothing found.'))
+        
+        cats = self.dialogs['attributes'].GetCats()
+        
+        qlayer = None
+        if not self.IsPaneShown('3d'):
+            try:
+                qlayer = self.Map.GetListOfLayers(l_name = globalvar.QUERYLAYER)[0]
+            except IndexError:
+                pass
+        
+        if self.dialogs['attributes'].mapDBInfo and cats:
+            if not self.IsPaneShown('3d'):
+                # 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.GetMapToolbar():
+            self.toolbars['map'].OnTool(event)
+            action = self.toolbars['map'].GetAction()
+            
+        self.toolbars['map'].action['desc'] = 'queryMap'
+        self.MapWindow.mouse['use'] = "query"
+        
+        if not self.IsStandalone():
+            # switch to output console to show query results
+            self._layerManager.notebook.SetSelectionByName('output')
+        
+        self.MapWindow.mouse['box'] = "point"
+        self.MapWindow.zoomtype = 0
+        
+        # change the cursor
+        self.MapWindow.SetCursor(self.cursors["cross"])
+        
+    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" % ListOfCatsToRange(lcats))
+        
+        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 OnMeasure(self, event):
+        """!Init measurement routine that calculates map distance
+        along transect drawn on map display
+        """
+        self.totaldist = 0.0 # total measured distance
+        
+        # switch Layer Manager to output console to show measure results
+        self._layerManager.notebook.SetSelectionByName('output')
+        
+        # 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']
+            self._layerManager.goutput.WriteCmdLog(_('Measuring distance') + ' ('
+                                                   + units + '):')
+        else:
+            self._layerManager.goutput.WriteCmdLog(_('Measuring distance:'))
+        
+        if self.Map.projinfo['proj'] == 'll':
+            try:
+                import grass.lib.gis as gislib
+                global haveCtypes
+                haveCtypes = True
+
+                gislib.G_begin_distance_calculations()
+            except ImportError, e:
+                self._layerManager.goutput.WriteWarning(_('Geodesic distance is not yet '
+                                                          'supported by this tool.\n'
+                                                          'Reason: %s' % e))
+        
+    def MeasureDist(self, beginpt, endpt):
+        """!Calculate map distance from screen distance
+        and print to output window
+        """
+        self._layerManager.notebook.SetSelectionByName('output')
+        
+        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 = '%s = %s %s\n%s = %s %s\n%s = %d %s\n%s' \
+                % (_('segment'), strdist, dunits,
+                   _('total distance'), strtotdist, tdunits,
+                   _('bearing'), angle, _('deg'),
+                   '-' * 60)
+        else:
+            mstring = '%s = %s %s\n%s = %s %s\n%s' \
+                % (_('segment'), strdist, dunits,
+                   _('total distance'), strtotdist, tdunits,
+                   '-' * 60)
+        
+        self._layerManager.goutput.WriteLog(mstring)
+        
+        return dist
+
+    def OnProfile(self, event):
+        """!Launch profile tool
+        """
+        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)
+
+        win = ProfileFrame(parent = self, rasterList = raster)
+        
+        win.CentreOnParent()
+        win.Show()
+        # Open raster select dialog to make sure that a raster (and
+        # the desired raster) is selected to be profiled
+        win.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
+        Grass Development Team 2006"""
+        
+        mapunits = self.Map.projinfo['units']
+        if mapunits == 'metres':
+            mapunits = 'meters'
+        outunits = mapunits
+        dist = float(dist)
+        divisor = 1.0
+        
+        # figure out which units to use
+        if mapunits == 'meters':
+            if dist > 2500.0:
+                outunits = 'km'
+                divisor = 1000.0
+            else: outunits = 'm'
+        elif mapunits == 'feet':
+            # nano-bug: we match any "feet", but US Survey feet is really
+            #  5279.9894 per statute mile, or 10.6' per 1000 miles. As >1000
+            #  miles the tick markers are rounded to the nearest 10th of a
+            #  mile (528'), the difference in foot flavours is ignored.
+            if dist > 5280.0:
+                outunits = 'miles'
+                divisor = 5280.0
+            else:
+                outunits = 'ft'
+        elif 'degree' in mapunits and \
+                not haveCtypes:
+            if dist < 1:
+                outunits = 'min'
+                divisor = (1/60.0)
+            else:
+                outunits = 'deg'
+        else:
+            outunits = 'meters'
+        
+        # format numbers in a nice way
+        if (dist/divisor) >= 2500.0:
+            outdist = round(dist/divisor)
+        elif (dist/divisor) >= 1000.0:
+            outdist = round(dist/divisor,1)
+        elif (dist/divisor) > 0.0:
+            outdist = round(dist/divisor,int(math.ceil(3-math.log10(dist/divisor))))
+        else:
+            outdist = float(dist/divisor)
+        
+        return (outdist, outunits)
+    
+    def OnHistogram(self, event):
+        """!Init histogram display canvas and tools
+        """
+        win = HistogramFrame(self)
+        
+        win.CentreOnParent()
+        win.Show()
+        win.Refresh()
+        win.Update()
+        
+    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'] = \
+            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'] = \
+            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
+            self.MapWindow.dragid = -1
+        else:
+            # index for overlay layer in render
+            if len(self.MapWindow.textdict.keys()) > 0:
+                id = max(self.MapWindow.textdict.keys()) + 1
+            else:
+                id = 101
+        
+        self.dialogs['text'] = 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']
+        
+            # delete object if it has no text or is not active
+            if text == '' or active == False:
+                try:
+                    self.MapWindow2D.pdc.ClearId(id)
+                    self.MapWindow2D.pdc.RemoveId(id)
+                    del self.MapWindow.textdict[id]
+                    if self.IsPaneShown('3d'):
+                        self.MapWindow3D.UpdateOverlays()
+                        self.MapWindow.UpdateMap()
+                    else:
+                        self.MapWindow2D.UpdateMap(render = False, renderVector = False)
+                except:
+                    pass
+                return
+
+            
+            self.MapWindow.textdict[id] = self.dialogs['text'].GetValues()
+            
+            if self.IsPaneShown('3d'):
+                self.MapWindow3D.UpdateOverlays()
+                self.MapWindow3D.UpdateMap()
+            else:
+                self.MapWindow2D.pdc.ClearId(id)
+                self.MapWindow2D.pdc.SetId(id)
+                self.MapWindow2D.UpdateMap(render = False, renderVector = False)
+            
+        self.MapWindow.mouse['use'] = 'pointer'
+    
+    def OnAddArrow(self, event):
+        """!Handler for north arrow menu selection.
+            Opens Appearance page of nviz notebook.
+        """
+        
+        self._layerManager.nviz.SetPage('decoration')
+        self.MapWindow3D.SetDrawArrow((70, 70))
+        
+    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.
+        """
+        self.MapWindow.ZoomToMap()
+
+    def OnZoomToRaster(self, event):
+        """!Set display extents to match selected raster map (ignore NULLs)
+        """
+        self.MapWindow.ZoomToMap(ignoreNulls = True)
+        
+    def OnZoomToSaved(self, event):
+        """!Set display geometry to match extents in
+        saved region file
+        """
+        self.MapWindow.ZoomToSaved()
+        
+    def OnDisplayToWind(self, event):
+        """!Set computational region (WIND file) to match display
+        extents
+        """
+        self.MapWindow.DisplayToWind()
+ 
+    def SaveDisplayRegion(self, event):
+        """!Save display extents to named region file.
+        """
+        self.MapWindow.SaveDisplayRegion()
+        
+    def OnZoomMenu(self, event):
+        """!Popup Zoom menu
+        """
+        point = wx.GetMousePosition()
+        zoommenu = wx.Menu()
+        # Add items to the menu
+
+        zoomwind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to computational region (set with g.region)'))
+        zoommenu.AppendItem(zoomwind)
+        self.Bind(wx.EVT_MENU, self.OnZoomToWind, zoomwind)
+
+        zoomdefault = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to default region'))
+        zoommenu.AppendItem(zoomdefault)
+        self.Bind(wx.EVT_MENU, self.OnZoomToDefault, zoomdefault)
+
+        zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to saved region'))
+        zoommenu.AppendItem(zoomsaved)
+        self.Bind(wx.EVT_MENU, self.OnZoomToSaved, zoomsaved)
+
+        savewind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Set computational region from display extent'))
+        zoommenu.AppendItem(savewind)
+        self.Bind(wx.EVT_MENU, self.OnDisplayToWind, savewind)
+
+        savezoom = wx.MenuItem(zoommenu, wx.ID_ANY, _('Save display geometry to named region'))
+        zoommenu.AppendItem(savezoom)
+        self.Bind(wx.EVT_MENU, self.SaveDisplayRegion, savezoom)
+
+        # Popup the menu. If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.PopupMenu(zoommenu)
+        zoommenu.Destroy()
+
+    def SetProperties(self, render = False, mode = 0, showCompExtent = False,
+                      constrainRes = False, projection = False, alignExtent = True):
+        """!Set properies of map display window"""
+        self.SetProperty('render', render)
+        self.statusbarManager.SetMode(mode)
+        self.StatusbarUpdate()
+        self.SetProperty('region', showCompExtent)
+        self.SetProperty('alignExtent', alignExtent)
+        self.SetProperty('resolution', constrainRes)
+        self.SetProperty('projection', projection)
+        
+    def IsStandalone(self):
+        """!Check if Map display is standalone"""
+        if self._layerManager:
+            return False
+        
+        return True
+    
+    def GetLayerManager(self):
+        """!Get reference to Layer Manager
+
+        @return window reference
+        @return None (if standalone)
+        """
+        return self._layerManager
+    
+    def GetMapToolbar(self):
+        """!Returns toolbar with zooming tools"""
+        return self.toolbars['map']


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/gprint.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/gprint.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/gprint.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,289 @@
+"""!
+ at package mapdisp.gprint
+
+ at brief Print context and utility functions for printing
+contents of map display window.
+
+Classes:
+ - gprint::MapPrint
+ - gprint::PrintOptions
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton (Arizona State University)
+"""
+
+import  wx
+
+from core.gcmd import GMessage
+
+class MapPrint(wx.Printout):
+    def __init__(self, canvas):
+        wx.Printout.__init__(self)
+        self.canvas = canvas
+
+    def OnBeginDocument(self, start, end):
+        return super(MapPrint, self).OnBeginDocument(start, end)
+
+    def OnEndDocument(self):
+        super(MapPrint, self).OnEndDocument()
+
+    def OnBeginPrinting(self):
+        super(MapPrint, self).OnBeginPrinting()
+
+    def OnEndPrinting(self):
+        super(MapPrint, self).OnEndPrinting()
+
+    def OnPreparePrinting(self):
+        super(MapPrint, self).OnPreparePrinting()
+
+    def HasPage(self, page):
+        if page <= 2:
+            return True
+        else:
+            return False
+
+    def GetPageInfo(self):
+        return (1, 2, 1, 2)
+
+    def OnPrintPage(self, page):
+        dc = self.GetDC()
+
+        #-------------------------------------------
+        # One possible method of setting scaling factors...
+        maxX, maxY = self.canvas.GetSize()
+
+        # Let's have at least 50 device units margin
+        marginX = 10
+        marginY = 10
+
+        # Add the margin to the graphic size
+        maxX = maxX + (2 * marginX)
+        maxY = maxY + (2 * marginY)
+
+        # Get the size of the DC in pixels
+        (w, h) = dc.GetSizeTuple()
+
+        # Calculate a suitable scaling factor
+        scaleX = float(w) / maxX
+        scaleY = float(h) / maxY
+
+        # Use x or y scaling factor, whichever fits on the DC
+        actualScale = min(scaleX, scaleY)
+
+        # Calculate the position on the DC for centering the graphic
+        posX = (w - (self.canvas.GetSize()[0] * actualScale)) / 2.0
+        posY = (h - (self.canvas.GetSize()[1] * actualScale)) / 2.0
+
+        # Set the scale and origin
+        dc.SetUserScale(actualScale, actualScale)
+        dc.SetDeviceOrigin(int(posX), int(posY))
+
+        #-------------------------------------------
+
+        self.canvas.pdc.DrawToDC(dc)
+
+        # prints a page number on the page
+#        dc.DrawText("Page: %d" % page, marginX/2, maxY-marginY)
+
+        return True
+
+class PrintOptions:
+    def __init__(self, parent, mapwin):
+        self.mapframe = parent
+        self.mapwin = mapwin
+	#self.frame = frame
+
+	self.printData = None
+
+	#self.canvas = ScrolledWindow.MyCanvas(self)
+
+    def setup(self):
+	if self.printData:
+	    return
+        self.printData = wx.PrintData()
+        self.printData.SetPaperId(wx.PAPER_LETTER)
+        self.printData.SetPrintMode(wx.PRINT_MODE_PRINTER)
+
+    def OnPageSetup(self, event):
+	self.setup()
+        psdd = wx.PageSetupDialogData(self.printData)
+        psdd.CalculatePaperSizeFromId()
+        dlg = wx.PageSetupDialog(self.mapwin, psdd)
+        dlg.ShowModal()
+
+        # this makes a copy of the wx.PrintData instead of just saving
+        # a reference to the one inside the PrintDialogData that will
+        # be destroyed when the dialog is destroyed
+        self.printData = wx.PrintData( dlg.GetPageSetupData().GetPrintData() )
+
+        dlg.Destroy()
+
+    def OnPrintPreview(self, event):
+	self.setup()
+        data = wx.PrintDialogData(self.printData)
+        printout = MapPrint(self.mapwin)
+        printout2 = MapPrint(self.mapwin)
+        self.preview = wx.PrintPreview(printout, printout2, data)
+
+        if not self.preview.Ok():
+            wx.MessageBox("There was a problem printing this display\n", wx.OK)
+            return
+
+        pfrm = wx.PreviewFrame(self.preview, self.mapframe, "Print preview")
+
+        pfrm.Initialize()
+        pfrm.SetPosition(self.mapframe.GetPosition())
+        pfrm.SetSize(self.mapframe.GetClientSize())
+        pfrm.Show(True)
+
+    def OnDoPrint(self, event):
+	self.setup()
+        pdd = wx.PrintDialogData(self.printData)
+        # set number of pages/copies
+        pdd.SetToPage(1)
+        printer = wx.Printer(pdd)
+        printout = MapPrint(self.mapwin)
+
+        if not printer.Print(self.mapframe, printout, True):
+            wx.MessageBox("There was a problem printing.\nPerhaps your current printer is not set correctly?", "Printing", wx.OK)
+        else:
+            self.printData = wx.PrintData( printer.GetPrintDialogData().GetPrintData() )
+        printout.Destroy()
+class MapPrint(wx.Printout):
+    def __init__(self, canvas):
+        wx.Printout.__init__(self)
+        self.canvas = canvas
+
+    def OnBeginDocument(self, start, end):
+        return super(MapPrint, self).OnBeginDocument(start, end)
+
+    def OnEndDocument(self):
+        super(MapPrint, self).OnEndDocument()
+
+    def OnBeginPrinting(self):
+        super(MapPrint, self).OnBeginPrinting()
+
+    def OnEndPrinting(self):
+        super(MapPrint, self).OnEndPrinting()
+
+    def OnPreparePrinting(self):
+        super(MapPrint, self).OnPreparePrinting()
+
+    def HasPage(self, page):
+        if page <= 2:
+            return True
+        else:
+            return False
+
+    def GetPageInfo(self):
+        return (1, 2, 1, 2)
+
+    def OnPrintPage(self, page):
+        dc = self.GetDC()
+
+        #-------------------------------------------
+        # One possible method of setting scaling factors...
+        maxX, maxY = self.canvas.GetSize()
+
+        # Let's have at least 50 device units margin
+        marginX = 10
+        marginY = 10
+
+        # Add the margin to the graphic size
+        maxX = maxX + (2 * marginX)
+        maxY = maxY + (2 * marginY)
+
+        # Get the size of the DC in pixels
+        (w, h) = dc.GetSizeTuple()
+
+        # Calculate a suitable scaling factor
+        scaleX = float(w) / maxX
+        scaleY = float(h) / maxY
+
+        # Use x or y scaling factor, whichever fits on the DC
+        actualScale = min(scaleX, scaleY)
+
+        # Calculate the position on the DC for centering the graphic
+        posX = (w - (self.canvas.GetSize()[0] * actualScale)) / 2.0
+        posY = (h - (self.canvas.GetSize()[1] * actualScale)) / 2.0
+
+        # Set the scale and origin
+        dc.SetUserScale(actualScale, actualScale)
+        dc.SetDeviceOrigin(int(posX), int(posY))
+
+        #-------------------------------------------
+
+        self.canvas.pdc.DrawToDC(dc)
+
+        # prints a page number on the page
+        # dc.DrawText("Page: %d" % page, marginX/2, maxY-marginY)
+
+        return True
+
+class PrintOptions(wx.Object):
+    def __init__(self, parent, mapwin):
+        self.mapframe = parent
+        self.mapwin = mapwin
+	#self.frame = frame
+
+	self.printData = None
+
+	#self.canvas = ScrolledWindow.MyCanvas(self)
+
+    def setup(self):
+	if self.printData:
+	    return
+        self.printData = wx.PrintData()
+        self.printData.SetPaperId(wx.PAPER_LETTER)
+        self.printData.SetPrintMode(wx.PRINT_MODE_PRINTER)
+
+    def OnPageSetup(self, event):
+	self.setup()
+        psdd = wx.PageSetupDialogData(self.printData)
+        psdd.CalculatePaperSizeFromId()
+        dlg = wx.PageSetupDialog(self.mapwin, psdd)
+        dlg.ShowModal()
+
+        # this makes a copy of the wx.PrintData instead of just saving
+        # a reference to the one inside the PrintDialogData that will
+        # be destroyed when the dialog is destroyed
+        self.printData = wx.PrintData( dlg.GetPageSetupData().GetPrintData() )
+
+        dlg.Destroy()
+
+    def OnPrintPreview(self, event):
+	self.setup()
+        data = wx.PrintDialogData(self.printData)
+        printout = MapPrint(self.mapwin)
+        printout2 = MapPrint(self.mapwin)
+        self.preview = wx.PrintPreview(printout, printout2, data)
+
+        if not self.preview.Ok():
+            wx.MessageBox("There was a problem printing this display\n", wx.OK)
+            return
+
+        pfrm = wx.PreviewFrame(self.preview, self.mapframe, "Print preview")
+
+        pfrm.Initialize()
+        pfrm.SetPosition(self.mapframe.GetPosition())
+        pfrm.SetSize(self.mapframe.GetClientSize())
+        pfrm.Show(True)
+
+    def OnDoPrint(self, event):
+	self.setup()
+        pdd = wx.PrintDialogData(self.printData)
+        # set number of pages/copies
+        pdd.SetToPage(1)
+        printer = wx.Printer(pdd)
+        printout = MapPrint(self.mapwin)
+
+        if not printer.Print(self.mapframe, printout, True):
+            GMessage(_("There was a problem printing.\n"
+                       "Perhaps your current printer is not set correctly?"))
+        else:
+            self.printData = wx.PrintData( printer.GetPrintDialogData().GetPrintData() )
+        printout.Destroy()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/main.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/main.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/main.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,142 @@
+"""!
+ at package mapdisp.main
+
+ at brief Start Map Display as standalone application
+
+Classes:
+ - mapdisp::MapApp
+
+Usage:
+python mapdisp/main.py monitor-identifier /path/to/map/file /path/to/command/file /path/to/env/file
+
+(C) 2006-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+ at author Vaclav Petras <wenzeslaus gmail.com> (MapFrameBase)
+ at author Anna Kratochvilova <kratochanna gmail.com> (MapFrameBase)
+"""
+
+import os
+import sys
+
+if __name__ == "__main__":
+    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython'))
+from core          import globalvar
+import wx
+
+from core.gcmd     import RunCommand
+from core.render   import Map
+from mapdisp.frame import MapFrame
+from grass.script  import core as grass
+
+# for standalone app
+monFile = { 'cmd' : None,
+            'map' : None,
+            'env' : None,
+            }
+monName = None
+monSize = list(globalvar.MAP_WINDOW_SIZE)
+
+
+class MapApp(wx.App):
+    def OnInit(self):
+        wx.InitAllImageHandlers()
+        if __name__ == "__main__":
+            self.cmdTimeStamp = os.path.getmtime(monFile['cmd'])
+            self.Map = Map(cmdfile = monFile['cmd'], mapfile = monFile['map'],
+                           envfile = monFile['env'], monitor = monName)
+        else:
+            self.Map = None
+
+        self.mapFrm = MapFrame(parent = None, id = wx.ID_ANY, Map = self.Map,
+                               size = monSize)
+        # self.SetTopWindow(Map)
+        self.mapFrm.Show()
+        
+        if __name__ == "__main__":
+            self.timer = wx.PyTimer(self.watcher)
+            #check each 0.5s
+            global mtime
+            mtime = 500
+            self.timer.Start(mtime)
+            
+        return True
+    
+    def OnExit(self):
+        if __name__ == "__main__":
+            # stop the timer
+            # self.timer.Stop()
+            # terminate thread
+            for f in monFile.itervalues():
+                grass.try_remove(f)
+            
+    def watcher(self):
+        """!Redraw, if new layer appears (check's timestamp of
+        cmdfile)
+        """
+        # todo: events
+        if os.path.getmtime(monFile['cmd']) > self.cmdTimeStamp:
+            self.timer.Stop()
+            self.cmdTimeStamp = os.path.getmtime(monFile['cmd'])
+            self.mapFrm.OnDraw(None)
+            self.mapFrm.GetMap().GetLayersFromCmdFile()
+            self.timer.Start(mtime)
+
+if __name__ == "__main__":
+    # set command variable
+    if len(sys.argv) < 5:
+        print __doc__
+        sys.exit(1)
+    
+    monName = sys.argv[1]
+    monFile = { 'map' : sys.argv[2],
+                'cmd' : sys.argv[3],
+                'env' : sys.argv[4],
+                }
+    if len(sys.argv) >= 6:
+        try:
+            monSize[0] = int(sys.argv[5])
+        except ValueError:
+            pass
+    
+    if len(sys.argv) == 7:
+        try:
+            monSize[1] = int(sys.argv[6])
+        except ValueError:
+            pass
+
+    import gettext
+    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+    
+    grass.verbose(_("Starting map display <%s>...") % (monName))
+
+    RunCommand('g.gisenv',
+               set = 'MONITOR_%s_PID=%d' % (monName, os.getpid()))
+    
+    gmMap = MapApp(0)
+    # set title
+    gmMap.mapFrm.SetTitle(_("GRASS GIS Map Display: " +
+                            monName + 
+                            " - Location: " + grass.gisenv()["LOCATION_NAME"]))
+    
+    gmMap.MainLoop()
+    
+    grass.verbose(_("Stopping map display <%s>...") % (monName))
+
+    # clean up GRASS env variables
+    env = grass.gisenv()
+    env_name = 'MONITOR_%s' % monName
+    for key in env.keys():
+        if key.find(env_name) == 0:
+            RunCommand('g.gisenv',
+                       set = '%s=' % key)
+        if key == 'MONITOR' and env[key] == monName:
+            RunCommand('g.gisenv',
+                       set = '%s=' % key)
+    
+    sys.exit(0)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/mapwindow.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/mapwindow.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/mapwindow.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1721 @@
+"""!
+ at package mapdisp.mapwindow
+
+ at brief Map display canvas - buffered window.
+
+Classes:
+ - mapwindow::BufferedWindow
+
+(C) 2006-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+ at author Michael Barton
+ at author Jachym Cepicky
+"""
+
+import os
+import time
+import math
+import sys
+
+import wx
+
+import grass.script as grass
+
+from gui_core.dialogs   import SavedRegion
+from core.gcmd          import RunCommand, GException, GError
+from core.debug         import Debug
+from core.settings      import UserSettings
+from gui_core.mapwindow import MapWindow
+try:
+    import grass.lib.gis as gislib
+    haveCtypes = True
+except ImportError:
+    haveCtypes = False
+
+class BufferedWindow(MapWindow, wx.Window):
+    """!A Buffered window class (2D view mode)
+
+    Superclass for VDigitWindow (vector digitizer).
+    
+    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() method.
+    """
+    def __init__(self, parent, id = wx.ID_ANY,
+                 Map = None, tree = None, lmgr = None,
+                 style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
+        MapWindow.__init__(self, parent, id, Map, tree, lmgr, **kwargs)
+        wx.Window.__init__(self, parent, id, style = style, **kwargs)
+        
+        # flags
+        self.resize = False # indicates whether or not a resize event has taken place
+        self.dragimg = None # initialize variable for map panning
+        
+        # variables for drawing on DC
+        self.pen = None      # pen for drawing zoom boxes, etc.
+        self.polypen = None  # pen for drawing polylines (measurements, profiles, etc)
+        # List of wx.Point tuples defining a polyline (geographical coordinates)
+        self.polycoords = []
+        # ID of rubber band line
+        self.lineid = None
+        # ID of poly line resulting from cumulative rubber band lines (e.g. measurement)
+        self.plineid = None
+        
+        # event bindings
+        self.Bind(wx.EVT_PAINT,        self.OnPaint)
+        self.Bind(wx.EVT_SIZE,         self.OnSize)
+        self.Bind(wx.EVT_IDLE,         self.OnIdle)
+        self._bindMouseEvents()
+        
+        self.processMouse = True
+        
+        # render output objects
+        self.mapfile = None   # image file to be rendered
+        self.img     = None   # wx.Image object (self.mapfile)
+        # decoration overlays
+        self.overlays = {}
+        # images and their PseudoDC ID's for painting and dragging
+        self.imagedict = {}   
+        self.select = {}      # selecting/unselecting decorations for dragging
+        self.textdict = {}    # text, font, and color indexed by id
+        self.currtxtid = None # PseudoDC id for currently selected text
+        
+        # zoom objects
+        self.zoomhistory  = [] # list of past zoom extents
+        self.currzoom     = 0  # current set of extents in zoom history being used
+        self.zoomtype     = 1  # 1 zoom in, 0 no zoom, -1 zoom out
+        self.hitradius    = 10 # distance for selecting map decorations
+        self.dialogOffset = 5  # offset for dialog (e.g. DisplayAttributesDialog)
+        
+        # OnSize called to make sure the buffer is initialized.
+        # This might result in OnSize getting called twice on some
+        # platforms at initialization, but little harm done.
+        ### self.OnSize(None)
+        
+        self._definePseudoDC()
+        # redraw all pdc's, pdcTmp layer is redrawn always (speed issue)
+        self.redrawAll = True
+        
+        # will store an off screen empty bitmap for saving to file
+        self._buffer = None
+        
+        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None)
+        
+        # vars for handling mouse clicks
+        self.dragid   = -1
+        self.lastpos  = (0, 0)
+        
+    def _definePseudoDC(self):
+        """!Define PseudoDC objects to use
+        """
+        # 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()
+        
+    def _bindMouseEvents(self):
+        self.Bind(wx.EVT_MOUSE_EVENTS, self.MouseActions)
+        self.Bind(wx.EVT_MOTION,       self.OnMotion)
+        
+    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)
+        
+        Debug.msg (5, "BufferedWindow.Draw(): id=%s, pdctype = %s, coord=%s" % \
+                       (drawid, pdctype, coords))
+        
+        # set PseudoDC id
+        if drawid is not None:
+            pdc.SetId(drawid)
+            
+        if pdctype == 'clear': # erase the display
+            bg = wx.WHITE_BRUSH
+            # bg = wx.Brush(self.GetBackgroundColour())
+            pdc.SetBackground(bg)
+            pdc.RemoveAll()
+            pdc.Clear()
+            pdc.EndDrawing()
+            
+            self.Refresh()
+            return
+        
+        if pdctype == 'image': # draw selected image
+            bitmap = wx.BitmapFromImage(img)
+            w,h = bitmap.GetSize()
+            pdc.DrawBitmap(bitmap, coords[0], coords[1], True) # draw the composite map
+            pdc.SetIdBounds(drawid, wx.Rect(coords[0],coords[1], w, h))
+        
+        elif pdctype == 'box': # draw a box on top of the map
+            if self.pen:
+                pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
+                pdc.SetPen(self.pen)
+                x2 = max(coords[0],coords[2])
+                x1 = min(coords[0],coords[2])
+                y2 = max(coords[1],coords[3])
+                y1 = min(coords[1],coords[3])
+                rwidth = x2-x1
+                rheight = y2-y1
+                rect = wx.Rect(x1, y1, rwidth, rheight)
+                pdc.DrawRectangleRect(rect)
+                pdc.SetIdBounds(drawid, rect)
+                
+        elif pdctype == 'line': # draw a line on top of the map
+            if self.pen:
+                pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
+                pdc.SetPen(self.pen)
+                pdc.DrawLinePoint(wx.Point(coords[0], coords[1]),wx.Point(coords[2], coords[3]))
+                pdc.SetIdBounds(drawid, wx.Rect(coords[0], coords[1], coords[2], coords[3]))
+        
+        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)
+                if (len(coords) < 2):
+                    return
+                i = 1
+                while i < len(coords):
+                    pdc.DrawLinePoint(wx.Point(coords[i-1][0], coords[i-1][1]),
+                                      wx.Point(coords[i][0], coords[i][1]))
+                    i += 1
+                
+                # get bounding rectangle for polyline
+                xlist = []
+                ylist = []
+                if len(coords) > 0:
+                    for point in coords:
+                        x,y = point
+                        xlist.append(x)
+                        ylist.append(y)
+                    x1 = min(xlist)
+                    x2 = max(xlist)
+                    y1 = min(ylist)
+                    y2 = max(ylist)
+                    pdc.SetIdBounds(drawid, wx.Rect(x1,y1,x2,y2))
+                    # self.ovlcoords[drawid] = [x1,y1,x2,y2]
+        
+        elif pdctype == 'point': # draw point
+            if self.pen:
+                pdc.SetPen(self.pen)
+                pdc.DrawPoint(coords[0], coords[1])
+                coordsBound = (coords[0] - 5,
+                               coords[1] - 5,
+                               coords[0] + 5,
+                               coords[1] + 5)
+                pdc.SetIdBounds(drawid, wx.Rect(coordsBound))
+        
+        elif pdctype == 'text': # draw text on top of map
+            if not img['active']:
+                return # only draw active text
+            if 'rotation' in img:
+                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, bbox = 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, bbox)
+        
+        pdc.EndDrawing()
+        
+        self.Refresh()
+        
+        return drawid
+    
+    def TextBounds(self, textinfo, relcoords = False):
+        """!Return text boundary data
+        
+        @param textinfo text metadata (text, font, color, rotation)
+        @param coords reference point
+        
+        @return coords of nonrotated text bbox (TL corner)
+        @return bbox of rotated text bbox (wx.Rect)
+        @return relCoords are text coord inside bbox
+        """
+        if 'rotation' in textinfo:
+            rotation = float(textinfo['rotation'])
+        else:
+            rotation = 0.0
+        
+        coords = textinfo['coords']
+        bbox = wx.Rect(coords[0], coords[1], 0, 0)
+        relCoords = (0, 0)
+        Debug.msg (4, "BufferedWindow.TextBounds(): text=%s, rotation=%f" % \
+                   (textinfo['text'], rotation))
+        
+        self.Update()
+        
+        self.SetFont(textinfo['font'])
+        
+        w, h = self.GetTextExtent(textinfo['text'])
+        
+        if rotation == 0:
+            bbox[2], bbox[3] = w, h
+            if relcoords:
+                return coords, bbox, relCoords
+            else:
+                return coords, bbox
+        
+        boxh = math.fabs(math.sin(math.radians(rotation)) * w) + h
+        boxw = math.fabs(math.cos(math.radians(rotation)) * w) + h
+        if rotation > 0 and rotation < 90:
+            bbox[1] -= boxh
+            relCoords = (0, boxh)
+        elif rotation >= 90 and rotation < 180:
+            bbox[0] -= boxw
+            bbox[1] -= boxh
+            relCoords = (boxw, boxh)
+        elif rotation >= 180 and rotation < 270:
+            bbox[0] -= boxw
+            relCoords = (boxw, 0)
+        bbox[2] = boxw
+        bbox[3] = boxh
+        bbox.Inflate(h,h)
+        if relcoords:
+            return coords, bbox, relCoords
+        else:
+            return coords, bbox
+
+    def OnPaint(self, event):
+        """!Draw PseudoDC's to buffered paint DC
+        
+        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.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 hasattr(self, "digit"):
+                # 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 hasattr(self, "digit"):
+                    # 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))
+            
+            self.pdc.DrawBitmap(self.bufferLast, 0, 0, False)
+            self.pdc.DrawToDC(dc)
+        
+        # draw decorations (e.g. region box)
+        try:
+            gcdc = wx.GCDC(dc)
+            self.pdcDec.DrawToDC(gcdc)
+        except NotImplementedError, e:
+            print >> sys.stderr, e
+            self.pdcDec.DrawToDC(dc)
+        
+        # draw temporary object on the foreground
+        ### self.pdcTmp.DrawToDCClipped(dc, rgn)
+        self.pdcTmp.DrawToDC(dc)
+        
+        if switchDraw:
+            self.redrawAll = False
+        
+    def OnSize(self, event):
+        """!Scale map image so that it is the same size as the Window
+        """
+        Debug.msg(3, "BufferedWindow.OnSize():")
+        
+        # set size of the input image
+        self.Map.ChangeMapSize(self.GetClientSize())
+        # align extent based on center point and display resolution
+        # this causes that image is not resized when display windows is resized
+        ### self.Map.AlignExtentFromDisplay()
+        
+        # Make new off screen bitmap: this bitmap will always have the
+        # current drawing in it, so it can be used to save the image to
+        # a file, or whatever.
+        self.buffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
+        
+        # get the image to be rendered
+        self.img = self.GetImage()
+        
+        # update map display
+        if self.img and self.Map.width + self.Map.height > 0: # scale image during resize
+            self.img = self.img.Scale(self.Map.width, self.Map.height)
+            if len(self.Map.GetListOfLayers()) > 0:
+                self.UpdateMap()
+        
+        # re-render image on idle
+        self.resize = True
+
+        # reposition checkbox in statusbar
+        self.parent.StatusbarReposition()
+        
+        # update statusbar
+        self.parent.StatusbarUpdate()
+        
+    def OnIdle(self, event):
+        """!Only re-render a composite map image from GRASS during
+        idle time instead of multiple times during resizing.
+        """
+        if self.resize:
+            self.UpdateMap(render = True)
+        
+        event.Skip()
+
+    def SaveToFile(self, FileName, FileType, width, height):
+        """!This draws the pseudo DC to a buffer that can be saved to
+        a file.
+        
+        @param FileName file name
+        @param FileType type of bitmap
+        @param width image width
+        @param height image height
+        """
+        busy = wx.BusyInfo(message = _("Please wait, exporting image..."),
+                           parent = self)
+        wx.Yield()
+        
+        self.Map.ChangeMapSize((width, height))
+        ibuffer = wx.EmptyBitmap(max(1, width), max(1, height))
+        self.Map.Render(force = True, windres = True)
+        img = self.GetImage()
+        self.pdc.RemoveAll()
+        self.Draw(self.pdc, img, drawid = 99)
+        
+        # compute size ratio to move overlay accordingly
+        cSize = self.GetClientSizeTuple()
+        ratio = float(width) / cSize[0], float(height) / cSize[1]
+        
+        # redraw lagend, scalebar
+        for img in self.GetOverlay():
+            # draw any active and defined overlays
+            if self.imagedict[img]['layer'].IsActive():
+                id = self.imagedict[img]['id']
+                coords = int(ratio[0] * self.overlays[id]['coords'][0]),\
+                         int(ratio[1] * self.overlays[id]['coords'][1])
+                self.Draw(self.pdc, img = img, drawid = id,
+                          pdctype = self.overlays[id]['pdcType'], coords = coords)
+                          
+        # redraw text labels
+        for id in self.textdict.keys():
+            textinfo = self.textdict[id]
+            oldCoords = textinfo['coords']
+            textinfo['coords'] = ratio[0] * textinfo['coords'][0],\
+                                 ratio[1] * textinfo['coords'][1]
+            self.Draw(self.pdc, img = self.textdict[id], drawid = id,
+                      pdctype = 'text')
+            # set back old coordinates
+            textinfo['coords'] = oldCoords
+            
+        dc = wx.BufferedPaintDC(self, ibuffer)
+        dc.Clear()
+        self.PrepareDC(dc)
+        self.pdc.DrawToDC(dc)
+        if self.pdcVector:
+            self.pdcVector.DrawToDC(dc)
+        ibuffer.SaveFile(FileName, FileType)
+        
+        busy.Destroy()
+        
+        self.UpdateMap(render = True)
+        self.Refresh()
+        
+    def GetOverlay(self):
+        """!Converts rendered overlay files to wx.Image
+        
+        Updates self.imagedict
+        
+        @return list of images
+        """
+        imgs = []
+        for overlay in self.Map.GetListOfLayers(l_type = "overlay", l_active = True):
+            if os.path.isfile(overlay.mapfile) and os.path.getsize(overlay.mapfile):
+                img = wx.Image(overlay.mapfile, wx.BITMAP_TYPE_ANY)
+                self.imagedict[img] = { 'id' : overlay.id,
+                                        'layer' : overlay }
+                imgs.append(img)
+
+        return imgs
+    
+    def GetImage(self):
+        """!Converts redered map files to wx.Image
+        
+        Updates self.imagedict (id=99)
+        
+        @return wx.Image instance (map composition)
+        """
+        imgId = 99
+        if self.mapfile and self.Map.mapfile and os.path.isfile(self.Map.mapfile) and \
+                os.path.getsize(self.Map.mapfile):
+            img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
+        else:
+            img = None
+        
+        self.imagedict[img] = { 'id': imgId }
+        
+        return img
+
+    def UpdateMap(self, render = True, renderVector = True):
+        """!Updates the canvas anytime there is a change to the
+        underlaying images or to the geometry of the canvas.
+        
+        @param render re-render map composition
+        @param renderVector re-render vector map layer enabled for editing (used for digitizer)
+        """
+        start = time.clock()
+        
+        self.resize = False
+        
+        if self.img is None:
+            render = True
+        
+        #
+        # initialize process bar (only on 'render')
+        #
+        if render or renderVector:
+            self.parent.GetProgressBar().Show()
+            if self.parent.GetProgressBar().GetRange() > 0:
+                self.parent.GetProgressBar().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:
+            self.tree.ReorderLayers()
+        
+        # reset flag for auto-rendering
+        if self.tree:
+            self.tree.rerender = False
+        
+        try:
+            if render:
+                # update display size
+                self.Map.ChangeMapSize(self.GetClientSize())
+                if self.parent.GetProperty('resolution'):
+                    # use computation region resolution for rendering
+                    windres = True
+                else:
+                    windres = False
+                self.mapfile = self.Map.Render(force = True, mapWindow = self.parent,
+                                               windres = windres)
+            else:
+                self.mapfile = self.Map.Render(force = False, mapWindow = self.parent)
+        except GException, e:
+            GError(message = e.value)
+            self.mapfile = None
+        
+        self.img = self.GetImage() # id=99
+        
+        #
+        # clear pseudoDcs
+        #
+        for pdc in (self.pdc,
+                    self.pdcDec,
+                    self.pdcTmp):
+            pdc.Clear()
+            pdc.RemoveAll()
+        
+        #
+        # draw background map image to PseudoDC
+        #
+        if not self.img:
+            self.Draw(self.pdc, pdctype = 'clear')
+        else:
+            try:
+                id = self.imagedict[self.img]['id']
+            except:
+                return False
+            
+            self.Draw(self.pdc, self.img, drawid = id)
+        
+        #
+        # render vector map layer
+        #
+        if renderVector and hasattr(self, "digit"):
+            self._updateMap()
+        #
+        # render overlays
+        #
+        for img in self.GetOverlay():
+            # draw any active and defined overlays
+            if self.imagedict[img]['layer'].IsActive():
+                id = self.imagedict[img]['id']
+                self.Draw(self.pdc, img = img, drawid = id,
+                          pdctype = self.overlays[id]['pdcType'], coords = self.overlays[id]['coords'])
+        
+        for id in self.textdict.keys():
+            self.Draw(self.pdc, img = self.textdict[id], drawid = id,
+                      pdctype = 'text', coords = [10, 10, 10, 10])
+        
+        # optionally draw computational extent box
+        self.DrawCompRegionExtent()
+        
+        #
+        # redraw pdcTmp if needed
+        #
+        if len(self.polycoords) > 0:
+            self.DrawLines(self.pdcTmp)
+            
+        if not self.parent.IsStandalone() and \
+                self.parent.GetLayerManager().gcpmanagement:
+            # -> georectifier (redraw GCPs)
+            if self.parent.GetMapToolbar():
+                if self == self.parent.TgtMapWindow:
+                    coordtype = 'target'
+                else:
+                    coordtype = 'source'
+
+                self.parent.DrawGCP(coordtype)
+
+        # 
+        # clear measurement
+        #
+        if self.mouse["use"] == "measure":
+            self.ClearLines(pdc = self.pdcTmp)
+            self.polycoords = []
+            self.mouse['use'] = 'pointer'
+            self.mouse['box'] = 'point'
+            self.mouse['end'] = [0, 0]
+            self.SetCursor(self.parent.cursors["default"])
+            
+        stop = time.clock()
+        
+        #
+        # hide process bar
+        #
+        self.parent.GetProgressBar().Hide()
+
+        #
+        # update statusbar 
+        #
+        ### self.Map.SetRegion()
+        self.parent.StatusbarUpdate()
+        
+        Debug.msg (1, "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 map canvas
+        """
+        self.Draw(self.pdc, pdctype = 'clear')
+        
+        if hasattr(self, "digit"):
+            self.Draw(self.pdcVector, pdctype = 'clear')
+        
+        self.Draw(self.pdcDec, pdctype = 'clear')
+        self.Draw(self.pdcTmp, pdctype = 'clear')
+        
+    def DragMap(self, moveto):
+        """!Drag the entire map image for panning.
+        
+        @param moveto dx,dy
+        """
+        dc = wx.BufferedDC(wx.ClientDC(self))
+        dc.SetBackground(wx.Brush("White"))
+        dc.Clear()
+        
+        self.dragimg = wx.DragImage(self.buffer)
+        self.dragimg.BeginDrag((0, 0), self)
+        self.dragimg.GetImageRect(moveto)
+        self.dragimg.Move(moveto)
+        
+        self.dragimg.DoDrawImage(dc, moveto)
+        self.dragimg.EndDrag()
+        
+    def DragItem(self, id, event):
+        """!Drag an overlay decoration item
+        """
+        if id == 99 or id == '' or id == None: return
+        Debug.msg (5, "BufferedWindow.DragItem(): id=%d" % id)
+        x, y = self.lastpos
+        dx = event.GetX() - x
+        dy = event.GetY() - y
+        self.pdc.SetBackground(wx.Brush(self.GetBackgroundColour()))
+        r = self.pdc.GetIdBounds(id)
+        if type(r) is list:
+            r = wx.Rect(r[0], r[1], r[2], r[3])
+        if id > 100: # text dragging
+            rtop = (r[0],r[1]-r[3],r[2],r[3])
+            r = r.Union(rtop)
+            rleft = (r[0]-r[2],r[1],r[2],r[3])
+            r = r.Union(rleft)
+        self.pdc.TranslateId(id, dx, dy)
+        
+        r2 = self.pdc.GetIdBounds(id)
+        if type(r2) is list:
+            r2 = wx.Rect(r[0], r[1], r[2], r[3])
+        if id > 100: # text
+            self.textdict[id]['bbox'] = r2
+            self.textdict[id]['coords'][0] += dx
+            self.textdict[id]['coords'][1] += dy
+        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'].
+        """
+        if not pdc:
+            return
+        
+        if begin is None:
+            begin = self.mouse['begin']
+        if end is None:
+            end   = self.mouse['end']
+        
+        Debug.msg (5, "BufferedWindow.MouseDraw(): use=%s, box=%s, begin=%f,%f, end=%f,%f" % \
+                       (self.mouse['use'], self.mouse['box'],
+                        begin[0], begin[1], end[0], end[1]))
+        
+        if self.mouse['box'] == "box":
+            boxid = wx.ID_NEW
+            mousecoords = [begin[0], begin[1],
+                           end[0], end[1]]
+            r = pdc.GetIdBounds(boxid)
+            if type(r) is list:
+                r = wx.Rect(r[0], r[1], r[2], r[3])
+            r.Inflate(4, 4)
+            try:
+                pdc.ClearId(boxid)
+            except:
+                pass
+            self.RefreshRect(r, False)
+            pdc.SetId(boxid)
+            self.Draw(pdc, drawid = boxid, pdctype = 'box', coords = mousecoords)
+        
+        elif self.mouse['box'] == "line":
+            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
+        
+        # zoom with mouse wheel
+        if event.GetWheelRotation() != 0:
+            self.OnMouseWheel(event)
+            
+        # left mouse button pressed
+        elif event.LeftDown():
+            self.OnLeftDown(event)
+        
+        # left mouse button released
+        elif event.LeftUp():
+            self.OnLeftUp(event)
+        
+        # dragging
+        elif event.Dragging():
+            self.OnDragging(event)
+        
+        # double click
+        elif event.ButtonDClick():
+            self.OnButtonDClick(event)
+        
+        # middle mouse button pressed
+        elif event.MiddleDown():
+            self.OnMiddleDown(event)
+        
+        # middle mouse button relesed
+        elif event.MiddleUp():
+            self.OnMiddleUp(event)
+        
+        # right mouse button pressed
+        elif event.RightDown():
+            self.OnRightDown(event)
+        
+        # right mouse button released
+        elif event.RightUp():
+            self.OnRightUp(event)
+        
+        elif event.Entering():
+            self.OnMouseEnter(event)
+        
+        elif event.Moving():
+            self.OnMouseMoving(event)
+                
+    def OnMouseWheel(self, event):
+        """!Mouse wheel moved
+        """
+        if not UserSettings.Get(group = 'display',
+                                key = 'mouseWheelZoom',
+                                subkey = 'enabled'):
+            event.Skip()
+            return
+            
+        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
+        
+        if UserSettings.Get(group = 'display',
+                            key = 'mouseWheelZoom',
+                            subkey = 'selection'):
+            zoomtype *= -1
+            
+        # zoom
+        self.Zoom(begin, end, zoomtype)
+        
+        # redraw map
+        self.UpdateMap()
+        
+        # update statusbar
+        self.parent.StatusbarUpdate()
+        
+        self.Refresh()
+        self.processMouse = True
+        
+    def OnDragging(self, event):
+        """!Mouse dragging
+        """
+        Debug.msg (5, "BufferedWindow.MouseAction(): Dragging")
+        current  = event.GetPositionTuple()[:]
+        previous = self.mouse['begin']
+        move = (current[0] - previous[0],
+                current[1] - previous[1])
+        
+        if hasattr(self, "digit"):
+            digitToolbar = self.toolbar
+        else:
+            digitToolbar = None
+        
+        # dragging or drawing box with left button
+        if self.mouse['use'] == 'pan' or \
+                event.MiddleIsDown():
+            self.DragMap(move)
+        
+        # dragging decoration overlay item
+        elif (self.mouse['use'] == 'pointer' and 
+                not digitToolbar and 
+                self.dragid != None):
+            self.DragItem(self.dragid, event)
+        
+        # dragging anything else - rubber band box or line
+        else:
+            if (self.mouse['use'] == 'pointer' and 
+                not digitToolbar):
+                return
+            
+            self.mouse['end'] = event.GetPositionTuple()[:]
+            if (event.LeftIsDown() and 
+                not (digitToolbar and 
+                    digitToolbar.GetAction() in ("moveLine",) and 
+                     self.digit.GetDisplay().GetSelected() > 0)):
+                self.MouseDraw(pdc = self.pdcTmp)
+        
+    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'] in ('zoom', 'legend'):
+            pass
+        
+        # vector digizer
+        elif self.mouse["use"] == "pointer" and \
+                hasattr(self, "digit"):
+            if event.ControlDown():
+                self.OnLeftDownUndo(event)
+            else:
+                self._onLeftDown(event)
+        
+        elif self.mouse['use'] == 'pointer':
+            # get decoration or text id
+            self.idlist = []
+            self.dragid = ''
+            self.lastpos = self.mouse['begin']
+            idlist = self.pdc.FindObjects(self.lastpos[0], self.lastpos[1],
+                                          self.hitradius)
+            if 99 in idlist:
+                idlist.remove(99)                             
+            if idlist != []:
+                self.dragid = idlist[0] #drag whatever is on top
+        else:
+            pass
+        
+        event.Skip()
+        
+    def 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
+            layers = self.GetSelectedLayer(multi = True)
+            isRaster = False
+            nVectors = 0
+            for l in layers:
+                if l.GetType() == 'raster':
+                    isRaster = True
+                    break
+                if l.GetType() == 'vector':
+                    nVectors += 1
+            
+            if isRaster or nVectors > 1:
+                self.parent.QueryMap(self.mouse['begin'][0],self.mouse['begin'][1])
+            else:
+                self.parent.QueryVector(self.mouse['begin'][0], self.mouse['begin'][1])
+                # clear temp canvas
+                self.UpdateMap(render = False, renderVector = False)
+            
+        elif self.mouse["use"] == "queryVector":
+            # editable mode for vector map layers
+            self.parent.QueryVector(self.mouse['begin'][0], self.mouse['begin'][1])
+            
+            # clear temp canvas
+            self.UpdateMap(render = False, renderVector = False)
+        
+        elif self.mouse["use"] in ["measure", "profile"]:
+            # measure or profile
+            if self.mouse["use"] == "measure":
+                self.parent.MeasureDist(self.mouse['begin'], self.mouse['end'])
+            
+            self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
+            self.ClearLines(pdc = self.pdcTmp)
+            self.DrawLines(pdc = self.pdcTmp)
+        
+        elif self.mouse["use"] == "pointer" and \
+                self.parent.GetLayerManager().gcpmanagement:
+            # -> GCP manager
+            if self.parent.GetToolbar('gcpdisp'):
+                coord = self.Pixel2Cell(self.mouse['end'])
+                if self.parent.MapWindow == self.parent.SrcMapWindow:
+                    coordtype = 'source'
+                else:
+                    coordtype = 'target'
+                
+                self.parent.GetLayerManager().gcpmanagement.SetGCPData(coordtype, coord, self, confirm = True)
+                self.UpdateMap(render = False, renderVector = False)
+            
+        elif self.mouse["use"] == "pointer" and \
+                hasattr(self, "digit"):
+            self._onLeftUp(event)
+            
+        elif (self.mouse['use'] == 'pointer' and 
+                self.dragid >= 0):
+            # end drag of overlay decoration
+            
+            if self.dragid < 99 and self.dragid in self.overlays:
+                self.overlays[self.dragid]['coords'] = self.pdc.GetIdBounds(self.dragid)
+            elif self.dragid > 100 and self.dragid in self.textdict:
+                self.textdict[self.dragid]['bbox'] = self.pdc.GetIdBounds(self.dragid)
+            else:
+                pass
+            self.dragid = None
+            self.currtxtid = None
+            
+        elif self.mouse['use'] == 'legend':
+            self.ResizeLegend(self.mouse["begin"], self.mouse["end"])
+            self.parent.dialogs['legend'].FindWindowByName("resize").SetValue(False)
+            self.Map.GetOverlay(1).SetActive(True)
+            self.parent.MapWindow.SetCursor(self.parent.cursors["default"])
+            self.parent.MapWindow.mouse['use'] = 'pointer'
+            
+            self.UpdateMap()
+            
+    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" or \
+                (self.mouse['use'] != 'pointer' and \
+                     hasattr(self, "digit")):
+               # select overlay decoration options dialog
+            clickposition = event.GetPositionTuple()[:]
+            idlist  = self.pdc.FindObjects(clickposition[0], clickposition[1], self.hitradius)
+            if idlist == []:
+                return
+            self.dragid = idlist[0]
+
+            # self.ovlcoords[self.dragid] = self.pdc.GetIdBounds(self.dragid)
+            if self.dragid > 100:
+                self.currtxtid = self.dragid
+                self.parent.OnAddText(None)
+            elif self.dragid == 0:
+                self.parent.OnAddBarscale(None)
+            elif self.dragid == 1:
+                self.parent.OnAddLegend(None)
+        
+    def OnRightDown(self, event):
+        """!Right mouse button pressed
+        """
+        Debug.msg (5, "BufferedWindow.OnRightDown(): use=%s" % \
+                   self.mouse["use"])
+        
+        if hasattr(self, "digit"):
+            self._onRightDown(event)
+        
+        event.Skip()
+        
+    def OnRightUp(self, event):
+        """!Right mouse button released
+        """
+        Debug.msg (5, "BufferedWindow.OnRightUp(): use=%s" % \
+                   self.mouse["use"])
+        
+        if hasattr(self, "digit"):
+            self._onRightUp(event)
+        
+        self.redrawAll = True
+        self.Refresh()
+        
+        event.Skip()
+        
+    def OnMiddleDown(self, event):
+        """!Middle mouse button pressed
+        """
+        if not event:
+            return
+        
+        self.mouse['begin'] = event.GetPositionTuple()[:]
+        
+    def OnMiddleUp(self, event):
+        """!Middle mouse button released
+        """
+        self.mouse['end'] = event.GetPositionTuple()[:]
+        
+        # set region in zoom or pan
+        begin = self.mouse['begin']
+        end   = self.mouse['end']
+        
+        self.Zoom(begin, end, 0) # no zoom
+        
+        # redraw map
+        self.UpdateMap(render = True)
+        
+        # update statusbar
+        self.parent.StatusbarUpdate()
+        
+    def OnMouseEnter(self, event):
+        """!Mouse entered window and no mouse buttons were pressed
+        """
+        if self.parent.GetLayerManager().gcpmanagement:
+            if self.parent.GetToolbar('gcpdisp'):
+                if not self.parent.MapWindow == self:
+                    self.parent.MapWindow = self
+                    self.parent.Map = self.Map
+                    self.parent.UpdateActive(self)
+                    # needed for wingrass
+                    self.SetFocus()
+        else:
+            event.Skip()
+        
+    def OnMouseMoving(self, event):
+        """!Motion event and no mouse buttons were pressed
+        """
+        if self.mouse["use"] == "pointer" and \
+                hasattr(self, "digit"):
+            self._onMouseMoving(event)
+        
+        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))
+        
+        return True
+
+    def Pixel2Cell(self, (x, y)):
+        """!Convert image coordinates to real word coordinates
+        
+        @param x, y image coordinates
+        
+        @return easting, northing
+        @return None on error
+        """
+        try:
+            x = int(x)
+            y = int(y)
+        except:
+            return None
+        
+        if self.Map.region["ewres"] > self.Map.region["nsres"]:
+            res = self.Map.region["ewres"]
+        else:
+            res = self.Map.region["nsres"]
+        
+        w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
+        n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
+        
+        east  = w + x * res
+        north = n - y * res
+        
+        return (east, north)
+    
+    def Cell2Pixel(self, (east, north)):
+        """!Convert real word coordinates to image coordinates
+        """
+        try:
+            east  = float(east)
+            north = float(north)
+        except:
+            return None
+        
+        if self.Map.region["ewres"] > self.Map.region["nsres"]:
+            res = self.Map.region["ewres"]
+        else:
+            res = self.Map.region["nsres"]
+        
+        w = self.Map.region["center_easting"] - (self.Map.width / 2) * res
+        n = self.Map.region["center_northing"] + (self.Map.height / 2) * res
+        
+        x = (east  - w) / res
+        y = (n - north) / res
+        
+        return (x, y)
+    
+    def ResizeLegend(self, begin, end):
+        w = abs(begin[0] - end[0])
+        h = abs(begin[1] - end[1])
+        if begin[0] < end[0]:
+            x = begin[0]
+        else:
+            x = end[0]
+        if begin[1] < end[1]:
+            y = begin[1]
+        else:
+            y = end[1]
+        screenRect = wx.Rect(x, y, w, h)
+        screenSize = self.GetClientSizeTuple()
+        at = [(screenSize[1] - (y + h)) / float(screenSize[1]) * 100,
+              (screenSize[1] - y) / float(screenSize[1]) * 100,
+              x / float(screenSize[0]) * 100,
+              (x + w) / float(screenSize[0]) * 100]
+        for i, subcmd in enumerate(self.overlays[1]['cmd']):
+            if subcmd.startswith('at='):
+                self.overlays[1]['cmd'][i] = "at=%d,%d,%d,%d" % (at[0], at[1], at[2], at[3])
+        self.Map.ChangeOverlay(1, True, command = self.overlays[1]['cmd'])
+        self.overlays[1]['coords'] = (0,0)
+        
+    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.Map.projinfo['proj'] == 'll':
+                self.Map.region['n'] = min(self.Map.region['n'], 90.0)
+                self.Map.region['s'] = max(self.Map.region['s'], -90.0)
+            
+            ce = newreg['w'] + (newreg['e'] - newreg['w']) / 2
+            cn = newreg['s'] + (newreg['n'] - newreg['s']) / 2
+            
+            # 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
+            if not self.parent.HasProperty('alignExtent') or \
+                    self.parent.GetProperty('alignExtent'):
+                self.Map.AlignExtentFromDisplay()
+            else:
+                for k in ('n', 's', 'e', 'w'):
+                    self.Map.region[k] = newreg[k]
+            
+            if hasattr(self, "digit") and \
+                    hasattr(self, "moveInfo"):
+                self._zoom(None)
+            
+            self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                             self.Map.region['e'], self.Map.region['w'])
+        
+        if self.redrawAll is False:
+            self.redrawAll = True
+        
+    def ZoomBack(self):
+        """!Zoom to previous extents in zoomhistory list
+        """
+        zoom = list()
+        
+        if len(self.zoomhistory) > 1:
+            self.zoomhistory.pop()
+            zoom = self.zoomhistory[-1]
+        
+        # disable tool if stack is empty
+        if len(self.zoomhistory) < 2: # disable tool
+            toolbar = self.parent.GetMapToolbar()
+            toolbar.Enable('zoomBack', enable = False)
+        
+        # zoom to selected region
+        self.Map.GetRegion(n = zoom[0], s = zoom[1],
+                           e = zoom[2], w = zoom[3],
+                           update = True)
+        # update map
+        self.UpdateMap()
+        
+        # update statusbar
+        self.parent.StatusbarUpdate()
+
+    def ZoomHistory(self, n, s, e, w):
+        """!Manages a list of last 10 zoom extents
+
+        @param n,s,e,w north, south, east, west
+
+        @return removed history item if exists (or None)
+        """
+        removed = None
+        self.zoomhistory.append((n,s,e,w))
+        
+        if len(self.zoomhistory) > 10:
+            removed = self.zoomhistory.pop(0)
+        
+        if removed:
+            Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s, removed=%s" %
+                      (self.zoomhistory, removed))
+        else:
+            Debug.msg(4, "BufferedWindow.ZoomHistory(): hist=%s" %
+                      (self.zoomhistory))
+        
+        # update toolbar
+        if len(self.zoomhistory) > 1:
+            enable = True
+        else:
+            enable = False
+        
+        toolbar = self.parent.GetMapToolbar()
+        
+        toolbar.Enable('zoomBack', enable)
+        
+        return removed
+
+    def ResetZoomHistory(self):
+        """!Reset zoom history"""
+        self.zoomhistory = list()
+                
+    def ZoomToMap(self, layers = None, ignoreNulls = False, render = True):
+        """!Set display extents to match selected raster
+        or vector map(s).
+
+        @param layers list of layers to be zoom to
+        @param ignoreNulls True to ignore null-values (valid only for rasters)
+        @param render True to re-render display
+        """
+        zoomreg = {}
+        
+        if not layers:
+            layers = self.GetSelectedLayer(multi = True)
+        
+        if not layers:
+            return
+        
+        rast = []
+        vect = []
+        updated = False
+        for l in layers:
+            # only raster/vector layers are currently supported
+            if l.type == 'raster':
+                rast.append(l.GetName())
+            elif l.type == 'vector':
+                if hasattr(self, "digit") and \
+                        self.toolbar.GetLayer() == l:
+                    w, s, b, e, n, t = self.digit.GetDisplay().GetMapBoundingBox()
+                    self.Map.GetRegion(n = n, s = s, w = w, e = e,
+                                       update = True)
+                    updated = True
+                else:
+                    vect.append(l.name)
+            elif l.type == 'rgb':
+                for rname in l.GetName().splitlines():
+                    rast.append(rname)
+            
+        if not updated:
+            self.Map.GetRegion(rast = rast,
+                               vect = vect,
+                               update = True)
+        
+        self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                         self.Map.region['e'], self.Map.region['w'])
+        
+        if render:
+            self.UpdateMap()
+        
+        self.parent.StatusbarUpdate()
+        
+    def ZoomToWind(self):
+        """!Set display geometry to match computational region
+        settings (set with g.region)
+        """
+        self.Map.region = self.Map.GetRegion()
+        
+        self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                         self.Map.region['e'], self.Map.region['w'])
+        
+        self.UpdateMap()
+        
+        self.parent.StatusbarUpdate()
+
+    def ZoomToDefault(self):
+        """!Set display geometry to match default region settings
+        """
+        self.Map.region = self.Map.GetRegion(default = True)
+        self.Map.AdjustRegion() # aling region extent to the display
+
+        self.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                         self.Map.region['e'], self.Map.region['w'])
+        
+        self.UpdateMap()
+        
+        self.parent.StatusbarUpdate()
+    
+    
+    def GoTo(self, e, n):
+        region = self.Map.GetCurrentRegion()
+
+        region['center_easting'], region['center_northing'] = e, n
+        
+        dn = (region['nsres'] * region['rows']) / 2.
+        region['n'] = region['center_northing'] + dn
+        region['s'] = region['center_northing'] - dn
+        de = (region['ewres'] * region['cols']) / 2.
+        region['e'] = region['center_easting'] + de
+        region['w'] = region['center_easting'] - de
+
+        self.Map.AdjustRegion()
+
+        # add to zoom history
+        self.ZoomHistory(region['n'], region['s'],
+                                   region['e'], region['w'])        
+        self.UpdateMap()
+    
+    def DisplayToWind(self):
+        """!Set computational region (WIND file) to match display
+        extents
+        """
+        tmpreg = os.getenv("GRASS_REGION")
+        if tmpreg:
+            del os.environ["GRASS_REGION"]
+        
+        # We ONLY want to set extents here. Don't mess with resolution. Leave that
+        # for user to set explicitly with g.region
+        new = self.Map.AlignResolution()
+        RunCommand('g.region',
+                   parent = self,
+                   overwrite = True,
+                   n = new['n'],
+                   s = new['s'],
+                   e = new['e'],
+                   w = new['w'],
+                   rows = int(new['rows']),
+                   cols = int(new['cols']))
+        
+        if tmpreg:
+            os.environ["GRASS_REGION"] = tmpreg
+        
+    def ZoomToSaved(self):
+        """!Set display geometry to match extents in
+        saved region file
+        """
+        dlg = SavedRegion(parent = self,
+                          title = _("Zoom to saved region extents"),
+                          loadsave='load')
+        
+        if dlg.ShowModal() == wx.ID_CANCEL or not dlg.wind:
+            dlg.Destroy()
+            return
+        
+        if not grass.find_file(name = dlg.wind, element = 'windows')['name']:
+            wx.MessageBox(parent = self,
+                          message = _("Region <%s> not found. Operation canceled.") % dlg.wind,
+                          caption = _("Error"), style = wx.ICON_ERROR | wx.OK | wx.CENTRE)
+            dlg.Destroy()
+            return
+        
+        self.Map.GetRegion(regionName = dlg.wind,
+                           update = True)
+        
+        dlg.Destroy()
+        
+        self.ZoomHistory(self.Map.region['n'],
+                         self.Map.region['s'],
+                         self.Map.region['e'],
+                         self.Map.region['w'])
+        
+        self.UpdateMap()
+                
+    def SaveDisplayRegion(self):
+        """!Save display extents to named region file.
+        """
+        dlg = SavedRegion(parent = self,
+                          title = _("Save display extents to region file"),
+                          loadsave='save')
+        
+        if dlg.ShowModal() == wx.ID_CANCEL or not dlg.wind:
+            dlg.Destroy()
+            return
+        
+        # test to see if it already exists and ask permission to overwrite
+        if grass.find_file(name = dlg.wind, element = 'windows')['name']:
+            overwrite = wx.MessageBox(parent = self,
+                                      message = _("Region file <%s> already exists. "
+                                                  "Do you want to overwrite it?") % (dlg.wind),
+                                      caption = _("Warning"), style = wx.YES_NO | wx.CENTRE)
+            if (overwrite == wx.YES):
+                self.SaveRegion(dlg.wind)
+        else:
+            self.SaveRegion(dlg.wind)
+        
+        dlg.Destroy()
+        
+    def SaveRegion(self, wind):
+        """!Save region settings
+        
+        @param wind region name
+        """
+        new = self.Map.GetCurrentRegion()
+        
+        tmpreg = os.getenv("GRASS_REGION")
+        if tmpreg:
+            del os.environ["GRASS_REGION"]
+        
+        RunCommand('g.region',
+                   overwrite = True,
+                   parent = self,
+                   flags = 'u',
+                   n = new['n'],
+                   s = new['s'],
+                   e = new['e'],
+                   w = new['w'],
+                   rows = int(new['rows']),
+                   cols = int(new['cols']),
+                   save = wind)
+        
+        if tmpreg:
+            os.environ["GRASS_REGION"] = tmpreg
+        
+    def Distance(self, beginpt, endpt, screen = True):
+        """!Calculete distance
+        
+        Ctypes required for LL-locations
+        
+        @param beginpt first point
+        @param endpt second point
+        @param screen True for screen coordinates otherwise EN
+        """
+        if screen:
+            e1, n1 = self.Pixel2Cell(beginpt)
+            e2, n2 = self.Pixel2Cell(endpt)
+        else:
+            e1, n1 = beginpt
+            e2, n2 = endpt
+            
+        dEast  = (e2 - e1)
+        dNorth = (n2 - n1)
+        
+        if self.parent.Map.projinfo['proj'] == 'll' and haveCtypes:
+            dist = gislib.G_distance(e1, n1, e2, n2)
+        else:
+            dist = math.sqrt(math.pow((dEast), 2) + math.pow((dNorth), 2))
+        
+        return (dist, (dEast, dNorth))


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/statusbar.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/statusbar.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/statusbar.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1059 @@
+"""!
+ at package mapdisp.statusbar
+
+ at brief Classes for statusbar management
+
+Classes:
+ - statusbar::SbException
+ - statusbar::SbManager
+ - statusbar::SbItem
+ - statusbar::SbRender
+ - statusbar::SbShowRegion
+ - statusbar::SbAlignExtent
+ - statusbar::SbResolution
+ - statusbar::SbMapScale
+ - statusbar::SbGoTo
+ - statusbar::SbProjection
+ - statusbar::SbMask
+ - statusbar::SbTextItem
+ - statusbar::SbDisplayGeometry
+ - statusbar::SbCoordinates
+ - statusbar::SbRegionExtent
+ - statusbar::SbCompRegionExtent
+ - statusbar::SbProgress
+
+(C) 2006-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Vaclav Petras <wenzeslaus gmail.com>
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import wx
+
+from core          import utils
+from core.gcmd     import GMessage, RunCommand
+from core.settings import UserSettings
+
+from grass.script  import core as grass
+
+class SbException:
+    """! Exception class used in SbManager and SbItems"""
+    def __init__(self, message):
+        self.message = message
+    def __str__(self):
+        return self.message
+
+
+class SbManager:
+    """!Statusbar manager for wx.Statusbar and SbItems.
+    
+    Statusbar manager manages items added by AddStatusbarItem method.
+    Provides progress bar (SbProgress) and choice (wx.Choice).
+    Items with position 0 are shown according to choice selection.
+    Only one item of the same class is supposed to be in statusbar.
+    Manager user have to create statusbar on his own, add items to manager
+    and call Update method to show particular widgets.
+    User settings (group = 'display', key = 'statusbarMode', subkey = 'selection')
+    are taken into account.
+    
+    @todo generalize access to UserSettings (specify group, etc.) 
+    @todo add GetMode method using name instead of index
+    """
+    def __init__(self, mapframe, statusbar):
+        """!Connects manager to statusbar
+        
+        Creates choice and progress bar.
+        """
+        self.mapFrame = mapframe
+        self.statusbar = statusbar
+        
+        self.choice = wx.Choice(self.statusbar, wx.ID_ANY)
+        
+        self.choice.Bind(wx.EVT_CHOICE, self.OnToggleStatus)
+        
+        self.statusbarItems = dict()
+        
+        self._postInitialized = False
+        
+        self.progressbar = SbProgress(self.mapFrame, self.statusbar)
+        
+        self._hiddenItems = {}
+    
+    def SetProperty(self, name, value):
+        """!Sets property represented by one of contained SbItems
+            
+        @param name name of SbItem (from name attribute)
+        @param value value to be set
+        """
+        self.statusbarItems[name].SetValue(value)
+        
+    def GetProperty(self, name):
+        """!Returns property represented by one of contained SbItems
+        
+        @param name name of SbItem (from name attribute)
+        """
+        return self.statusbarItems[name].GetValue()
+        
+    def HasProperty(self, name):
+        """!Checks whether property is represented by one of contained SbItems
+        
+        @param name name of SbItem (from name attribute)
+        
+        @returns True if particular SbItem is contained, False otherwise
+        """
+        if name in self.statusbarItems:
+            return True
+        return False
+    
+    def AddStatusbarItem(self, item):
+        """!Adds item to statusbar
+        
+        If item position is 0, item is managed by choice.
+        
+        @see AddStatusbarItemsByClass
+        """
+        self.statusbarItems[item.name] = item
+        if item.GetPosition() == 0:
+            self.choice.Append(item.label, clientData = item) #attrError?
+            
+    def AddStatusbarItemsByClass(self, itemClasses, **kwargs):
+        """!Adds items to statusbar
+
+        @param itemClasses list of classes of items to be add
+        @param kwargs SbItem constructor parameters
+        
+        @see AddStatusbarItem
+        """
+        for Item in itemClasses:
+            item = Item(**kwargs)
+            self.AddStatusbarItem(item)
+                      
+    def HideStatusbarChoiceItemsByClass(self, itemClasses):
+        """!Hides items showed in choice
+        
+        Hides items with position 0 (items showed in choice) by removing
+        them from choice.
+        
+        @param itemClasses list of classes of items to be hided
+        
+        @see ShowStatusbarChoiceItemsByClass
+        @todo consider adding similar function which would take item names
+        """
+        index = []
+        for itemClass in itemClasses:
+            for i in range(0, self.choice.GetCount() - 1):
+                item = self.choice.GetClientData(i)
+                if item.__class__ == itemClass:
+                    index.append(i)
+                    self._hiddenItems[i] = item
+        # must be sorted in reverse order to be removed correctly
+        for i in sorted(index, reverse = True):
+            self.choice.Delete(i)
+        
+    def ShowStatusbarChoiceItemsByClass(self, itemClasses):
+        """!Shows items showed in choice
+        
+        Shows items with position 0 (items showed in choice) by adding
+        them to choice.
+        Items are restored in their old positions.
+        
+        @param itemClasses list of classes of items to be showed
+        
+        @see HideStatusbarChoiceItemsByClass
+        """
+        # must be sorted to be inserted correctly
+        for pos in sorted(self._hiddenItems.keys()):
+            item = self._hiddenItems[pos]
+            if item.__class__ in itemClasses:
+                self.choice.Insert(item.label, pos, item)
+        
+    def ShowItem(self, itemName):
+        """!Invokes showing of particular item
+        
+        @see Update
+        """
+        self.statusbarItems[itemName].Show()
+        
+    def _postInit(self):
+        """!Post-initialization method
+        
+        It sets internal user settings,
+        set choice's selection (from user settings) and does reposition.
+        It needs choice filled by items.
+        it is called automatically.
+        """
+        UserSettings.Set(group = 'display',
+                         key = 'statusbarMode',
+                         subkey = 'choices',
+                         value = self.choice.GetItems(),
+                         internal = True)
+        
+        self.choice.SetSelection(UserSettings.Get(group = 'display',
+                                                  key = 'statusbarMode',
+                                                  subkey = 'selection')) 
+        self.Reposition()
+        
+        self._postInitialized = True
+        
+    def Update(self):
+        """!Updates statusbar
+
+        It always updates mask.
+        """
+        if not self._postInitialized:
+            self._postInit()
+        
+        for item in self.statusbarItems.values():
+            if item.GetPosition() == 0:
+                item.Hide()
+            else:
+                item.Update() # mask, render
+        
+        if self.choice.GetCount() > 0:
+            item = self.choice.GetClientData(self.choice.GetSelection())
+            item.Update()
+        
+    def Reposition(self):
+        """!Reposition items in statusbar
+        
+        Set positions to all items managed by statusbar manager.
+        It should not be necessary to call it manually.
+        """
+        
+        widgets = []
+        for item in self.statusbarItems.values():
+            widgets.append((item.GetPosition(), item.GetWidget()))
+            
+        widgets.append((1, self.choice))
+        widgets.append((0, self.progressbar.GetWidget()))
+                
+        for idx, win in widgets:
+            if not win:
+                continue
+            rect = self.statusbar.GetFieldRect(idx)
+            if idx == 0: # show region / mapscale / process bar
+                # -> size
+                wWin, hWin = win.GetBestSize()
+                if win == self.progressbar.GetWidget():
+                    wWin = rect.width - 6
+                # -> position
+                # if win == self.statusbarWin['region']:
+                # x, y = rect.x + rect.width - wWin, rect.y - 1
+                # align left
+                # else:
+                x, y = rect.x + 3, rect.y - 1
+                w, h = wWin, rect.height + 2
+            else: # choice || auto-rendering
+                x, y = rect.x, rect.y - 1
+                w, h = rect.width, rect.height + 2
+                if idx == 2: # mask
+                    x += 5
+                    y += 4
+                elif idx == 3: # render
+                    x += 5
+            win.SetPosition((x, y))
+            win.SetSize((w, h))
+        
+    def GetProgressBar(self):
+        """!Returns progress bar"""
+        return self.progressbar
+    
+    def OnToggleStatus(self, event):
+        """!Toggle status text
+        """
+        self.Update()
+        
+    def SetMode(self, modeIndex):
+        """!Sets current mode
+        
+        Mode is usually driven by user through choice.
+        """
+        self.choice.SetSelection(modeIndex)
+    
+    def GetMode(self):
+        """!Returns current mode"""
+        return self.choice.GetSelection()
+
+class SbItem:
+    """!Base class for statusbar items.
+    
+    Each item represents functionality (or action) controlled by statusbar
+    and related to MapFrame.
+    One item is usually connected with one widget but it is not necessary.
+    Item can represent property (depends on manager).
+    Items are not widgets but can provide interface to them.
+    Items usually has requirements to MapFrame instance
+    (specified as MapFrame.methodname or MapWindow.methodname).
+    
+    @todo consider externalizing position (see SbProgress use in SbManager)
+    """
+    def __init__(self, mapframe, statusbar, position = 0):
+        """!
+        
+        @param mapframe instance of class with MapFrame interface
+        @param statusbar statusbar instance (wx.Statusbar)
+        @param position item position in statusbar
+        
+        @todo rewrite Update also in derived classes to take in account item position
+        """
+        self.mapFrame = mapframe
+        self.statusbar = statusbar
+        self.position = position
+    
+    def Show(self):
+        """!Invokes showing of underlying widget.
+        
+        In derived classes it can do what is appropriate for it,
+        e.g. showing text on statusbar (only).
+        """
+        self.widget.Show()
+        
+    def Hide(self):
+        self.widget.Hide()
+        
+    def SetValue(self, value):
+        self.widget.SetValue(value)
+    
+    def GetValue(self):
+        return self.widget.GetValue()
+        
+    def GetPosition(self):
+        return self.position
+    
+    def GetWidget(self):
+        """!Returns underlaying winget.
+        
+        @return widget or None if doesn't exist
+        """
+        return self.widget
+    
+    def _update(self, longHelp):
+        """!Default implementation for Update method.
+        
+        @param longHelp True to enable long help (help from toolbars)
+        """
+        self.statusbar.SetStatusText("", 0)
+        self.Show()
+        self.mapFrame.StatusbarEnableLongHelp(longHelp)
+        
+    def Update(self):
+        """!Called when statusbar action is activated (e.g. through wx.Choice).
+        """
+        self._update(longHelp = False)
+
+class SbRender(SbItem):
+    """!Checkbox to enable and disable auto-rendering.
+    
+    Requires MapFrame.OnRender method.
+    """
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'render'
+        
+        self.widget = wx.CheckBox(parent = self.statusbar, id = wx.ID_ANY,
+                                  label = _("Render"))
+        
+        self.widget.SetValue(UserSettings.Get(group = 'display',
+                                              key = 'autoRendering',
+                                              subkey = 'enabled'))
+        self.widget.Hide()
+        self.widget.SetToolTip(wx.ToolTip (_("Enable/disable auto-rendering")))
+                                            
+        self.widget.Bind(wx.EVT_CHECKBOX, self.OnToggleRender)
+        
+    def OnToggleRender(self, event):
+        # (other items should call self.mapFrame.IsAutoRendered())
+        if self.GetValue():
+            self.mapFrame.OnRender(None)
+
+    def Update(self):
+        self.Show()
+        
+class SbShowRegion(SbItem):
+    """!Checkbox to enable and disable showing of computational region.
+    
+    Requires MapFrame.OnRender, MapFrame.IsAutoRendered, MapFrame.GetWindow.
+    Expects that instance returned by MapFrame.GetWindow will handle
+    regionCoords attribute. 
+    """
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'region'
+        self.label = _("Show comp. extent")
+        
+        self.widget = wx.CheckBox(parent = self.statusbar, id = wx.ID_ANY,
+                                  label = _("Show computational extent"))
+        
+        self.widget.SetValue(False)
+        self.widget.Hide()
+        self.widget.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.widget.Bind(wx.EVT_CHECKBOX, self.OnToggleShowRegion)
+    
+    def OnToggleShowRegion(self, event):
+        """!Shows/Hides extent (comp. region) in map canvas.
+        
+        Shows or hides according to checkbox value.
+        """
+        if self.widget.GetValue():
+            # show extent
+            self.mapFrame.GetWindow().regionCoords = []
+        elif hasattr(self.mapFrame.GetWindow(), 'regionCoords'):
+            del self.mapFrame.GetWindow().regionCoords
+
+        # redraw map if auto-rendering is enabled
+        if self.mapFrame.IsAutoRendered():
+            self.mapFrame.OnRender(None)
+
+    def SetValue(self, value):
+        SbItem.SetValue(self, value)
+        if value:
+            self.mapFrame.GetWindow().regionCoords = []
+        elif hasattr(self.mapFrame.GetWindow(), 'regionCoords'):
+            del self.mapFrame.GetWindow().regionCoords
+            
+class SbAlignExtent(SbItem):
+    """!Checkbox to select zoom behavior.
+    
+    Used by BufferedWindow (through MapFrame property).
+    See tooltip for explanation.
+    """
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'alignExtent'
+        self.label = _("Display mode")
+        
+        self.widget = wx.CheckBox(parent = self.statusbar, id = wx.ID_ANY,
+                                  label = _("Align region extent based on display size"))
+        
+        self.widget.SetValue(UserSettings.Get(group = 'display', key = 'alignExtent', subkey = 'enabled'))
+        self.widget.Hide()
+        self.widget.SetToolTip(wx.ToolTip (_("Align region extent based on display "
+                                             "size from center point. "
+                                             "Default value for new map displays can "
+                                             "be set up in 'User GUI settings' dialog.")))      
+        
+class SbResolution(SbItem):
+    """!Checkbox to select used display resolution.
+    
+    Requires MapFrame.OnRender method. 
+    """
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'resolution'
+        self.label = _("Display resolution")
+        
+        self.widget = wx.CheckBox(parent = self.statusbar, id = wx.ID_ANY,
+                                  label = _("Constrain display resolution to computational settings"))
+        
+        self.widget.SetValue(UserSettings.Get(group = 'display', key = 'compResolution', subkey = 'enabled'))
+        self.widget.Hide()
+        self.widget.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.widget.Bind(wx.EVT_CHECKBOX, self.OnToggleUpdateMap)
+        
+    def OnToggleUpdateMap(self, event):
+        """!Update display when toggle display mode
+        """
+        # redraw map if auto-rendering is enabled
+        if self.mapFrame.IsAutoRendered():
+            self.mapFrame.OnRender(None)
+
+
+class SbMapScale(SbItem):
+    """!Editable combobox to get/set current map scale.
+    
+    Requires MapFrame.GetMapScale, MapFrame.SetMapScale
+    and MapFrame.GetWindow (and GetWindow().UpdateMap()).
+    """
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'mapscale'
+        self.label = _("Map scale")
+        
+        self.widget = wx.ComboBox(parent = self.statusbar, id = wx.ID_ANY,
+                                                    style = wx.TE_PROCESS_ENTER,
+                                                    size = (150, -1))
+        
+        self.widget.SetItems(['1:1000',
+                              '1:5000',
+                              '1:10000',
+                              '1:25000',
+                              '1:50000',
+                              '1:100000',
+                              '1:1000000'])
+        self.widget.Hide()
+        self.widget.SetToolTip(wx.ToolTip (_("As everyone's monitors and resolutions "
+                                            "are set differently these values are not "
+                                            "true map scales, but should get you into "
+                                            "the right neighborhood.")))
+                                            
+        self.widget.Bind(wx.EVT_TEXT_ENTER, self.OnChangeMapScale)
+        self.widget.Bind(wx.EVT_COMBOBOX, self.OnChangeMapScale)
+        
+        self.lastMapScale = None
+
+    def Update(self):
+        scale = self.mapFrame.GetMapScale()
+        self.statusbar.SetStatusText("")
+        try:
+            self.SetValue("1:%ld" % (scale + 0.5))
+        except TypeError:
+            pass # FIXME, why this should happen?
+        
+        self.lastMapScale = scale
+        self.Show()
+
+        # disable long help
+        self.mapFrame.StatusbarEnableLongHelp(False)
+
+    def OnChangeMapScale(self, event):
+        """!Map scale changed by user
+        """
+        scale = event.GetString()
+
+        try:
+            if scale[:2] != '1:':
+                raise ValueError
+            value = int(scale[2:])
+        except ValueError:
+            self.SetValue('1:%ld' % int(self.lastMapScale))
+            return
+        
+        self.mapFrame.SetMapScale(value)
+        
+        # redraw a map
+        self.mapFrame.GetWindow().UpdateMap()
+        self.GetWidget().SetFocus()
+        
+        
+class SbGoTo(SbItem):
+    """!Textctrl to set coordinates which to focus on.
+    
+    Requires MapFrame.GetWindow, MapWindow.GoTo method.
+    """
+    
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'goto'
+        self.label = _("Go to")
+        
+        self.widget = wx.TextCtrl(parent = self.statusbar, id = wx.ID_ANY,
+                                                value = "", style = wx.TE_PROCESS_ENTER,
+                                                size = (300, -1))
+        
+        self.widget.Hide()
+        
+        self.widget.Bind(wx.EVT_TEXT_ENTER, self.OnGoTo)
+    
+    def ReprojectENToMap(self, e, n, useDefinedProjection):
+        """!Reproject east, north from user defined projection
+        
+        @param e,n coordinate (for DMS string, else float or string)
+        @param useDefinedProjection projection defined by user in settings dialog
+        
+        @throws SbException if useDefinedProjection is True and projection is not defined in UserSettings
+        """
+        if useDefinedProjection:
+            settings = UserSettings.Get(group = 'projection', key = 'statusbar', subkey = 'proj4')
+            if not settings:
+                raise SbException(_("Projection not defined (check the settings)"))
+            else:
+                # reproject values
+                projIn = settings
+                projOut = RunCommand('g.proj',
+                                     flags = 'jf',
+                                     read = True)
+                proj = projIn.split(' ')[0].split('=')[1]
+                if proj in ('ll', 'latlong', 'longlat'):
+                    e, n = utils.DMS2Deg(e, n)
+                    proj, coord1 = utils.ReprojectCoordinates(coord = (e, n),
+                                                              projIn = projIn,
+                                                              projOut = projOut, flags = 'd')
+                    e, n = coord1
+                else:
+                    e, n = float(e), float(n)
+                    proj, coord1 = utils.ReprojectCoordinates(coord = (e, n),
+                                                              projIn = projIn,
+                                                              projOut = projOut, flags = 'd')
+                    e, n = coord1
+        elif self.mapFrame.GetMap().projinfo['proj'] == 'll':
+            e, n = utils.DMS2Deg(e, n)
+        else: 
+            e, n = float(e), float(n)
+        return e, n
+
+    def OnGoTo(self, event):
+        """!Go to position
+        """
+        try:
+            e, n = self.GetValue().split(';')
+            e, n = self.ReprojectENToMap(e, n, self.mapFrame.GetProperty('projection'))
+            self.mapFrame.GetWindow().GoTo(e, n)
+            self.widget.SetFocus()
+        except ValueError:
+            # FIXME: move this code to MapWindow/BufferedWindow/MapFrame
+            region = self.mapFrame.GetMap().GetCurrentRegion()
+            precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                                             subkey = 'precision'))
+            format = UserSettings.Get(group = 'projection', key = 'format',
+                                      subkey = 'll')
+            if self.mapFrame.GetMap().projinfo['proj'] == 'll' and format == 'DMS':
+                    self.SetValue("%s" % utils.Deg2DMS(region['center_easting'], 
+                                                                            region['center_northing'],
+                                                                            precision = precision))
+            else:
+                self.SetValue("%.*f; %.*f" % \
+                               (precision, region['center_easting'],
+                                precision, region['center_northing']))
+        except SbException, e:
+            # FIXME: this may be useless since statusbar update checks user defined projection and this exception raises when user def proj does not exists
+            self.statusbar.SetStatusText(str(e), 0)
+
+    def GetCenterString(self, map):
+        """!Get current map center in appropriate format"""
+        region = map.GetCurrentRegion()
+        precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                                         subkey = 'precision'))
+        format = UserSettings.Get(group = 'projection', key = 'format',
+                                  subkey = 'll')
+        projection = UserSettings.Get(group='projection', key='statusbar', subkey='proj4')
+        
+        if self.mapFrame.GetProperty('projection'):
+            if not projection:
+                raise SbException(_("Projection not defined (check the settings)"))
+            else:
+                proj, coord  = utils.ReprojectCoordinates(coord = (region['center_easting'],
+                                                                   region['center_northing']),
+                                                          projOut = projection,
+                                                          flags = 'd')
+                if coord:
+                    if proj in ('ll', 'latlong', 'longlat') and format == 'DMS':
+                        return "%s" % utils.Deg2DMS(coord[0],
+                                                                                coord[1],
+                                                                                precision = precision)
+                    else:
+                        return "%.*f; %.*f" % (precision, coord[0], precision, coord[1])
+                else:
+                    raise SbException(_("Error in projection (check the settings)"))
+        else:
+            if self.mapFrame.GetMap().projinfo['proj'] == 'll' and format == 'DMS':
+                return "%s" % utils.Deg2DMS(region['center_easting'], region['center_northing'],
+                                                                      precision = precision)
+            else:
+                return "%.*f; %.*f" % (precision, region['center_easting'], precision, region['center_northing'])
+
+
+    def SetCenter(self):
+        """!Set current map center as item value"""
+        center = self.GetCenterString(self.mapFrame.GetMap())
+        self.SetValue(center)
+        
+    def Update(self):
+        self.statusbar.SetStatusText("")
+        
+        try:
+            self.SetCenter()
+            self.Show()
+        except SbException, e:
+            self.statusbar.SetStatusText(str(e), 0)
+                        
+        # disable long help
+        self.mapFrame.StatusbarEnableLongHelp(False)
+        
+
+class SbProjection(SbItem):
+    """!Checkbox to enable user defined projection (can be set in settings)"""
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'projection'
+        self.label = _("Projection")
+        
+        self.defaultLabel = _("Use defined projection")
+        
+        self.widget = wx.CheckBox(parent = self.statusbar, id = wx.ID_ANY,
+                                  label = self.defaultLabel)
+        
+        self.widget.SetValue(False)
+        
+        # necessary?
+        size = self.widget.GetSize()
+        self.widget.SetMinSize((size[0] + 150, size[1]))
+        
+        self.widget.Hide()
+        self.widget.SetToolTip(wx.ToolTip (_("Reproject coordinates displayed "
+                                             "in the statusbar. Projection can be "
+                                             "defined in GUI preferences dialog "
+                                             "(tab 'Projection')")))
+                                            
+    def Update(self):
+        self.statusbar.SetStatusText("")
+        epsg = UserSettings.Get(group = 'projection', key = 'statusbar', subkey = 'epsg')
+        if epsg:
+            label = '%s (EPSG: %s)' % (self.defaultLabel, epsg)
+            self.widget.SetLabel(label)
+        else:
+            self.widget.SetLabel(self.defaultLabel)
+        self.Show()
+        
+        # disable long help
+        self.mapFrame.StatusbarEnableLongHelp(False)
+        
+
+class SbMask(SbItem):
+    """!StaticText to show whether mask is activated."""
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'mask'
+        
+        self.widget = wx.StaticText(parent = self.statusbar, id = wx.ID_ANY, label = _('MASK'))
+        self.widget.SetForegroundColour(wx.Colour(255, 0, 0))
+        self.widget.Hide()
+        
+    def Update(self):
+        if grass.find_file(name = 'MASK', element = 'cell')['name']:
+            self.Show()
+        else:
+            self.Hide()
+        
+class SbTextItem(SbItem):
+    """!Base class for items without widgets.
+    
+    Only sets statusbar text.
+    """
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbItem.__init__(self, mapframe, statusbar, position)
+        
+        self.text = None
+        
+    def Show(self):
+        self.statusbar.SetStatusText(self.GetValue(), self.position)
+        
+    def Hide(self):
+        self.statusbar.SetStatusText("", self.position)
+        
+    def SetValue(self, value):
+        self.text = value
+    
+    def GetValue(self):
+        return self.text
+            
+    def GetWidget(self):
+        return None
+    
+    def Update(self):
+        self._update(longHelp = True)
+
+class SbDisplayGeometry(SbTextItem):
+    """!Show current display resolution."""
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbTextItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'displayGeometry'
+        self.label = _("Display geometry")
+        
+    def Show(self):
+        region = self.mapFrame.GetMap().GetCurrentRegion()
+        self.SetValue("rows=%d; cols=%d; nsres=%.2f; ewres=%.2f" %
+                     (region["rows"], region["cols"],
+                      region["nsres"], region["ewres"]))
+        SbTextItem.Show(self)
+
+class SbCoordinates(SbTextItem):
+    """!Show map coordinates when mouse moves.
+    
+    Requires MapWindow.GetLastEN method."""
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbTextItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'coordinates'
+        self.label = _("Coordinates")
+        
+    def Show(self):
+        precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                             subkey = 'precision'))
+        format = UserSettings.Get(group = 'projection', key = 'format',
+                                       subkey = 'll')
+        projection = self.mapFrame.GetProperty('projection')
+        try:
+            e, n = self.mapFrame.GetWindow().GetLastEN()
+            self.SetValue(self.ReprojectENFromMap(e, n, projection, precision, format))
+        except SbException, e:
+            self.SetValue(e)
+        except TypeError, e:
+            self.SetValue("")
+        except AttributeError:
+            self.SetValue("") # during initialization MapFrame has no MapWindow
+        SbTextItem.Show(self)
+        
+    def ReprojectENFromMap(self, e, n, useDefinedProjection, precision, format):
+        """!Reproject east, north to user defined projection.
+        
+        @param e,n coordinate
+        
+        @throws SbException if useDefinedProjection is True and projection is not defined in UserSettings
+        """
+        if useDefinedProjection:
+            settings = UserSettings.Get(group = 'projection', key = 'statusbar', subkey = 'proj4')
+            if not settings:
+                raise SbException(_("Projection not defined (check the settings)"))
+            else:
+                # reproject values
+                proj, coord  = utils.ReprojectCoordinates(coord = (e, n),
+                                                          projOut = settings,
+                                                          flags = 'd')
+                if coord:
+                    e, n = coord
+                    if proj in ('ll', 'latlong', 'longlat') and format == 'DMS':
+                        return utils.Deg2DMS(e, n, precision = precision)
+                    else:
+                        return "%.*f; %.*f" % (precision, e, precision, n)
+                else:
+                    raise SbException(_("Error in projection (check the settings)"))
+        else:
+            if self.mapFrame.GetMap().projinfo['proj'] == 'll' and format == 'DMS':
+                return utils.Deg2DMS(e, n, precision = precision)
+            else:
+                return "%.*f; %.*f" % (precision, e, precision, n)
+        
+class SbRegionExtent(SbTextItem):
+    """!Shows current display region"""
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbTextItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'displayRegion'
+        self.label = _("Extent")
+        
+    def Show(self):
+        precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                             subkey = 'precision'))
+        format = UserSettings.Get(group = 'projection', key = 'format',
+                                       subkey = 'll')
+        projection = self.mapFrame.GetProperty('projection')        
+        region = self._getRegion()
+        try:
+            regionReprojected = self.ReprojectRegionFromMap(region, projection, precision, format)
+            self.SetValue(regionReprojected)
+        except SbException, e:
+            self.SetValue(e)
+        SbTextItem.Show(self)
+    
+    def _getRegion(self):
+        """!Get current display region"""
+        return self.mapFrame.GetMap().GetCurrentRegion() # display region
+        
+    def _formatRegion(self, w, e, s, n, nsres, ewres, precision = None):
+        """!Format display region string for statusbar
+
+        @param nsres,ewres unused
+        """
+        if precision is not None:
+            return "%.*f - %.*f, %.*f - %.*f" % (precision, w, precision, e,
+                                                 precision, s, precision, n)
+        else:
+            return "%s - %s, %s - %s" % (w, e, s, n)
+         
+           
+    def ReprojectRegionFromMap(self, region, useDefinedProjection, precision, format):
+        """!Reproject region values
+        
+        @todo reorganize this method to remove code useful only for derived class SbCompRegionExtent
+        """
+        if useDefinedProjection:
+            settings = UserSettings.Get(group = 'projection', key = 'statusbar', subkey = 'proj4')
+            
+            if not settings:
+                raise SbException(_("Projection not defined (check the settings)"))
+            else:
+                projOut = settings
+                proj, coord1 = utils.ReprojectCoordinates(coord = (region["w"], region["s"]),
+                                                          projOut = projOut, flags = 'd')
+                proj, coord2 = utils.ReprojectCoordinates(coord = (region["e"], region["n"]),
+                                                          projOut = projOut, flags = 'd')
+                # useless, used in derived class
+                proj, coord3 = utils.ReprojectCoordinates(coord = (0.0, 0.0),
+                                                          projOut = projOut, flags = 'd')
+                proj, coord4 = utils.ReprojectCoordinates(coord = (region["ewres"], region["nsres"]),
+                                                          projOut = projOut, flags = 'd')
+                if coord1 and coord2:
+                    if proj in ('ll', 'latlong', 'longlat') and format == 'DMS':
+                        w, s = utils.Deg2DMS(coord1[0], coord1[1], string = False,
+                                             precision = precision)
+                        e, n = utils.Deg2DMS(coord2[0], coord2[1], string = False,
+                                             precision = precision)
+                        ewres, nsres = utils.Deg2DMS(abs(coord3[0]) - abs(coord4[0]),
+                                                         abs(coord3[1]) - abs(coord4[1]),
+                                                         string = False, hemisphere = False,
+                                                         precision = precision)
+                        return self._formatRegion(w = w, s = s, e = e, n = n, ewres = ewres, nsres = nsres)
+                    else:
+                        w, s = coord1
+                        e, n = coord2
+                        ewres, nsres = coord3
+                        return self._formatRegion(w = w, s = s, e = e, n = n, ewres = ewres,
+                                                  nsres = nsres, precision = precision)
+                else:
+                    raise SbException(_("Error in projection (check the settings)"))
+                
+        else:
+            if self.mapFrame.GetMap().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)
+                ewres, nsres = utils.Deg2DMS(region['ewres'], region['nsres'],
+                                             string = False, precision = precision)
+                return self._formatRegion(w = w, s = s, e = e, n = n, ewres = ewres, nsres = nsres)
+            else:
+                w, s = region["w"], region["s"]
+                e, n = region["e"], region["n"]
+                ewres, nsres = region['ewres'], region['nsres']
+                return self._formatRegion(w = w, s = s, e = e, n = n, ewres = ewres,
+                                          nsres = nsres, precision = precision)
+                                
+                                
+class SbCompRegionExtent(SbRegionExtent):
+    """!Shows computational region."""
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbRegionExtent.__init__(self, mapframe, statusbar, position)
+        self.name = 'computationalRegion'
+        self.label = _("Comp. region")
+        
+    def _formatRegion(self, w, e, s, n, ewres, nsres, precision = None):
+        """!Format computational region string for statusbar"""
+        if precision is not None:
+            return "%.*f - %.*f, %.*f - %.*f (%.*f, %.*f)" % (precision, w, precision, e,
+                                                              precision, s, precision, n,
+                                                              precision, ewres, precision, nsres)
+        else:
+            return "%s - %s, %s - %s (%s, %s)" % (w, e, s, n, ewres, nsres)
+        
+    def _getRegion(self):
+        """!Returns computational region."""
+        return self.mapFrame.GetMap().GetRegion() # computational region
+        
+        
+class SbProgress(SbItem):
+    """!General progress bar to show progress.
+    
+    Underlaying widget is wx.Gauge.
+    """
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'progress'
+
+        # on-render gauge
+        self.widget = wx.Gauge(parent = self.statusbar, id = wx.ID_ANY,
+                                      range = 0, style = wx.GA_HORIZONTAL)
+        self.widget.Hide()
+        
+    def GetRange(self):
+        """!Returns progress range."""
+        return self.widget.GetRange()
+    
+    def SetRange(self, range):
+        """!Sets progress range."""
+        self.widget.SetRange(range)
+    
+
+class SbGoToGCP(SbItem):
+    """!SpinCtrl to select GCP to focus on
+    
+    Requires MapFrame.GetSrcWindow, MapFrame.GetTgtWindow, MapFrame.GetListCtrl,
+    MapFrame.GetMapCoordList.
+    """
+    
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'gotoGCP'
+        self.label = _("Go to GCP No.")
+
+        self.widget = wx.SpinCtrl(parent = self.statusbar, id = wx.ID_ANY,
+                                                value = "", min = 0)
+        self.widget.Hide()
+        
+        self.widget.Bind(wx.EVT_TEXT_ENTER, self.OnGoToGCP)
+        self.widget.Bind(wx.EVT_SPINCTRL, self.OnGoToGCP)
+    
+    def OnGoToGCP(self, event):
+        """!Zooms to given GCP."""
+        GCPNo = self.GetValue()
+        mapCoords = self.mapFrame.GetMapCoordList()
+        
+        if GCPNo < 0 or GCPNo > len(mapCoords): # always false, spin checks it
+            GMessage(parent = self,
+                     message = "%s 1 - %s." % (_("Valid Range:"),
+                                               len(mapCoords)))
+            return
+
+        if GCPNo == 0:
+            return
+            
+        listCtrl = self.mapFrame.GetListCtrl()
+        
+        listCtrl.selectedkey = GCPNo
+        listCtrl.selected = listCtrl.FindItemData(-1, GCPNo)
+        listCtrl.render = False
+        listCtrl.SetItemState(listCtrl.selected,
+                          wx.LIST_STATE_SELECTED,
+                          wx.LIST_STATE_SELECTED)
+        listCtrl.render = True
+        
+        srcWin = self.mapFrame.GetSrcWindow()
+        tgtWin = self.mapFrame.GetTgtWindow()
+        
+        # Source MapWindow:
+        begin = (mapCoords[GCPNo][1], mapCoords[GCPNo][2])
+        begin = srcWin.Cell2Pixel(begin)
+        end = begin
+        srcWin.Zoom(begin, end, 0)
+
+        # redraw map
+        srcWin.UpdateMap()
+
+        if self.mapFrame.GetShowTarget():
+            # Target MapWindow:
+            begin = (mapCoords[GCPNo][3], mapCoords[GCPNo][4])
+            begin = tgtWin.Cell2Pixel(begin)
+            end = begin
+            tgtWin.Zoom(begin, end, 0)
+
+            # redraw map
+            tgtWin.UpdateMap()
+
+        self.GetWidget().SetFocus()
+    
+    def Update(self):
+        self.statusbar.SetStatusText("")
+        max = self.mapFrame.GetListCtrl().GetItemCount()
+        if max < 1:
+            max = 1
+        self.widget.SetRange(0, max)
+        self.Show()
+                        
+        # disable long help
+        self.mapFrame.StatusbarEnableLongHelp(False)
+        
+class SbRMSError(SbTextItem):
+    """!Shows RMS error.
+    
+    Requires MapFrame.GetFwdError, MapFrame.GetBkwError.
+    """
+    def __init__(self, mapframe, statusbar, position = 0):
+        SbTextItem.__init__(self, mapframe, statusbar, position)
+        self.name = 'RMSError'
+        self.label = _("RMS error")
+        
+    def Show(self):
+        self.SetValue(_("Forward: %(forw)s, Backward: %(back)s") %
+                                   { 'forw' : self.mapFrame.GetFwdError(),
+                                     'back' : self.mapFrame.GetBkwError() })
+        SbTextItem.Show(self)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/toolbars.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/toolbars.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/mapdisp/toolbars.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,267 @@
+"""!
+ at package mapdisp.toolbars
+
+ at brief Map display frame - toolbars
+
+Classes:
+ - toolbars::MapToolbar
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import wx
+
+from gui_core.toolbars import BaseToolbar, BaseIcons
+from nviz.main         import haveNviz
+from vdigit.main       import haveVDigit
+from icons.icon        import MetaIcon
+
+MapIcons =  {
+    'query'      : MetaIcon(img = 'info',
+                            label = _('Query raster/vector map(s)'),
+                            desc = _('Query selected raster/vector map(s)')),
+    'addBarscale': MetaIcon(img = 'scalebar-add',
+                            label = _('Add scalebar and north arrow')),
+    'addLegend'  : MetaIcon(img = 'legend-add',
+                            label = _('Add legend')),
+    'addNorthArrow': MetaIcon(img = 'north-arrow-add',
+                              label = _('North Arrow')),
+    'analyze'    : MetaIcon(img = 'layer-raster-analyze',
+                            label = _('Analyze map'),
+                            desc = _('Measuring, profiling, histogramming, ...')),
+    'measure'    : MetaIcon(img = 'measure-length',
+                            label = _('Measure distance')),
+    'profile'    : MetaIcon(img = 'layer-raster-profile',
+                            label = _('Profile surface map')),
+    'scatter'    : MetaIcon(img = 'layer-raster-profile',
+                            label = _("Create bivariate scatterplot of raster maps")),
+    'addText'    : MetaIcon(img = 'text-add',
+                            label = _('Add text layer')),
+    'histogram'  : MetaIcon(img = 'layer-raster-histogram',
+                            label = _('Create histogram of raster map')),
+    }
+
+NvizIcons = {
+    'rotate'    : MetaIcon(img = '3d-rotate',
+                           label = _('Rotate 3D scene'),
+                           desc = _('Drag with mouse to rotate 3D scene')), 
+    'flyThrough': MetaIcon(img = 'flythrough',
+                           label = _('Fly-through mode'),
+                           desc = _('Drag with mouse, hold Ctrl down for different mode'
+                                    ' or Shift to accelerate')),
+    'zoomIn'    : BaseIcons['zoomIn'].SetLabel(desc = _('Click mouse to zoom')),
+    'zoomOut'   : BaseIcons['zoomOut'].SetLabel(desc = _('Click mouse to unzoom'))
+    }
+
+class MapToolbar(BaseToolbar):
+    """!Map Display toolbar
+    """
+    def __init__(self, parent, mapcontent):
+        """!Map Display constructor
+
+        @param parent reference to MapFrame
+        @param mapcontent reference to render.Map (registred by MapFrame)
+        """
+        self.mapcontent = mapcontent # render.Map
+        BaseToolbar.__init__(self, parent = parent) # MapFrame
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # optional tools
+        choices = [ _('2D view'), ]
+        self.toolId = { '2d' : 0 }
+        if self.parent.GetLayerManager():
+            log = self.parent.GetLayerManager().GetLogWindow()
+        
+        if haveNviz:
+            choices.append(_('3D view'))
+            self.toolId['3d'] = 1
+        else:
+            from nviz.main import errorMsg
+            log.WriteCmdLog(_('3D view mode not available'))
+            log.WriteWarning(_('Reason: %s') % str(errorMsg))
+            log.WriteLog(_('Note that the wxGUI\'s 3D view mode is currently disabled '
+                           'on MS Windows (hopefully this will be fixed soon). '
+                           'Please keep an eye out for updated versions of GRASS. '
+                           'In the meantime you can use "NVIZ" from the File menu.'), wrap = 60)
+            
+            self.toolId['3d'] = -1
+
+        if haveVDigit:
+            choices.append(_('Digitize'))
+            if self.toolId['3d'] > -1:
+                self.toolId['vdigit'] = 2
+            else:
+                self.toolId['vdigit'] = 1
+        else:
+            from vdigit.main import errorMsg
+            log.WriteCmdLog(_('Vector digitizer not available'))
+            log.WriteWarning(_('Reason: %s') % errorMsg)
+            log.WriteLog(_('Note that the wxGUI\'s vector digitizer is currently disabled '
+                           '(hopefully this will be fixed soon). '
+                           'Please keep an eye out for updated versions of GRASS. '
+                           'In the meantime you can use "v.digit" from the Develop Vector menu.'), wrap = 60)
+            
+            self.toolId['vdigit'] = -1
+        
+        self.combo = wx.ComboBox(parent = self, id = wx.ID_ANY,
+                                 choices = choices,
+                                 style = wx.CB_READONLY, size = (110, -1))
+        self.combo.SetSelection(0)
+        
+        self.comboid = self.AddControl(self.combo)
+        self.parent.Bind(wx.EVT_COMBOBOX, self.OnSelectTool, self.comboid)
+        
+        # realize the toolbar
+        self.Realize()
+        
+        # workaround for Mac bug. May be fixed by 2.8.8, but not before then.
+        self.combo.Hide()
+        self.combo.Show()
+        
+        self.action = { 'id' : self.pointer }
+        self.defaultAction = { 'id' : self.pointer,
+                               'bind' : self.parent.OnPointer }
+        
+        self.OnTool(None)
+        
+        self.EnableTool(self.zoomBack, False)
+        
+        self.FixSize(width = 90)
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        return self._getToolbarData((('displayMap', BaseIcons['display'],
+                                      self.parent.OnDraw),
+                                     ('renderMap', BaseIcons['render'],
+                                      self.parent.OnRender),
+                                     ('erase', BaseIcons['erase'],
+                                      self.parent.OnErase),
+                                     (None, ),
+                                     ('pointer', BaseIcons['pointer'],
+                                      self.parent.OnPointer,
+                                      wx.ITEM_CHECK),
+                                     ('query', MapIcons['query'],
+                                      self.parent.OnQuery,
+                                      wx.ITEM_CHECK),
+                                     ('pan', BaseIcons['pan'],
+                                      self.parent.OnPan,
+                                      wx.ITEM_CHECK),
+                                     ('zoomIn', BaseIcons['zoomIn'],
+                                      self.parent.OnZoomIn,
+                                      wx.ITEM_CHECK),
+                                     ('zoomOut', BaseIcons['zoomOut'],
+                                      self.parent.OnZoomOut,
+                                      wx.ITEM_CHECK),
+                                     ('zoomExtent', BaseIcons['zoomExtent'],
+                                      self.parent.OnZoomToMap),
+                                     ('zoomBack', BaseIcons['zoomBack'],
+                                      self.parent.OnZoomBack),
+                                     ('zoomMenu', BaseIcons['zoomMenu'],
+                                      self.parent.OnZoomMenu),
+                                     (None, ),
+                                     ('analyze', MapIcons['analyze'],
+                                      self.OnAnalyze),
+                                     (None, ),
+                                     ('overlay', BaseIcons['overlay'],
+                                      self.OnDecoration),
+                                     (None, ),
+                                     ('saveFile', BaseIcons['saveFile'],
+                                      self.parent.SaveToFile),
+                                     ('printMap', BaseIcons['print'],
+                                      self.parent.PrintMenu),
+                                     (None, ))
+                                    )
+    def InsertTool(self, data):
+        """!Insert tool to toolbar
+        
+        @param data toolbar data"""
+        data = self._getToolbarData(data)
+        for tool in data:
+            self.CreateTool(*tool)
+        self.Realize()
+        
+        self.parent._mgr.GetPane('mapToolbar').BestSize(self.GetBestSize())
+        self.parent._mgr.Update()
+        
+    def RemoveTool(self, tool):
+        """!Remove tool from toolbar
+        
+        @param tool tool id"""
+        self.DeleteTool(tool)
+        
+        self.parent._mgr.GetPane('mapToolbar').BestSize(self.GetBestSize())
+        self.parent._mgr.Update()
+        
+    def ChangeToolsDesc(self, mode2d):
+        """!Change description of zoom tools for 2D/3D view"""
+        if mode2d:
+            icons = BaseIcons
+        else:
+            icons = NvizIcons
+        for i, data in enumerate(self._data):
+            for tool in (('zoomIn', 'zoomOut')):
+                if data[0] == tool:
+                    tmp = list(data)
+                    tmp[4] = icons[tool].GetDesc()
+                    self._data[i] = tuple(tmp)
+        
+    def OnSelectTool(self, event):
+        """!Select / enable tool available in tools list
+        """
+        tool =  event.GetSelection()
+        
+        if tool == self.toolId['2d']:
+            self.ExitToolbars()
+            self.Enable2D(True)
+            self.ChangeToolsDesc(mode2d = True)            
+        
+        elif tool == self.toolId['3d'] and \
+                not (self.parent.MapWindow3D and self.parent.IsPaneShown('3d')):
+            self.ExitToolbars()
+            self.parent.AddNviz()
+            
+        elif tool == self.toolId['vdigit'] and \
+                not self.parent.GetToolbar('vdigit'):
+            self.ExitToolbars()
+            self.parent.AddToolbar("vdigit")
+            self.parent.MapWindow.SetFocus()
+
+    def OnAnalyze(self, event):
+        """!Analysis tools menu
+        """
+        self._onMenu(((MapIcons["measure"],    self.parent.OnMeasure),
+                      (MapIcons["profile"],    self.parent.OnProfile),
+                      (MapIcons["histogram"], self.parent.OnHistogram)))
+        
+    def OnDecoration(self, event):
+        """!Decorations overlay menu
+        """
+        if self.parent.IsPaneShown('3d'):
+            self._onMenu(((MapIcons["addNorthArrow"], self.parent.OnAddArrow),
+                          (MapIcons["addLegend"],     self.parent.OnAddLegend),
+                          (MapIcons["addText"],       self.parent.OnAddText)))
+        else:
+            self._onMenu(((MapIcons["addBarscale"], self.parent.OnAddBarscale),
+                          (MapIcons["addLegend"],   self.parent.OnAddLegend),
+                          (MapIcons["addText"],     self.parent.OnAddText)))
+        
+    def ExitToolbars(self):
+        if self.parent.GetToolbar('vdigit'):
+            self.parent.toolbars['vdigit'].OnExit()
+        if self.parent.GetLayerManager().IsPaneShown('toolbarNviz'):
+            self.parent.RemoveNviz()
+        
+    def Enable2D(self, enabled):
+        """!Enable/Disable 2D display mode specific tools"""
+        for tool in (self.zoomMenu,
+                     self.analyze,
+                     self.printMap):
+            self.EnableTool(tool, enabled)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/modules/colorrules.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/modules/colorrules.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/modules/colorrules.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1796 @@
+"""
+ at package module.colorrules
+
+ at brief Dialog for interactive management of raster/vector color tables
+and color rules.
+
+Classes:
+ - colorrules::RulesPanel
+ - colorrules::ColorTable
+ - colorrules::RasterColorTable
+ - colorrules::VectorColorTable
+ - colorrules::BufferedWindow
+
+(C) 2008, 2010-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton (Arizona State University)
+ at author Martin Landa <landa.martin gmail.com> (various updates)
+ at author Anna Kratochvilova <kratochanna gmail.com> (split to base and derived classes)
+"""
+
+import os
+import shutil
+import copy
+import tempfile
+
+import wx
+import wx.lib.colourselect     as csel
+import wx.lib.scrolledpanel    as scrolled
+import wx.lib.filebrowsebutton as filebrowse
+
+import grass.script as grass
+
+from core             import globalvar
+from core             import utils
+from core.gcmd        import GMessage, RunCommand, GError
+from gui_core.gselect import Select, LayerSelect, ColumnSelect, VectorDBInfo
+from core.render      import Map
+from gui_core.forms   import GUI
+from core.debug       import Debug as Debug
+from core.settings    import UserSettings
+
+class RulesPanel:
+    def __init__(self, parent, mapType, attributeType, properties, panelWidth = 180):
+        """!Create rules panel
+        
+        @param mapType raster/vector
+        @param attributeType color/size for choosing widget type
+        @param properties properties of classes derived from ColorTable
+        @param panelWidth width of scroll panel"""
+        
+        self.ruleslines = {}
+        self.mapType = mapType
+        self.attributeType = attributeType
+        self.properties = properties
+        self.parent = parent
+        self.panelWidth = panelWidth
+        
+        self.mainSizer = wx.FlexGridSizer(cols = 3, vgap = 6, hgap = 4)
+        # put small border at the top of panel
+        for i in range(3):
+            self.mainSizer.Add(item = wx.Size(3, 3))
+        
+        self.mainPanel = scrolled.ScrolledPanel(parent, id = wx.ID_ANY,
+                                          size = (self.panelWidth, 300),
+                                          style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
+                
+        # (un)check all
+        self.checkAll = wx.CheckBox(parent, id = wx.ID_ANY, label = _("Check all"))
+        self.checkAll.SetValue(True)
+        # clear button
+        self.clearAll = wx.Button(parent, id = wx.ID_ANY, label = _("Clear all"))
+        #  determines how many rules should be added
+        self.numRules = wx.SpinCtrl(parent, id = wx.ID_ANY,
+                                    min = 1, max = 1e6, initial = 1)
+        # add rules
+        self.btnAdd = wx.Button(parent, id = wx.ID_ADD)
+        
+        self.btnAdd.Bind(wx.EVT_BUTTON, self.OnAddRules)
+        self.checkAll.Bind(wx.EVT_CHECKBOX, self.OnCheckAll)
+        self.clearAll.Bind(wx.EVT_BUTTON, self.OnClearAll)
+
+        self.mainPanel.SetSizer(self.mainSizer)
+        self.mainPanel.SetAutoLayout(True)
+        self.mainPanel.SetupScrolling()    
+    
+    def Clear(self):
+        """!Clear and widgets and delete information"""
+        self.ruleslines.clear()
+        self.mainSizer.Clear(deleteWindows=True)
+    
+    def OnCheckAll(self, event):
+        """!(Un)check all rules"""
+        check = event.GetInt()
+        for child in self.mainPanel.GetChildren():
+            if child.GetName() == 'enable':
+                child.SetValue(check)
+            else:
+                child.Enable(check)
+                
+    def OnClearAll(self, event):
+        """!Delete all widgets in panel"""
+        self.Clear()
+        
+    def OnAddRules(self, event):
+        """!Add rules button pressed"""
+        nrules = self.numRules.GetValue()
+        self.AddRules(nrules)
+        
+    def AddRules(self, nrules, start = False):
+        """!Add rules 
+        @param start set widgets (not append)"""
+       
+        snum = len(self.ruleslines.keys())
+        if start:
+            snum = 0
+        for num in range(snum, snum + nrules):
+            # enable
+            enable = wx.CheckBox(parent = self.mainPanel, id = num)
+            enable.SetValue(True)
+            enable.SetName('enable')
+            enable.Bind(wx.EVT_CHECKBOX, self.OnRuleEnable)
+            # value
+            txt_ctrl = wx.TextCtrl(parent = self.mainPanel, id = 1000 + num,
+                                   size = (80, -1),
+                                   style = wx.TE_NOHIDESEL)
+            if self.mapType == 'vector':
+                txt_ctrl.SetToolTipString(_("Enter vector attribute values"))
+            txt_ctrl.Bind(wx.EVT_TEXT, self.OnRuleValue)
+            txt_ctrl.SetName('source')
+            if self.attributeType == 'color':
+                # color
+                columnCtrl = csel.ColourSelect(self.mainPanel, id = 2000 + num,
+                                               size  =  globalvar.DIALOG_COLOR_SIZE)
+                columnCtrl.Bind(csel.EVT_COLOURSELECT, self.OnRuleColor)
+                columnCtrl.SetName('target')
+                if not start:
+                    self.ruleslines[enable.GetId()] = { 'value' : '',
+                                                        'color': "0:0:0" }
+            else:
+                # size or width
+                init = 2
+                if self.attributeType == 'size':
+                    init = 100
+                columnCtrl = wx.SpinCtrl(self.mainPanel, id = 2000 + num,
+                                         size = (50, -1), min = 1, max = 1e4,
+                                         initial = init)
+                columnCtrl.Bind(wx.EVT_SPINCTRL, self.OnRuleSize)
+                columnCtrl.Bind(wx.EVT_TEXT, self.OnRuleSize)
+                columnCtrl.SetName('target')
+                if not start:
+                    self.ruleslines[enable.GetId()] = { 'value' : '',
+                                                        self.attributeType: init }
+        
+            self.mainSizer.Add(item = enable, proportion = 0,
+                              flag = wx.ALIGN_CENTER_VERTICAL)
+            self.mainSizer.Add(item = txt_ctrl, proportion = 0,
+                              flag = wx.ALIGN_CENTER | wx.RIGHT, border = 5)
+            self.mainSizer.Add(item = columnCtrl, proportion = 0,
+                              flag = wx.ALIGN_CENTER | wx.RIGHT, border = 10)
+        
+        self.mainPanel.Layout()
+        self.mainPanel.SetupScrolling(scroll_x = False)
+    
+    def OnRuleEnable(self, event):
+        """!Rule enabled/disabled"""
+        id = event.GetId()
+        
+        if event.IsChecked():
+            self.mainPanel.FindWindowById(id + 1000).Enable()
+            self.mainPanel.FindWindowById(id + 2000).Enable()
+            if self.mapType == 'vector' and not self.parent.GetParent().colorTable:
+                vals = []
+                vals.append(self.mainPanel.FindWindowById(id + 1000).GetValue())
+                try:
+                    vals.append(self.mainPanel.FindWindowById(id + 1 + 1000).GetValue())
+                except AttributeError:
+                    vals.append(None)
+                value = self.SQLConvert(vals)
+            else:
+                value = self.mainPanel.FindWindowById(id + 1000).GetValue()
+            color = self.mainPanel.FindWindowById(id + 2000).GetValue()
+            
+            if self.attributeType == 'color':
+            # color
+                color_str = str(color[0]) + ':' \
+                          + str(color[1]) + ':' \
+                          + str(color[2])
+                self.ruleslines[id] = {'value' : value,
+                                       'color' : color_str }
+                
+            else:
+            # size or width
+                self.ruleslines[id] = {'value' : value,
+                                       self.attributeType  : float(color) }
+        
+        else:
+            self.mainPanel.FindWindowById(id + 1000).Disable()
+            self.mainPanel.FindWindowById(id + 2000).Disable()
+            del self.ruleslines[id]
+        
+    def OnRuleColor(self, event):
+        """!Rule color changed"""
+        num = event.GetId()
+        
+        rgba_color = event.GetValue()
+        
+        rgb_string = str(rgba_color[0]) + ':' \
+                   + str(rgba_color[1]) + ':' \
+                   + str(rgba_color[2])
+        
+        self.ruleslines[num-2000]['color'] = rgb_string
+     
+    def OnRuleSize(self, event):
+        """!Rule size changed"""
+        num = event.GetId()
+        size = event.GetInt()
+        
+        self.ruleslines[num - 2000][self.attributeType] = size
+        
+    def OnRuleValue(self, event):
+        """!Rule value changed"""
+        num = event.GetId()
+        val = event.GetString().strip()
+        
+        if val == '':
+            return
+        try:
+            table = self.parent.colorTable
+        except AttributeError:
+            # due to panel/scrollpanel in vector dialog
+            if isinstance(self.parent.GetParent(), RasterColorTable):
+                table = self.parent.GetParent().colorTable
+            else:
+                table = self.parent.GetParent().GetParent().colorTable
+        if table:
+            self.SetRasterRule(num, val)
+        else:
+            self.SetVectorRule(num, val)
+
+    def SetRasterRule(self, num, val): 
+        """!Set raster rule"""       
+        self.ruleslines[num - 1000]['value'] = val
+
+    def SetVectorRule(self, num, val):
+        """!Set vector rule"""
+        vals = []
+        vals.append(val)
+        try:
+            vals.append(self.mainPanel.FindWindowById(num + 1).GetValue())
+        except AttributeError:
+            vals.append(None)
+        self.ruleslines[num - 1000]['value'] = self.SQLConvert(vals)
+            
+    def Enable(self, enable = True):
+        """!Enable/Disable all widgets"""
+        for child in self.mainPanel.GetChildren():
+            child.Enable(enable)
+        sql = True
+        self.LoadRulesline(sql)# todo
+        self.btnAdd.Enable(enable)
+        self.numRules.Enable(enable)
+        self.checkAll.Enable(enable)
+        self.clearAll.Enable(enable)
+        
+        
+    def LoadRules(self):
+        message = ""        
+        for item in range(len(self.ruleslines)):
+            try:
+                self.mainPanel.FindWindowById(item + 1000).SetValue(self.ruleslines[item]['value'])
+                r, g, b = (0, 0, 0) # default
+                if not self.ruleslines[item][self.attributeType]:
+                    if self.attributeType == 'color':
+                        self.ruleslines[item][self.attributeType] = '%d:%d:%d' % (r, g, b)
+                    elif self.attributeType == 'size':
+                        self.ruleslines[item][self.attributeType] = 100                
+                    elif self.attributeType == 'width':
+                        self.ruleslines[item][self.attributeType] = 2
+                    
+                if self.attributeType == 'color':
+                    try:
+                        r, g, b = map(int, self.ruleslines[item][self.attributeType].split(':'))
+                    except ValueError, e:
+                        message =  _("Bad color format. Use color format '0:0:0'")
+                    self.mainPanel.FindWindowById(item + 2000).SetValue((r, g, b))
+                else:
+                    value = float(self.ruleslines[item][self.attributeType])
+                    self.mainPanel.FindWindowById(item + 2000).SetValue(value)
+            except:
+                continue
+                
+        if message:
+            GMessage(parent = self.parent, message = message)
+            return False
+        
+        return True
+                
+    def SQLConvert(self, vals):
+        """!Prepare value for SQL query"""
+        if vals[0].isdigit():
+            sqlrule = '%s=%s' % (self.properties['sourceColumn'], vals[0])
+            if vals[1]:
+                sqlrule += ' AND %s<%s' % (self.properties['sourceColumn'], vals[1])
+        else:
+            sqlrule = '%s=%s' % (self.properties['sourceColumn'], vals[0])
+        
+        return sqlrule  
+
+class ColorTable(wx.Frame):
+    def __init__(self, parent, title, id = wx.ID_ANY,
+                 style = wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER,
+                 **kwargs):
+        """!Dialog for interactively entering rules for map management
+        commands
+        """
+        self.parent = parent # GMFrame
+        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))
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        
+        # instance of render.Map to be associated with display
+        self.Map = Map() 
+        
+        # input map to change
+        self.inmap = ''
+        # reference to layer with preview
+        self.layer = None     
+        # layout
+        self._doLayout()
+        
+        # bindings
+        self.Bind(wx.EVT_BUTTON, self.OnHelp, self.btnHelp)
+        self.selectionInput.Bind(wx.EVT_TEXT, self.OnSelectionInput)
+        self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel)
+        self.Bind(wx.EVT_BUTTON, self.OnApply, self.btnApply)
+        self.Bind(wx.EVT_BUTTON, self.OnOK, self.btnOK)
+        self.Bind(wx.EVT_CLOSE,  self.OnCloseWindow)
+       
+        self.Bind(wx.EVT_BUTTON, self.OnPreview, self.btnPreview)
+        
+    def _initLayer(self):
+        """!Set initial layer when opening dialog"""
+        # set map layer from layer tree, first selected,
+        # if not the right type, than select another
+        try:
+            sel = self.parent.curr_page.maptree.layer_selected
+            if sel and self.parent.curr_page.maptree.GetPyData(sel)[0]['type'] == self.mapType:
+                layer = sel
+            else:
+                layer = self.parent.curr_page.maptree.FindItemByData(key = 'type', value = self.mapType)
+        except:
+            layer = None
+        if layer:
+            mapLayer = self.parent.curr_page.maptree.GetPyData(layer)[0]['maplayer']
+            name = mapLayer.GetName()
+            type = mapLayer.GetType()
+            self.selectionInput.SetValue(name)
+            self.inmap = name
+    
+    def _createMapSelection(self, parent):
+        """!Create map selection part of dialog"""
+        # top controls
+        if self.mapType == 'raster':
+            maplabel = _('Select raster map:')
+        else:
+            maplabel = _('Select vector map:')
+        inputBox = wx.StaticBox(parent, id = wx.ID_ANY,
+                                label = " %s " % maplabel)
+        inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
+
+        self.selectionInput = Select(parent = parent, id = wx.ID_ANY,
+                                     size = globalvar.DIALOG_GSELECT_SIZE,
+                                     type = self.mapType)
+        # layout
+        inputSizer.Add(item = self.selectionInput,
+                       flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 5)
+        
+        return inputSizer
+    
+    def _createFileSelection(self, parent):
+        """!Create file (open/save rules) selection part of dialog"""
+        inputBox = wx.StaticBox(parent, id = wx.ID_ANY,
+                                label = " %s " % _("Import or export color table:"))
+        inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
+        
+        self.loadRules = filebrowse.FileBrowseButton(parent = parent, id = wx.ID_ANY, fileMask = '*',
+                                                     size = globalvar.DIALOG_GSELECT_SIZE,
+                                                     labelText = _('Load color table from file:'),
+                                                     dialogTitle = _('Choose file to load color table'),
+                                                     buttonText = _('Load'),
+                                                     toolTip = _("Type filename or click to choose "
+                                                                 "file and load color table"),
+                                                     startDirectory = os.getcwd(), fileMode = wx.OPEN,
+                                                     changeCallback = self.OnLoadRulesFile)
+        self.saveRules = filebrowse.FileBrowseButton(parent = parent, id = wx.ID_ANY, fileMask = '*',
+                                                     size = globalvar.DIALOG_GSELECT_SIZE,
+                                                     labelText = _('Save color table to file:'),
+                                                     dialogTitle = _('Choose file to save color table'),
+                                                     toolTip = _("Type filename or click to choose "
+                                                                 "file and save color table"),
+                                                     buttonText = _('Save'),
+                                                     startDirectory = os.getcwd(), fileMode = wx.SAVE,
+                                                     changeCallback = self.OnSaveRulesFile)
+        
+        default = wx.Button(parent = parent, id = wx.ID_ANY, label = _("Reload default table"))   
+        # layout
+        sizer = wx.BoxSizer(wx.HORIZONTAL)
+        sizer.Add(item = self.loadRules, proportion = 1,
+                  flag = wx.RIGHT | wx.EXPAND, border = 10)
+        sizer.Add(item = default, flag = wx.ALIGN_CENTER_VERTICAL)
+        inputSizer.Add(item = sizer,
+                       flag = wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border = 5)
+        sizer = wx.BoxSizer(wx.HORIZONTAL)
+        sizer.Add(item = self.saveRules, proportion = 1,
+                  flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
+        inputSizer.Add(item = sizer, proportion = 1,
+                       flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        default.Bind(wx.EVT_BUTTON, self.OnLoadDefaultTable)
+        
+        if self.mapType == 'vector':
+            # parent is collapsible pane
+            parent.SetSizer(inputSizer)
+        
+        return inputSizer   
+         
+    def _createPreview(self, parent):
+        """!Create preview"""
+        # initialize preview display
+        self.InitDisplay()
+        self.preview = BufferedWindow(parent, id = wx.ID_ANY, size = (400, 300),
+                                      Map = self.Map)
+        self.preview.EraseMap()
+        
+    def _createButtons(self, parent):
+        """!Create buttons for leaving dialog"""
+        self.btnHelp   = wx.Button(parent, id = wx.ID_HELP)
+        self.btnCancel = wx.Button(parent, id = wx.ID_CANCEL)
+        self.btnApply  = wx.Button(parent, id = wx.ID_APPLY) 
+        self.btnOK     = wx.Button(parent, id = wx.ID_OK)
+        
+        self.btnOK.SetDefault()
+        self.btnOK.Enable(False)
+        self.btnApply.Enable(False)
+        
+        # layout
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(wx.Size(-1, -1), proportion = 1)
+        btnSizer.Add(self.btnHelp,
+                     flag = wx.LEFT | wx.RIGHT, border = 5)
+        btnSizer.Add(self.btnCancel,
+                     flag = wx.LEFT | wx.RIGHT, border = 5)
+        btnSizer.Add(self.btnApply,
+                     flag = wx.LEFT | wx.RIGHT, border = 5)
+        btnSizer.Add(self.btnOK,
+                     flag = wx.LEFT | wx.RIGHT, border = 5)
+        
+        return btnSizer
+    
+    def _createBody(self, parent):
+        """!Create dialog body consisting of rules and preview"""
+        bodySizer =  wx.GridBagSizer(hgap = 5, vgap = 5)
+        bodySizer.AddGrowableRow(1)
+        bodySizer.AddGrowableCol(2)
+
+        row = 0
+        # label with range
+        self.cr_label = wx.StaticText(parent, id = wx.ID_ANY)
+        bodySizer.Add(item = self.cr_label, pos = (row, 0), span = (1, 3),
+                      flag = wx.ALL, border = 5)
+
+        row += 1
+        # color table
+        self.rulesPanel = RulesPanel(parent = parent, mapType = self.mapType,
+                                     attributeType = self.attributeType, properties = self.properties)
+        
+        bodySizer.Add(item = self.rulesPanel.mainPanel, pos = (row, 0),
+                      span = (1, 2), flag = wx.EXPAND)
+        # add two rules as default
+        self.rulesPanel.AddRules(2)
+        
+        # preview window
+        self._createPreview(parent = parent)
+        bodySizer.Add(item = self.preview, pos = (row, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER)
+        
+        row += 1
+        # add ckeck all and clear all
+        bodySizer.Add(item = self.rulesPanel.checkAll, flag = wx.ALIGN_CENTER_VERTICAL, 
+                      pos = (row, 0))
+        bodySizer.Add(item = self.rulesPanel.clearAll, pos = (row, 1))
+        
+        # preview button
+        self.btnPreview = wx.Button(parent, id = wx.ID_ANY,
+                                    label = _("Preview"))
+        bodySizer.Add(item = self.btnPreview, pos = (row, 2),
+                      flag = wx.ALIGN_RIGHT)
+        self.btnPreview.Enable(False)
+        self.btnPreview.SetToolTipString(_("Show preview of map "
+                                           "(current Map Display extent is used)."))
+        
+        row +=1
+        # add rules button and spin to sizer
+        bodySizer.Add(item = self.rulesPanel.numRules, pos = (row, 0),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        bodySizer.Add(item = self.rulesPanel.btnAdd, pos = (row, 1))
+        
+        return bodySizer    
+        
+    def InitDisplay(self):
+        """!Initialize preview display, set dimensions and region
+        """
+        self.width = self.Map.width = 400
+        self.height = self.Map.height = 300
+        self.Map.geom = self.width, self.height
+
+    def OnCloseWindow(self, event):
+        """!Window closed
+        """
+        self.OnCancel(event)
+          
+    def OnApply(self, event):
+        """!Apply selected color table
+        
+        @return True on success otherwise False
+        """
+        ret = self.CreateColorTable()
+        if not ret:
+            GMessage(parent = self, message = _("No valid color rules given."))
+        else:
+            # re-render preview and current map window
+            self.OnPreview(None)
+            display = self.parent.GetLayerTree().GetMapDisplay()
+            if display and display.IsAutoRendered():
+                display.GetWindow().UpdateMap(render = True)
+        
+        return ret
+
+    def OnOK(self, event):
+        """!Apply selected color table and close the dialog"""
+        if self.OnApply(event):
+            self.OnCancel(event)
+    
+    def OnCancel(self, event):
+        """!Do not apply any changes, remove associated
+            rendered images and close the dialog"""
+        self.Map.Clean()
+        self.Destroy()
+        
+    def OnSaveRulesFile(self, event):
+        """!Save color table to file"""
+        path = event.GetString()
+        if not os.path.exists(path):
+            return
+        
+        rulestxt = ''   
+        for rule in self.rulesPanel.ruleslines.itervalues():
+            if 'value' not in rule:
+                continue
+            rulestxt += rule['value'] + ' ' + rule['color'] + '\n'
+        if not rulestxt:
+            GMessage(message = _("Nothing to save."),
+                     parent = self)
+            return
+        
+        fd = open(path, 'w')
+        fd.write(rulestxt)
+        fd.close()            
+         
+    def OnLoadRulesFile(self, event):
+        """!Load color table from file"""
+        path = event.GetString()
+        if not os.path.exists(path):
+            return
+        
+        self.rulesPanel.Clear()
+        
+        file = open(path, 'r')
+        ctable = file.read()
+        self.ReadColorTable(ctable = ctable)
+        
+    def ReadColorTable(self, ctable):
+        """!Read color table
+        
+        @param table color table in format coming from r.colors.out"""
+        
+        rulesNumber = len(ctable.splitlines())
+        self.rulesPanel.AddRules(rulesNumber)
+        
+        minim = maxim = count = 0
+        for line in ctable.splitlines():
+            try:
+                value, color = map(lambda x: x.strip(), line.split(' '))
+            except ValueError:
+                GMessage(parent = self, message = _("Invalid color table format"))
+                self.rulesPanel.Clear()
+                return
+            
+            self.rulesPanel.ruleslines[count]['value'] = value
+            self.rulesPanel.ruleslines[count]['color'] = color
+            self.rulesPanel.mainPanel.FindWindowById(count + 1000).SetValue(value)
+            rgb = list()
+            for c in color.split(':'):
+                rgb.append(int(c))
+            self.rulesPanel.mainPanel.FindWindowById(count + 2000).SetColour(rgb)
+            # range
+            try:
+                if float(value) < minim:
+                    minim = float(value)
+                if float(value) > maxim:
+                    maxim = float(value)
+            except ValueError: # nv, default
+                pass
+            count += 1
+        
+        if self.mapType == 'vector':
+            # raster min, max is known from r.info
+            self.properties['min'], self.properties['max'] = minim, maxim
+            self.SetRangeLabel()
+            
+        self.OnPreview(tmp = True)  
+         
+    def OnLoadDefaultTable(self, event):
+        """!Load internal color table"""
+        self.LoadTable()
+        
+    def LoadTable(self, mapType = 'raster'):
+        """!Load current color table (using `r(v).colors.out`)
+        
+        @param mapType map type (raster or vector)"""
+        self.rulesPanel.Clear()
+
+        if mapType == 'raster':
+            cmd = ['r.colors.out',
+                   'read=True',
+                   'map=%s' % self.inmap,
+                   'rules=-']
+        else:
+            cmd = ['v.colors.out',
+                   'read=True',
+                   'map=%s' % self.inmap,
+                   'rules=-']
+            
+            if self.properties['sourceColumn'] and self.properties['sourceColumn'] != 'cat':
+                cmd.append('column=%s' % self.properties['sourceColumn'])
+            
+        cmd = utils.CmdToTuple(cmd)
+        
+        if self.inmap:
+            ctable = RunCommand(cmd[0], **cmd[1])
+        else:
+            self.OnPreview()
+            return
+        
+        self.ReadColorTable(ctable = ctable)     
+    
+    def CreateColorTable(self, tmp = False):
+        """!Creates color table
+
+        @return True on success
+        @return False on failure
+        """
+        rulestxt = ''
+        
+        for rule in self.rulesPanel.ruleslines.itervalues():
+            if 'value' not in rule: # skip empty rules
+                continue
+            
+            if rule['value'] not in ('nv', 'default') and \
+                    rule['value'][-1] != '%' and \
+                    not self._IsNumber(rule['value']):
+                GError(_("Invalid rule value '%s'. Unable to apply color table.") % rule['value'],
+                       parent = self)
+                return False
+            
+            rulestxt += rule['value'] + ' ' + rule['color'] + '\n'
+           
+        if not rulestxt:
+            return False
+        
+        gtemp = utils.GetTempfile()
+        output = open(gtemp, "w")
+        try:
+            output.write(rulestxt)
+        finally:
+            output.close()
+        
+        cmd = ['%s.colors' % self.mapType[0], #r.colors/v.colors
+                'map=%s' % self.inmap,
+                'rules=%s' % gtemp]
+        if self.mapType == 'vector' and self.properties['sourceColumn'] \
+                and self.properties['sourceColumn'] != 'cat':
+            cmd.append('column=%s' % self.properties['sourceColumn'])
+        cmd = utils.CmdToTuple(cmd)
+        ret = RunCommand(cmd[0], **cmd[1])               
+        if ret != 0:
+            return False
+        
+        return True
+    
+    def DoPreview(self, ltype, cmdlist):
+        """!Update preview (based on computational region)"""
+        
+        if not self.layer:
+            self.layer = self.Map.AddLayer(type = ltype, name = 'preview', command = cmdlist,
+                                           l_active = True, l_hidden = False, l_opacity = 1.0,
+                                           l_render = False) 
+        else:
+            self.layer.SetCmd(cmdlist)
+        
+        # apply new color table and display preview
+        self.CreateColorTable(tmp = True)
+        self.preview.UpdatePreview()
+        
+    def RunHelp(self, cmd):
+        """!Show GRASS manual page"""
+        RunCommand('g.manual',
+                   quiet = True,
+                   parent = self,
+                   entry = cmd)
+        
+    def _IsNumber(self, s):
+        """!Check if 's' is a number"""
+        try:
+            float(s)
+            return True
+        except ValueError:
+            return False
+        
+
+class RasterColorTable(ColorTable):
+    def __init__(self, parent, **kwargs):
+        """!Dialog for interactively entering color rules for raster maps"""
+
+        self.mapType = 'raster'
+        self.attributeType = 'color' 
+        self.colorTable = True 
+        # raster properties
+        self.properties = {
+            # min cat in raster map
+            'min' : None,
+            # max cat in raster map
+            'max' : None,
+            }        
+        
+        ColorTable.__init__(self, parent,
+                            title = _('Create new color table for raster map'), **kwargs)
+        
+        self._initLayer()
+        
+        # self.SetMinSize(self.GetSize()) 
+        self.SetMinSize((650, 700))
+        
+        self.CentreOnScreen()
+        self.Show()
+    
+    def _doLayout(self):
+        """!Do main layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        #
+        # map selection
+        #
+        mapSelection = self._createMapSelection(parent = self.panel)
+        sizer.Add(item = mapSelection, proportion = 0,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # manage extern tables
+        #
+        fileSelection = self._createFileSelection(parent = self.panel)
+        sizer.Add(item = fileSelection, proportion = 0,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # body & preview
+        #
+        bodySizer = self._createBody(parent = self.panel)
+        sizer.Add(item = bodySizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # buttons
+        #
+        btnSizer = self._createButtons(parent = self.panel)
+        sizer.Add(item = wx.StaticLine(parent = self.panel, id = wx.ID_ANY,
+                                       style = wx.LI_HORIZONTAL), proportion = 0,
+                                       flag = wx.EXPAND | wx.ALL, border = 5) 
+        
+        sizer.Add(item = btnSizer, proportion = 0,
+                  flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
+        
+        self.panel.SetSizer(sizer)
+        sizer.Layout()
+        sizer.Fit(self.panel)
+        self.Layout()
+    
+    def OnSelectionInput(self, event):
+        """!Raster map selected"""
+        if event:
+            self.inmap = event.GetString()
+    
+        self.loadRules.SetValue('')
+        self.saveRules.SetValue('')
+        if self.inmap:
+            if not grass.find_file(name = self.inmap, element = 'cell')['file']:
+                self.inmap = None
+        
+        if not self.inmap:
+            self.btnPreview.Enable(False)
+            self.btnOK.Enable(False)
+            self.btnApply.Enable(False)
+            self.LoadTable()
+            return
+        
+        info = grass.raster_info(map = self.inmap)
+        
+        if info:
+            self.properties['min'] = info['min']
+            self.properties['max'] = info['max']
+            self.LoadTable()
+        else:
+            self.inmap = ''
+            self.properties['min'] = self.properties['max'] = None
+            self.btnPreview.Enable(False)
+            self.btnOK.Enable(False)
+            self.btnApply.Enable(False)
+            self.preview.EraseMap()
+            self.cr_label.SetLabel(_('Enter raster category values or percents'))
+            return
+        
+        if info['datatype'] == 'CELL':
+            mapRange = _('range')
+        else:
+            mapRange = _('fp range')
+        self.cr_label.SetLabel(_('Enter raster category values or percents (%(range)s = %(min)d-%(max)d)') %
+                                 { 'range' : mapRange,
+                                   'min' : self.properties['min'],
+                                   'max' : self.properties['max'] })                       
+            
+        self.btnPreview.Enable()
+        self.btnOK.Enable()
+        self.btnApply.Enable()
+            
+          
+    def OnPreview(self, tmp = True):
+        """!Update preview (based on computational region)"""
+        if not self.inmap:
+            self.preview.EraseMap()
+            return
+        
+        cmdlist = ['d.rast',
+                   'map=%s' % self.inmap]
+        ltype = 'raster'
+        
+        # find existing color table and copy to temp file
+        try:
+            name, mapset = self.inmap.split('@')
+        except ValueError:
+            name = self.inmap
+            mapset = grass.find_file(self.inmap, element = 'cell')['mapset']
+            if not mapset:
+                return
+        old_colrtable = None
+        if mapset == grass.gisenv()['MAPSET']:
+            old_colrtable = grass.find_file(name = name, element = 'colr')['file']
+        else:
+            old_colrtable = grass.find_file(name = name, element = 'colr2/' + mapset)['file']
+        
+        if old_colrtable:
+            colrtemp = utils.GetTempfile()
+            shutil.copyfile(old_colrtable, colrtemp)
+            
+        ColorTable.DoPreview(self, ltype, cmdlist)  
+        
+        # restore previous color table
+        if tmp:
+            if old_colrtable:
+                shutil.copyfile(colrtemp, old_colrtable)
+                os.remove(colrtemp)
+            else:
+                RunCommand('r.colors',
+                           parent = self,
+                           flags = 'r',
+                           map = self.inmap)
+        
+    def OnHelp(self, event):
+        """!Show GRASS manual page"""
+        cmd = 'r.colors'
+        ColorTable.RunHelp(self, cmd = cmd)
+                     
+class VectorColorTable(ColorTable):
+    def __init__(self, parent, attributeType, **kwargs):
+        """!Dialog for interactively entering color rules for vector maps"""
+        # dialog attributes
+        self.mapType = 'vector'
+        self.attributeType = attributeType # color, size, width
+        # in version 7 v.colors used, otherwise color column only
+        self.version7 = int(grass.version()['version'].split('.')[0]) >= 7
+        self.colorTable = False
+        self.updateColumn = True
+        # vector properties
+        self.properties = {
+            # vector layer for attribute table to use for setting color
+            'layer' : 1, 
+            # vector attribute table used for setting color         
+            'table' : '',
+            # vector attribute column for assigning colors
+            'sourceColumn' : '', 
+            # vector attribute column to use for loading colors
+            'loadColumn' : '',
+            # vector attribute column to use for storing colors
+            'storeColumn' : '',    
+            # vector attribute column for temporary storing colors   
+            'tmpColumn' : 'tmp_0',
+            # min value of attribute column/vector color table
+            'min': None,
+            # max value of attribute column/vector color table            
+            'max': None
+            }     
+        self.columnsProp = {'color': {'name': 'GRASSRGB', 'type1': 'varchar(11)', 'type2': ['character']},
+                            'size' : {'name': 'GRASSSIZE', 'type1': 'integer', 'type2': ['integer']},
+                            'width': {'name': 'GRASSWIDTH', 'type1': 'integer', 'type2': ['integer']}}
+        ColorTable.__init__(self, parent = parent,
+                            title = _('Create new color rules for vector map'), **kwargs)
+        
+        # additional bindings for vector color management
+        self.Bind(wx.EVT_COMBOBOX, self.OnLayerSelection, self.layerSelect)
+        self.Bind(wx.EVT_COMBOBOX, self.OnSourceColumnSelection, self.sourceColumn)
+        self.Bind(wx.EVT_COMBOBOX, self.OnFromColSelection, self.fromColumn)
+        self.Bind(wx.EVT_COMBOBOX, self.OnToColSelection, self.toColumn)
+        self.Bind(wx.EVT_BUTTON, self.OnAddColumn, self.addColumn)
+        
+        self._initLayer()
+        if self.colorTable:
+            self.cr_label.SetLabel(_("Enter vector attribute values or percents:"))
+        else:
+            self.cr_label.SetLabel(_("Enter vector attribute values:"))
+        
+        #self.SetMinSize(self.GetSize()) 
+        self.SetMinSize((650, 700))
+        
+        self.CentreOnScreen()
+        self.Show()
+    
+    def _createVectorAttrb(self, parent):
+        """!Create part of dialog with layer/column selection"""
+        inputBox = wx.StaticBox(parent = parent, id = wx.ID_ANY,
+                                label = " %s " % _("Select vector columns"))
+        cb_vl_label = wx.StaticText(parent, id = wx.ID_ANY,
+                                             label = _('Layer:'))
+        cb_vc_label = wx.StaticText(parent, id = wx.ID_ANY,
+                                         label = _('Attribute column:'))
+                                        
+        if self.attributeType == 'color':
+            labels =  [_("Load color from column:"), _("Save color to column:")]
+        elif self.attributeType == 'size':
+            labels =  [_("Load size from column:"), _("Save size to column:")]
+        elif self.attributeType == 'width':
+            labels =  [_("Load width from column:"), _("Save width to column:")]
+            
+        if self.version7 and self.attributeType == 'color':
+            self.useColumn = wx.CheckBox(parent, id = wx.ID_ANY,
+                                  label = _("Use color column instead of color table:"))
+            self.useColumn.Bind(wx.EVT_CHECKBOX, self.OnCheckColumn)
+        
+        fromColumnLabel = wx.StaticText(parent, id = wx.ID_ANY,
+                                            label = labels[0])
+        toColumnLabel = wx.StaticText(parent, id = wx.ID_ANY,
+                                            label = labels[1])
+                                                
+        self.rgb_range_label = wx.StaticText(parent, id = wx.ID_ANY)
+        self.layerSelect = LayerSelect(parent)
+        self.sourceColumn = ColumnSelect(parent)
+        self.fromColumn = ColumnSelect(parent)
+        self.toColumn = ColumnSelect(parent)
+        self.addColumn = wx.Button(parent, id = wx.ID_ANY,
+                                             label = _('Add column'))
+        self.addColumn.SetToolTipString(_("Add GRASSRGB column to current attribute table."))
+        
+        # layout
+        inputSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL)
+        vSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        row = 0
+        vSizer.Add(cb_vl_label, pos = (row, 0),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.layerSelect,  pos = (row, 1),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        row += 1
+        vSizer.Add(cb_vc_label, pos = (row, 0),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.sourceColumn, pos = (row, 1),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.rgb_range_label, pos = (row, 2),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        row += 1   
+        if self.version7 and self.attributeType == 'color':
+            vSizer.Add(self.useColumn, pos = (row, 0), span = (1, 2),
+                       flag = wx.ALIGN_CENTER_VERTICAL)
+            row += 1
+            
+        vSizer.Add(fromColumnLabel, pos = (row, 0),
+                  flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.fromColumn, pos = (row, 1),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        row += 1
+        vSizer.Add(toColumnLabel, pos = (row, 0),
+                  flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.toColumn, pos = (row, 1),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        vSizer.Add(self.addColumn, pos = (row, 2),
+                   flag = wx.ALIGN_CENTER_VERTICAL)
+        inputSizer.Add(item = vSizer,
+                       flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, border = 5)
+        self.colorColumnSizer = vSizer        
+        return inputSizer 
+       
+    def _doLayout(self):
+        """!Do main layout"""
+        scrollPanel = scrolled.ScrolledPanel(parent = self.panel, id = wx.ID_ANY,
+                                             style = wx.TAB_TRAVERSAL)
+        scrollPanel.SetupScrolling()
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        #
+        # map selection
+        #
+        mapSelection = self._createMapSelection(parent = scrollPanel)
+        sizer.Add(item = mapSelection, proportion = 0,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # manage extern tables
+        #
+        if self.version7 and self.attributeType == 'color':
+            self.cp = wx.CollapsiblePane(scrollPanel, label = _("Import or export color table"),
+                                         winid = wx.ID_ANY,
+                                        style = wx.CP_DEFAULT_STYLE|wx.CP_NO_TLW_RESIZE)
+            self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnPaneChanged, self.cp)
+        
+            self._createFileSelection(parent = self.cp.GetPane())
+            sizer.Add(item = self.cp, proportion = 0,
+                      flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # set vector attributes
+        #
+        vectorAttrb = self._createVectorAttrb(parent = scrollPanel)
+        sizer.Add(item = vectorAttrb, proportion = 0,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # body & preview
+        #
+        bodySizer = self._createBody(parent = scrollPanel)
+        sizer.Add(item = bodySizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        scrollPanel.SetSizer(sizer)
+        scrollPanel.Fit()        
+        
+        #
+        # buttons
+        #
+        btnSizer = self._createButtons(self.panel)
+        
+        mainsizer = wx.BoxSizer(wx.VERTICAL)
+        mainsizer.Add(scrollPanel, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
+        mainsizer.Add(item = wx.StaticLine(parent = self.panel, id = wx.ID_ANY,
+                                       style = wx.LI_HORIZONTAL), proportion = 0,
+                                       flag = wx.EXPAND | wx.ALL, border = 5) 
+        mainsizer.Add(item = btnSizer, proportion = 0,
+                  flag = wx.ALL | wx.ALIGN_RIGHT | wx.EXPAND, border = 5)
+        
+        self.panel.SetSizer(mainsizer)
+        mainsizer.Layout()
+        mainsizer.Fit(self.panel)     
+        self.Layout()
+        
+    def OnPaneChanged(self, event = None):
+        # redo the layout
+        self.Layout()
+        # and also change the labels
+        if self.cp.IsExpanded():
+            self.cp.SetLabel('')
+        else:
+            self.cp.SetLabel(_("Import or export color table"))
+        
+    def CheckMapset(self):
+        """!Check if current vector is in current mapset"""
+        if grass.find_file(name = self.inmap,
+                           element = 'vector')['mapset'] == grass.gisenv()['MAPSET']:
+            return True
+        else:
+            return False 
+         
+    def NoConnection(self, vectorName):
+        dlg = wx.MessageDialog(parent = self,
+                                message = _("Database connection for vector map <%s> "
+                                            "is not defined in DB file.  Do you want to create and "
+                                            "connect new attribute table?") % vectorName,
+                                caption = _("No database connection defined"),
+                                style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
+        if dlg.ShowModal() == wx.ID_YES:
+            dlg.Destroy()
+            GUI(parent = self).ParseCommand(['v.db.addtable', 'map=' + self.inmap], 
+                                            completed = (self.CreateAttrTable, self.inmap, ''))
+        else:
+            dlg.Destroy()
+  
+    def OnCheckColumn(self, event):
+        """!Use color column instead of color table"""
+        if self.useColumn.GetValue():
+            self.properties['loadColumn'] = self.fromColumn.GetStringSelection()
+            self.properties['storeColumn'] = self.toColumn.GetStringSelection()
+            self.fromColumn.Enable(True)
+            self.toColumn.Enable(True)
+            self.colorTable = False
+            
+            if self.properties['loadColumn']:
+                self.LoadTable()
+            else:
+                self.rulesPanel.Clear()
+        else:
+            self.properties['loadColumn'] = ''
+            self.properties['storeColumn'] = ''
+            self.fromColumn.Enable(False)
+            self.toColumn.Enable(False)
+            self.colorTable = True
+            self.LoadTable()
+            
+    def EnableVectorAttributes(self, enable):
+        """!Enable/disable part of dialog connected with db"""
+        for child in self.colorColumnSizer.GetChildren():
+            child.GetWindow().Enable(enable)
+    
+    def DisableClearAll(self):
+        """!Enable, disable the whole dialog"""
+        self.rulesPanel.Clear()
+        self.EnableVectorAttributes(False)
+        self.btnPreview.Enable(False)
+        self.btnOK.Enable(False)
+        self.btnApply.Enable(False)
+        self.preview.EraseMap()
+        
+    def OnSelectionInput(self, event):
+        """!Vector map selected"""
+        if event:
+            if self.inmap:
+                # switch to another map -> delete temporary column
+                self.DeleteTemporaryColumn()
+            self.inmap = event.GetString()
+            
+        if self.version7 and self.attributeType == 'color': 
+            self.loadRules.SetValue('')
+            self.saveRules.SetValue('')
+        
+        if self.inmap:
+            if not grass.find_file(name = self.inmap, element = 'vector')['file']:
+                self.inmap = None
+        
+        self.UpdateDialog()
+       
+    def UpdateDialog(self):
+        """!Update dialog after map selection"""  
+        
+        if not self.inmap:
+            self.DisableClearAll()
+            return
+        
+        if self.inmap and not self.CheckMapset():
+            # currently v.colors need the map to be in current mapset
+            if self.version7 and self.attributeType == 'color':
+                message = _("Selected map <%(map)s> is not in current mapset <%(mapset)s>. "
+                            "Color rules cannot be edited.") % \
+                            { 'map' : self.inmap,
+                              'mapset' : grass.gisenv()['MAPSET'] }
+            else:
+                message = _("Selected map <%(map)s> is not in current mapset <%(mapset)s>. "
+                            "Attribute table cannot be edited.") % \
+                            { 'map' : self.inmap,
+                              'mapset' : grass.gisenv()['MAPSET'] }
+            wx.CallAfter(GMessage, parent = self, message = message)
+            self.DisableClearAll()
+            return
+              
+        # check for db connection
+        self.dbInfo = VectorDBInfo(self.inmap)
+        enable = True
+        if not len(self.dbInfo.layers): # no connection
+            if not (self.version7 and self.attributeType == 'color'): # otherwise it doesn't matter
+                wx.CallAfter(self.NoConnection, self.inmap)
+                enable = False
+            for combo in (self.layerSelect, self.sourceColumn, self.fromColumn, self.toColumn):
+                combo.SetValue("")
+                combo.Clear()
+            for prop in ('sourceColumn', 'loadColumn', 'storeColumn'):
+                self.properties[prop] = ''
+            self.EnableVectorAttributes(False)
+        else: # db connection exist
+        # initialize layer selection combobox
+            self.EnableVectorAttributes(True)
+            self.layerSelect.InsertLayers(self.inmap)
+            # initialize attribute table for layer=1
+            self.properties['layer'] = self.layerSelect.GetString(0)
+            self.layerSelect.SetStringSelection(self.properties['layer'])
+            layer = int(self.properties['layer'])
+            self.properties['table'] = self.dbInfo.layers[layer]['table']
+            
+            if self.attributeType == 'color':
+                self.AddTemporaryColumn(type = 'varchar(11)')
+            else:
+                self.AddTemporaryColumn(type = 'integer')
+            
+            # initialize column selection comboboxes 
+            
+            self.OnLayerSelection(event = None)
+
+        if self.version7 and self.attributeType == 'color':
+            self.useColumn.SetValue(False)
+            self.OnCheckColumn(event = None) 
+                    
+        self.LoadTable()
+            
+        self.btnPreview.Enable(enable)
+        self.btnOK.Enable(enable)
+        self.btnApply.Enable(enable)   
+    
+    def AddTemporaryColumn(self, type):
+        """!Add temporary column to not overwrite the original values,
+        need to be deleted when closing dialog and unloading map
+        
+        @param type type of column (e.g. vachar(11))"""
+        # because more than one dialog with the same map can be opened we must test column name and
+        # create another one
+        while self.properties['tmpColumn'] in self.dbInfo.GetTableDesc(self.properties['table']).keys():
+            name, idx = self.properties['tmpColumn'].split('_')
+            idx = int(idx)
+            idx += 1
+            self.properties['tmpColumn'] = name + '_' + str(idx)
+        
+        if self.version7:
+            modul = 'v.db.addcolumn'
+        else:
+            modul = 'v.db.addcol'
+        ret = RunCommand(modul,
+                         parent = self,
+                         map = self.inmap,
+                         layer = self.properties['layer'],
+                         column = '%s %s' % (self.properties['tmpColumn'], type))
+        
+    def DeleteTemporaryColumn(self):
+        """!Delete temporary column"""
+        if self.inmap:
+            if self.version7:
+                modul = 'v.db.dropcolumn'
+            else:
+                modul = 'v.db.dropcol'
+            ret = RunCommand(modul,
+                             map = self.inmap,
+                             layer = self.properties['layer'],
+                             column = self.properties['tmpColumn'])
+        
+    def OnLayerSelection(self, event):
+        # reset choices in column selection comboboxes if layer changes
+        vlayer = int(self.layerSelect.GetStringSelection())
+        self.sourceColumn.InsertColumns(vector = self.inmap, layer = vlayer,
+                                        type = ['integer', 'double precision'], dbInfo = self.dbInfo,
+                                        excludeCols = ['tmpColumn'])
+        self.sourceColumn.SetStringSelection('cat')
+        self.properties['sourceColumn'] = self.sourceColumn.GetString(0)
+        
+        if self.attributeType == 'color':
+            type = ['character']
+        else:
+            type = ['integer']
+        self.fromColumn.InsertColumns(vector = self.inmap, layer = vlayer, type = type,
+                                      dbInfo = self.dbInfo, excludeCols = ['tmpColumn'])
+        self.toColumn.InsertColumns(vector = self.inmap, layer = vlayer, type = type,
+                                    dbInfo = self.dbInfo, excludeCols = ['tmpColumn'])
+        
+        found = self.fromColumn.FindString(self.columnsProp[self.attributeType]['name'])
+        if found != wx.NOT_FOUND:
+            self.fromColumn.SetSelection(found)
+            self.toColumn.SetSelection(found)
+            self.properties['loadColumn'] = self.fromColumn.GetString(found)
+            self.properties['storeColumn'] = self.toColumn.GetString(found)
+        else:
+            self.properties['loadColumn'] = ''
+            self.properties['storeColumn'] = ''
+        
+        if event:
+            self.LoadTable()
+        self.Update()
+        
+    def OnSourceColumnSelection(self, event):
+        self.properties['sourceColumn'] = event.GetString()
+        
+        self.LoadTable()
+    
+    def OnAddColumn(self, event):
+        """!Add GRASS(RGB,SIZE,WIDTH) column if it doesn't exist"""
+        if self.columnsProp[self.attributeType]['name'] not in self.fromColumn.GetItems():
+            if self.version7:
+                modul = 'v.db.addcolumn'
+            else:
+                modul = 'v.db.addcol'
+            ret = RunCommand(modul,
+                             map = self.inmap,
+                             layer = self.properties['layer'],
+                             columns = '%s %s' % (self.columnsProp[self.attributeType]['name'],
+                                                  self.columnsProp[self.attributeType]['type1']))
+            self.toColumn.InsertColumns(self.inmap, self.properties['layer'],
+                                        type = self.columnsProp[self.attributeType]['type2'])
+            self.toColumn.SetStringSelection(self.columnsProp[self.attributeType]['name'])
+            self.properties['storeColumn'] = self.toColumn.GetStringSelection()
+            
+            self.LoadTable()
+        else:
+            GMessage(parent = self,
+                     message = _("%s column already exists.") % \
+                         self.columnsProp[self.attributeType]['name'])
+                        
+    def CreateAttrTable(self, dcmd, layer, params, propwin):
+        """!Create attribute table"""
+        if dcmd:
+            cmd = utils.CmdToTuple(dcmd)
+            ret = RunCommand(cmd[0], **cmd[1])
+            if ret == 0:
+                self.OnSelectionInput(None)
+                return True
+            
+        for combo in (self.layerSelect, self.sourceColumn, self.fromColumn, self.toColumn):
+            combo.SetValue("")
+            combo.Disable()
+        return False    
+    
+    def LoadTable(self):
+        """!Load table"""
+        if self.colorTable:
+            ColorTable.LoadTable(self, mapType = 'vector')
+        else:
+            self.LoadRulesFromColumn()
+            
+    def LoadRulesFromColumn(self):
+        """!Load current column (GRASSRGB, size column)"""
+        
+        self.rulesPanel.Clear()
+        if not self.properties['sourceColumn']:
+            self.preview.EraseMap()
+            return
+        
+        busy = wx.BusyInfo(message = _("Please wait, loading data from attribute table..."),
+                           parent = self)
+        wx.Yield()
+        
+        columns = self.properties['sourceColumn']
+        if self.properties['loadColumn']:
+            columns += ',' + self.properties['loadColumn']
+        
+        sep = ';'            
+        if self.inmap:
+            outFile = tempfile.NamedTemporaryFile(mode = 'w+b')
+            ret = RunCommand('v.db.select',
+                             quiet = True,
+                             flags = 'c',
+                             map = self.inmap,
+                             layer = self.properties['layer'],
+                             columns = columns,
+                             fs = sep,
+                             stdout = outFile)
+        else:
+            self.preview.EraseMap()
+            busy.Destroy()
+            return
+        
+        outFile.seek(0)
+        i = 0
+        minim = maxim = 0.0
+        limit = 1000
+        
+        colvallist = []
+        readvals = False
+        
+        while True:
+            # os.linesep doesn't work here (MSYS)
+            record = outFile.readline().replace('\n', '')
+            if not record:
+                break
+            self.rulesPanel.ruleslines[i] = {}
+            
+            if not self.properties['loadColumn']:
+                col1 = record
+                col2 = None
+            else:
+                col1, col2 = record.split(sep)
+            
+            if float(col1) < minim:
+                minim = float(col1)
+            if float(col1) > maxim:
+                maxim = float(col1)
+
+                
+            # color rules list should only have unique values of col1, not all records
+            if col1 not in colvallist:                
+                self.rulesPanel.ruleslines[i]['value'] = col1
+                self.rulesPanel.ruleslines[i][self.attributeType] = col2
+
+                colvallist.append(col1)            
+                i += 1
+            
+            if i > limit and readvals == False:
+                dlg = wx.MessageDialog (parent = self, message = _(
+                                        "Number of loaded records reached %d, "
+                                        "displaying all the records will be time-consuming "
+                                        "and may lead to computer freezing, "
+                                        "do you still want to continue?") % i,
+                                        caption = _("Too many records"),
+                                        style  =  wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
+                if dlg.ShowModal() == wx.ID_YES:
+                    readvals = True
+                    dlg.Destroy()
+                else:
+                    busy.Destroy()
+                    dlg.Destroy()
+                    self.updateColumn = False
+                    return
+            
+        self.rulesPanel.AddRules(i, start = True)
+        ret = self.rulesPanel.LoadRules()
+        
+        self.properties['min'], self.properties['max'] = minim, maxim
+        self.SetRangeLabel()
+        
+        if ret:
+            self.OnPreview()   
+        else:
+            self.rulesPanel.Clear()
+    
+        busy.Destroy()
+        
+    def SetRangeLabel(self):
+        """!Set labels with info about attribute column range"""
+        
+        if self.properties['sourceColumn']:
+            ctype = self.dbInfo.GetTableDesc(self.properties['table'])[self.properties['sourceColumn']]['ctype']
+        else:
+            ctype = int
+        
+        range = ''
+        if self.properties['min'] or self.properties['max']:
+            if ctype == float:
+                range = "%s: %.1f - %.1f)" % (_("range"),
+                                              self.properties['min'], self.properties['max'])
+            elif ctype == int:
+                range = "%s: %d - %d)" % (_("range"),
+                                          self.properties['min'], self.properties['max'])
+        if range:
+            if self.colorTable:
+                self.cr_label.SetLabel(_("Enter vector attribute values or percents %s:") % range)
+            else:
+                self.cr_label.SetLabel(_("Enter vector attribute values %s:") % range)
+        else:
+            if self.colorTable:
+                self.cr_label.SetLabel(_("Enter vector attribute values or percents:"))
+            else:
+                self.cr_label.SetLabel(_("Enter vector attribute values:"))
+                
+    def OnFromColSelection(self, event):
+        """!Selection in combobox (for loading values) changed"""
+        self.properties['loadColumn'] = event.GetString()
+        
+        self.LoadTable()
+    
+    def OnToColSelection(self, event):
+        """!Selection in combobox (for storing values) changed"""
+        self.properties['storeColumn'] = event.GetString()
+    
+    def OnPreview(self, event = None, tmp = True):
+        """!Update preview (based on computational region)"""
+        if self.colorTable:
+            self.OnTablePreview(tmp)
+        else:
+            self.OnColumnPreview() 
+                                 
+    def OnTablePreview(self, tmp):
+        """!Update preview (based on computational region)"""
+        if not self.inmap:
+            self.preview.EraseMap()
+            return
+        
+        ltype = 'vector'
+        cmdlist = ['d.vect',
+                   'map=%s' % self.inmap]
+        
+        # find existing color table and copy to temp file
+        old_colrtable = None
+        path = grass.find_file(name = self.inmap, element = 'vector')['file']
+        
+        if os.path.exists(os.path.join(path, 'colr')):
+            old_colrtable = os.path.join(path, 'colr')
+            colrtemp = utils.GetTempfile()
+            shutil.copyfile(old_colrtable, colrtemp)
+            
+        ColorTable.DoPreview(self, ltype, cmdlist)  
+        
+        # restore previous color table
+        if tmp:
+            if old_colrtable:
+                shutil.copyfile(colrtemp, old_colrtable)
+                os.remove(colrtemp)
+            else:
+                RunCommand('v.colors',
+                           parent = self,
+                           flags = 'r',
+                           map = self.inmap)
+        
+    def OnColumnPreview(self):
+        """!Update preview (based on computational region)"""
+        if not self.inmap or not self.properties['tmpColumn']:
+            self.preview.EraseMap()
+            return
+        
+        cmdlist = ['d.vect',
+                   'map=%s' % self.inmap,
+                   'type=point,line,boundary,area']
+                
+        if self.attributeType == 'color':
+            cmdlist.append('flags=a')
+            cmdlist.append('rgb_column=%s' % self.properties['tmpColumn'])
+        elif self.attributeType == 'size':
+            cmdlist.append('size_column=%s' % self.properties['tmpColumn'])
+        elif self.attributeType == 'width':
+            cmdlist.append('width_column=%s' % self.properties['tmpColumn'])
+            
+        ltype = 'vector'
+        
+        ColorTable.DoPreview(self, ltype, cmdlist)
+        
+    def OnHelp(self, event):
+        """!Show GRASS manual page"""
+        cmd = 'v.colors'
+        ColorTable.RunHelp(self, cmd = cmd)
+        
+    def UseAttrColumn(self, useAttrColumn):
+        """!Find layers and apply the changes in d.vect command"""
+        layers = self.parent.curr_page.maptree.FindItemByData(key = 'name', value = self.inmap)
+        if not layers:
+            return
+        for layer in layers:
+            if self.parent.curr_page.maptree.GetPyData(layer)[0]['type'] != 'vector':
+                continue
+            cmdlist = self.parent.curr_page.maptree.GetPyData(layer)[0]['maplayer'].GetCmd()
+            
+            if self.attributeType == 'color':
+                if useAttrColumn:
+                    cmdlist[1].update({'flags': 'a'})
+                    cmdlist[1].update({'rgb_column': self.properties['storeColumn']})
+                else:
+                    if 'flags' in cmdlist[1]:
+                        cmdlist[1]['flags'] = cmdlist[1]['flags'].replace('a', '')
+                    cmdlist[1].pop('rgb_column', None)
+            elif self.attributeType == 'size':
+                cmdlist[1].update({'size_column': self.properties['storeColumn']})
+            elif self.attributeType == 'width':
+                cmdlist[1].update({'width_column' :self.properties['storeColumn']})
+            self.parent.curr_page.maptree.GetPyData(layer)[0]['cmd'] = cmdlist
+        
+    def CreateColorTable(self, tmp = False):
+        """!Create color rules (color table or color column)"""
+        if self.colorTable:
+            ret = ColorTable.CreateColorTable(self)
+        else:
+            if self.updateColumn:
+                ret = self.UpdateColorColumn(tmp)
+            else:
+                ret = True
+        
+        return ret
+        
+    def UpdateColorColumn(self, tmp):
+        """!Creates color table
+
+        @return True on success
+        @return False on failure
+        """
+        rulestxt = ''
+        
+        for rule in self.rulesPanel.ruleslines.itervalues():
+            if 'value' not in rule: # skip empty rules
+                break
+            
+            if tmp:
+                rgb_col = self.properties['tmpColumn']
+            else:
+                rgb_col = self.properties['storeColumn']
+                if not self.properties['storeColumn']:
+                    GMessage(parent = self.parent,
+                             message = _("Please select column to save values to."))
+            
+            rulestxt += "UPDATE %s SET %s='%s' WHERE %s ;\n" % (self.properties['table'],
+                                                                rgb_col,
+                                                                rule[self.attributeType],
+                                                                rule['value'])
+        if not rulestxt:
+            return False
+        
+        gtemp = utils.GetTempfile()
+        output = open(gtemp, "w")
+        try:
+            output.write(rulestxt)
+        finally:
+            output.close()
+        
+        RunCommand('db.execute',
+                   parent = self,
+                   input = gtemp)
+        
+        return True
+    
+    def OnCancel(self, event):
+        """!Do not apply any changes and close the dialog"""
+        self.DeleteTemporaryColumn()
+        self.Map.Clean()
+        self.Destroy()
+
+    def OnApply(self, event):
+        """!Apply selected color table
+        
+        @return True on success otherwise False
+        """
+        if self.colorTable:
+            self.UseAttrColumn(False)
+        else:
+            if not self.properties['storeColumn']:
+                GError(_("No color column defined. Operation canceled."),
+                       parent = self)
+                return
+            
+            self.UseAttrColumn(True)
+        
+        return ColorTable.OnApply(self, event)
+           
+class BufferedWindow(wx.Window):
+    """!A Buffered window class"""
+    def __init__(self, parent, id,
+                 style = wx.NO_FULL_REPAINT_ON_RESIZE,
+                 Map = None, **kwargs):
+        
+        wx.Window.__init__(self, parent, id, style = style, **kwargs)
+
+        self.parent = parent
+        self.Map = Map
+        
+        # re-render the map from GRASS or just redraw image
+        self.render = True
+        # indicates whether or not a resize event has taken place
+        self.resize = False 
+
+        #
+        # event bindings
+        #
+        self.Bind(wx.EVT_PAINT,        self.OnPaint)
+        self.Bind(wx.EVT_IDLE,         self.OnIdle)
+        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
+
+        #
+        # render output objects
+        #
+        # image file to be rendered
+        self.mapfile = None 
+        # wx.Image object (self.mapfile)
+        self.img = None
+
+        self.pdc = wx.PseudoDC()
+        # will store an off screen empty bitmap for saving to file
+        self._Buffer = None 
+
+        # make sure that extents are updated at init
+        self.Map.region = self.Map.GetRegion()
+        self.Map.SetRegion()
+
+    def Draw(self, pdc, img = None, pdctype = 'image'):
+        """!Draws preview or clears window"""
+        pdc.BeginDrawing()
+
+        Debug.msg (3, "BufferedWindow.Draw(): pdctype=%s" % (pdctype))
+
+        if pdctype == 'clear': # erase the display
+            bg = wx.WHITE_BRUSH
+            pdc.SetBackground(bg)
+            pdc.Clear()
+            self.Refresh()
+            pdc.EndDrawing()
+            return
+
+        if pdctype == 'image' and img:
+            bg = wx.TRANSPARENT_BRUSH
+            pdc.SetBackground(bg)
+            bitmap = wx.BitmapFromImage(img)
+            w, h = bitmap.GetSize()
+            pdc.DrawBitmap(bitmap, 0, 0, True) # draw the composite map
+            
+        pdc.EndDrawing()
+        self.Refresh()
+
+    def OnPaint(self, event):
+        """!Draw pseudo DC to buffer"""
+        self._Buffer = wx.EmptyBitmap(self.Map.width, self.Map.height)
+        dc = wx.BufferedPaintDC(self, self._Buffer)
+        
+        # use PrepareDC to set position correctly
+        self.PrepareDC(dc)
+        
+        # we need to clear the dc BEFORE calling PrepareDC
+        bg = wx.Brush(self.GetBackgroundColour())
+        dc.SetBackground(bg)
+        dc.Clear()
+        
+        # create a clipping rect from our position and size
+        # and the Update Region
+        rgn = self.GetUpdateRegion()
+        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"""
+        # 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)
+
+        # 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.UpdatePreview()
+
+        # re-render image on idle
+        self.resize = True
+
+    def OnIdle(self, event):
+        """!Only re-render a preview image from GRASS during
+        idle time instead of multiple times during resizing.
+        """
+        if self.resize:
+            self.render = True
+            self.UpdatePreview()
+        event.Skip()
+
+    def GetImage(self):
+        """!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
+        
+        return img
+    
+    def UpdatePreview(self, img = None):
+        """!Update canvas if window changes geometry"""
+        Debug.msg (2, "BufferedWindow.UpdatePreview(%s): render=%s" % (img, self.render))
+        oldfont = ""
+        oldencoding = ""
+        
+        if self.render:
+            # extent is taken from current map display
+            try:
+                self.Map.region = copy.deepcopy(self.parent.parent.curr_page.maptree.Map.region)
+            except AttributeError:
+                self.Map.region = self.Map.GetRegion()
+            # render new map images
+            self.mapfile = self.Map.Render(force = self.render)
+            self.img = self.GetImage()
+            self.resize = False
+        
+        if not self.img:
+            return
+        
+        # paint images to PseudoDC
+        self.pdc.Clear()
+        self.pdc.RemoveAll()
+        # draw map image background
+        self.Draw(self.pdc, self.img, pdctype = 'image')
+        
+        self.resize = False
+        
+    def EraseMap(self):
+        """!Erase preview"""
+        self.Draw(self.pdc, pdctype = 'clear')


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/modules/extensions.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/modules/extensions.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/modules/extensions.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,508 @@
+"""!
+ at package modules.extensions
+
+ at brief GRASS Addons extensions management classes
+
+Classes:
+ - extensions::InstallExtensionWindow
+ - extensions::ExtensionTree
+ - extensions::UninstallExtensionWindow
+ - extensions::CheckListExtension
+
+(C) 2008-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+import sys
+
+import wx
+import wx.lib.mixins.listctrl as listmix
+try:
+    import wx.lib.agw.customtreectrl as CT
+except ImportError:
+    import wx.lib.customtreectrl as CT
+import wx.lib.flatnotebook as FN
+
+import grass.script as grass
+from grass.script import task as gtask
+
+from core             import globalvar
+from core.gcmd        import GError, RunCommand
+from gui_core.forms   import GUI
+from gui_core.widgets import ItemTree
+from gui_core.ghelp   import SearchModuleWindow
+
+class InstallExtensionWindow(wx.Frame):
+    def __init__(self, parent, id = wx.ID_ANY,
+                 title = _("Fetch & install extension from GRASS Addons"), **kwargs):
+        self.parent = parent
+        self.options = dict() # list of options
+        
+        wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+        self.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)
+        self.fullDesc = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
+                                    label = _("Fetch full info including description and keywords"))
+        self.fullDesc.SetValue(True)
+        
+        self.search = SearchModuleWindow(parent = self.panel)
+        self.search.SetSelection(0) 
+        
+        self.tree   = ExtensionTree(parent = self.panel, log = parent.GetLogWindow())
+        
+        self.optionBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                      label = " %s " % _("Options"))
+        
+        task = gtask.parse_interface('g.extension.py')
+        
+        ignoreFlags = ['l', 'c', 'g', 'a', 'f', 'quiet', 'verbose']
+        if sys.platform == 'win32':
+            ignoreFlags.append('d')
+            ignoreFlags.append('i')
+        
+        for f in task.get_options()['flags']:
+            name = f.get('name', '')
+            desc = f.get('label', '')
+            if not desc:
+                desc = f.get('description', '')
+            if not name and not desc:
+                continue
+            if name in ignoreFlags:
+                continue
+            self.options[name] = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
+                                             label = desc)
+        self.repo.SetValue(task.get_param(value = 'svnurl').get('default',
+                                                                'http://svn.osgeo.org/grass/grass-addons'))
+        
+        self.statusbar = self.CreateStatusBar(number = 1)
+        
+        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.btnCmd = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                label = _("Command dialog"))
+        self.btnCmd.SetToolTipString(_('Open %s dialog') % 'g.extension.py')
+
+        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.btnCmd.Bind(wx.EVT_BUTTON, self.OnCmdDialog)
+        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)
+
+        # options
+        optionSizer = wx.StaticBoxSizer(self.optionBox, wx.VERTICAL)
+        for key in self.options.keys():
+            optionSizer.Add(item = self.options[key], proportion = 0)
+        
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(item = self.btnCmd, proportion = 0,
+                     flag = wx.RIGHT, border = 5)
+        btnSizer.AddSpacer(10)
+        btnSizer.Add(item = self.btnClose, proportion = 0,
+                     flag = wx.RIGHT, border = 5)
+        btnSizer.Add(item = self.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 = optionSizer, proportion = 0,
+                        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 _getCmd(self):
+        item = self.tree.GetSelected()
+        if not item or not item.IsOk():
+            return ['g.extension.py']
+        
+        name = self.tree.GetItemText(item)
+        if not name:
+            GError(_("Extension not defined"), parent = self)
+            return
+        flags = list()
+        for key in self.options.keys():
+            if self.options[key].IsChecked():
+                flags.append('-%s' % key)
+        
+        return ['g.extension.py'] + flags + ['extension=' + name,
+                                             'svnurl=' + self.repo.GetValue().strip()]
+    
+    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"""
+        wx.BeginBusyCursor()
+        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)
+        wx.EndBusyCursor()
+
+    def OnItemActivated(self, event):
+        item = event.GetItem()
+        data = self.tree.GetPyData(item)
+        if data and 'command' in data:
+            self.OnInstall(event = None)
+        
+    def OnInstall(self, event):
+        """!Install selected extension"""
+        log = self.parent.GetLogWindow()
+        log.RunCmd(self._getCmd(), onDone = self.OnDone)
+        
+    def OnDone(self, cmd, returncode):
+        item = self.tree.GetSelected()
+        if not item or not item.IsOk() or \
+                returncode != 0 or \
+                not os.getenv('GRASS_ADDON_PATH'):
+            return
+        
+        name = self.tree.GetItemText(item)
+        globalvar.grassCmd.add(name)
+        
+    def OnItemSelected(self, event):
+        """!Item selected"""
+        item = event.GetItem()
+        self.tree.itemSelected = item
+        data = self.tree.GetPyData(item)
+        if data is None:
+            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)
+
+    def OnCmdDialog(self, event):
+        """!Shows command dialog"""
+        GUI(parent = self).ParseCommand(cmd = self._getCmd())
+        
+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', 'wxGUI', 'other'):
+            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',
+                 'wx' : 'wxGUI',
+                 ''   : 'other' }
+        
+        if c in name:
+            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 = RunCommand('g.extension.py', read = True, parent = self,
+                         svnurl = url,
+                         flags = flags, quiet = True)
+        if not ret:
+            return
+        
+        mdict = dict()
+        for line in ret.splitlines():
+            if full:
+                try:
+                    key, value = line.split('=', 1)
+                except ValueError:
+                    key = 'name'
+                    value = line
+                
+                if key == 'name':
+                    try:
+                        prefix, name = value.split('.', 1)
+                    except ValueError:
+                        prefix = ''
+                        name = value
+                    if prefix not in mdict:
+                        mdict[prefix] = dict()
+                    mdict[prefix][name] = dict()
+                else:
+                    mdict[prefix][name][key] = value
+            else:
+                try:
+                    prefix, name = line.strip().split('.', 1)
+                except:
+                    prefix = ''
+                    name = line.strip()
+                
+                if self._expandPrefix(prefix) == prefix:
+                    prefix = ''
+                    
+                if prefix not in mdict:
+                    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:
+                if prefix:
+                    text = prefix + '.' + name
+                else:
+                    text = name
+                new = self.AppendItem(parentId = item,
+                                      text = text)
+                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 UninstallExtensionWindow(wx.Frame):
+    def __init__(self, parent, id = wx.ID_ANY,
+                 title = _("Uninstall GRASS Addons extensions"), **kwargs):
+        self.parent = parent
+        
+        wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+
+        self.extBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                   label = " %s " % _("List of installed extensions"))
+        
+        self.extList = CheckListExtension(parent = self.panel)
+
+        # buttons
+        self.btnUninstall = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                    label = _("&Uninstall"))
+        self.btnUninstall.SetToolTipString(_("Uninstall selected AddOns extensions"))
+        self.btnCmd = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                label = _("Command dialog"))
+        self.btnCmd.SetToolTipString(_('Open %s dialog') % 'g.extension')
+        self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
+        
+        self.btnUninstall.Bind(wx.EVT_BUTTON, self.OnUninstall)
+        self.btnCmd.Bind(wx.EVT_BUTTON, self.OnCmdDialog)
+        self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
+        
+        self._layout()
+        
+    def _layout(self):
+        """!Do layout"""
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        extSizer = wx.StaticBoxSizer(self.extBox, wx.HORIZONTAL)
+        extSizer.Add(item = self.extList, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 1)
+        
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(item = self.btnCmd, proportion = 0,
+                     flag = wx.RIGHT, border = 5)
+        btnSizer.AddSpacer(10)
+        btnSizer.Add(item = self.btnClose, proportion = 0,
+                     flag = wx.RIGHT, border = 5)
+        btnSizer.Add(item = self.btnUninstall, proportion = 0)
+        
+        sizer.Add(item = extSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        sizer.Add(item = btnSizer, proportion = 0,
+                  flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+        
+        self.panel.SetSizer(sizer)
+        sizer.Fit(self.panel)
+        
+        self.Layout()
+
+    def OnCloseWindow(self, event):
+        """!Close window"""
+        self.Destroy()
+
+    def OnUninstall(self, event):
+        """!Uninstall selected extensions"""
+        log = self.parent.GetLogWindow()
+        eList = self.extList.GetExtensions()
+        if not eList:
+            GError(_("No extension selected for removal. "
+                     "Operation canceled."),
+                   parent = self)
+            return
+        
+        for ext in eList:
+            files = RunCommand('g.extension.py', parent = self, read = True, quiet = True,
+                               extension = ext, operation = 'remove').splitlines()
+            dlg = wx.MessageDialog(parent = self,
+                                   message = _("List of files to be removed:\n%(files)s\n\n"
+                                               "Do you want really to remove <%(ext)s> extension?") % \
+                                       { 'files' : os.linesep.join(files), 'ext' : ext },
+                                   caption = _("Remove extension"),
+                                   style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
+            
+            if dlg.ShowModal() ==  wx.ID_YES:
+                RunCommand('g.extension.py', flags = 'f', parent = self, quiet = True,
+                           extension = ext, operation = 'remove')
+        
+        self.extList.LoadData()
+        
+    def OnCmdDialog(self, event):
+        """!Shows command dialog"""
+        GUI(parent = self).ParseCommand(cmd = ['g.extension.py'])
+
+class CheckListExtension(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin):
+    """!List of mapset/owner/group"""
+    def __init__(self, parent):
+        self.parent = parent
+        
+        wx.ListCtrl.__init__(self, parent, id = wx.ID_ANY,
+                             style = wx.LC_REPORT)
+        listmix.CheckListCtrlMixin.__init__(self)
+        
+        # setup mixins
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+
+        self.InsertColumn(0, _('Extension'))
+        self.LoadData()
+        
+    def LoadData(self):
+        """!Load data into list"""
+        self.DeleteAllItems()
+        for ext in RunCommand('g.extension.py',
+                              quiet = True, parent = self, read = True,
+                              flags = 'a').splitlines():
+            if ext:
+                self.InsertStringItem(sys.maxint, ext)
+
+    def GetExtensions(self):
+        """!Get extensions to be un-installed
+        """
+        extList = list()
+        for i in range(self.GetItemCount()):
+            if self.IsChecked(i):
+                name = self.GetItemText(i)
+                if name:
+                    extList.append(name)
+        
+        return extList


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/modules/histogram.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/modules/histogram.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/modules/histogram.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,493 @@
+"""!
+ at package modules.histogram
+
+Plotting histogram based on d.histogram
+
+Classes:
+ - histogram::BufferedWindow
+ - histogram::HistogramFrame
+ - histogram::HistogramToolbar
+
+(C) 2007, 2010-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton
+ at author Various updates by Martin Landa <landa.martin gmail.com>
+"""
+
+import os
+
+import wx
+
+from core                 import globalvar
+from core.render          import Map
+from gui_core.forms       import GUI
+from mapdisp.gprint       import PrintOptions
+from core.utils           import GetLayerNameFromCmd
+from gui_core.dialogs     import GetImageHandlers, ImageSizeDialog
+from gui_core.preferences import DefaultFontDialog
+from core.debug           import Debug
+from core.gcmd            import GError
+from gui_core.toolbars    import BaseToolbar, BaseIcons
+
+class BufferedWindow(wx.Window):
+    """!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 =  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
+        #
+        self.render = True  # re-render the map from GRASS or just redraw image
+        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
+        
+        # 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]):
+        """!Draws histogram or clears window
+        """
+        if drawid == None:
+            if pdctype == 'image' :
+                drawid = imagedict[img]
+            elif pdctype == 'clear':
+                drawid == None
+            else:
+                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)
+            pdc.Clear()
+            self.Refresh()
+            pdc.EndDrawing()
+            return
+        
+        if pdctype == 'image':
+            bg = wx.TRANSPARENT_BRUSH
+            pdc.SetBackground(bg)
+            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))
+        
+        pdc.EndDrawing()
+        self.Refresh()
+        
+    def OnPaint(self, event):
+        """!Draw psuedo DC to 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
+        bg = wx.Brush(self.GetBackgroundColour())
+        dc.SetBackground(bg)
+        dc.Clear()
+        # create a clipping rect from our position and size
+        # and the Update Region
+        rgn = self.GetUpdateRegion()
+        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
+        """
+        # 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)
+        
+        # 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.
+        """
+        if self.resize:
+            self.render = True
+            self.UpdateHist()
+        event.Skip()
+        
+    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
+        """
+        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)
+        ibuffer.SaveFile(FileName, FileType)
+        
+        busy.Destroy()
+        
+    def GetImage(self):
+        """!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):
+        """!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"]
+            if self.parent.font != "": os.environ["GRASS_FONT"] = self.parent.font
+            if "GRASS_ENCODING" in os.environ:
+                oldencoding = os.environ["GRASS_ENCODING"]
+            if self.parent.encoding != None and self.parent.encoding != "ISO-8859-1":
+                os.environ[GRASS_ENCODING] = self.parent.encoding
+            
+            # using active comp region
+            self.Map.GetRegion(update = True)
+            
+            self.Map.width, self.Map.height = self.GetClientSize()
+            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.resize = False
+        
+        # update statusbar
+        # Debug.msg (3, "BufferedWindow.UpdateHist(%s): region=%s" % self.Map.region)
+        self.Map.SetRegion()
+        self.parent.statusbar.SetStatusText("Image/Raster map <%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
+        """
+        self.Draw(self.pdc, pdctype = 'clear')
+        
+class HistogramFrame(wx.Frame):
+    """!Main frame for hisgram display window. Uses d.histogram
+    rendered onto canvas
+    """
+    def __init__(self, parent = None, id = wx.ID_ANY,
+                 title = _("GRASS GIS Histogramming Tool (d.histogram)"),
+                 size = wx.Size(500, 350),
+                 style = wx.DEFAULT_FRAME_STYLE, **kwargs):
+        wx.Frame.__init__(self, parent, id, title, size = size, style = style, **kwargs)
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        
+        self.Map   = Map()         # instance of render.Map to be associated with display
+        self.layer = None          # reference to layer with histogram
+        
+        # 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.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
+        
+        # Bind various events
+        self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
+        
+        # Init print module and classes
+        self.printopt = 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 InitDisplay(self):
+        """!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"""
+        cmd = ['d.histogram']
+        if self.mapname != '':
+            cmd.append('map=%s' % self.mapname)
+        
+        GUI(parent = self).ParseCommand(cmd,
+                                        completed = (self.GetOptData, None, self.params))
+        
+    def GetOptData(self, dcmd, layer, params, propwin):
+        """!Callback method for histogram command generated by dialog
+        created in menuform.py
+        """
+        if dcmd:
+            name, found = GetLayerNameFromCmd(dcmd, fullyQualified = True,
+                                              layerType = 'raster')
+            if not found:
+                GError(parent = propwin,
+                       message = _("Raster map <%s> not found") % name)
+                return
+            
+            self.SetHistLayer(name)
+        self.params = params
+        self.propwin = propwin
+        
+        self.HistWindow.UpdateHist()
+        
+    def SetHistLayer(self, name):
+        """!Set histogram layer
+        """
+        self.mapname = name
+        
+        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.
+        """
+        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
+        """
+        self.HistWindow.Draw(self.HistWindow.pdc, pdctype = 'clear')
+        
+    def OnRender(self, event):
+        """!Re-render histogram
+        """
+        self.HistWindow.UpdateHist()
+        
+    def GetWindow(self):
+        """!Get buffered window"""
+        return self.HistWindow
+    
+    def SaveToFile(self, event):
+        """!Save to file
+        """
+        filetype, ltype = GetImageHandlers(self.HistWindow.img)
+        
+        # get size
+        dlg = 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:
+            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
+        """
+        point = wx.GetMousePosition()
+        printmenu = wx.Menu()
+        # Add items to the menu
+        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, id = wx.ID_ANY, text = _('Print preview'))
+        printmenu.AppendItem(preview)
+        self.Bind(wx.EVT_MENU, self.printopt.OnPrintPreview, preview)
+        
+        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
+        Also remove associated rendered images
+        """
+        try:
+            self.propwin.Close(True)
+        except:
+            pass
+        self.Map.Clean()
+        self.Destroy()
+
+class HistogramToolbar(BaseToolbar):
+    """!Histogram toolbar (see histogram.py)
+    """
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        return self._getToolbarData((('histogram', BaseIcons["histogramD"],
+                                      self.parent.OnOptions),
+                                     ('render', BaseIcons["display"],
+                                      self.parent.OnRender),
+                                     ('erase', BaseIcons["erase"],
+                                      self.parent.OnErase),
+                                     ('font', BaseIcons["font"],
+                                      self.parent.SetHistFont),
+                                     (None, ),
+                                     ('save', BaseIcons["saveFile"],
+                                      self.parent.SaveToFile),
+                                     ('hprint', BaseIcons["print"],
+                                      self.parent.PrintMenu),
+                                     (None, ),
+                                     ('quit', BaseIcons["quit"],
+                                      self.parent.OnQuit))
+                                    )


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/modules/mcalc_builder.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/modules/mcalc_builder.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/modules/mcalc_builder.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,550 @@
+"""!
+ at package modules::mcalc_builder
+
+ at brief Map calculator, GUI wrapper for r.mapcalc
+
+Classes:
+ - mcalc_builder::MapCalcFrame
+
+(C) 2008, 2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton, Arizona State University
+ at author Martin Landa <landa.martin gmail.com>
+ at author Tim Michelsen (load/save expression)
+"""
+
+import os
+import sys
+
+if __name__ == "__main__":
+    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython'))
+from core import globalvar
+import wx
+
+import grass.script as grass
+
+from core.gcmd        import GError, RunCommand
+from gui_core.gselect import Select
+from core.settings    import UserSettings
+
+
+class MapCalcFrame(wx.Frame):
+    """!Mapcalc Frame class. Calculator-style window to create and run
+    r(3).mapcalc statements.
+    """
+    def __init__(self, parent, cmd, id = wx.ID_ANY,
+                 style = wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER, **kwargs):
+        self.parent = parent
+        if self.parent:
+            self.log = self.parent.GetLogWindow()
+        else:
+            self.log = None
+        
+        # grass command
+        self.cmd = cmd
+
+        if self.cmd == 'r.mapcalc':
+            self.rast3d = False
+            title = _('GRASS GIS Raster Map Calculator')
+        if self.cmd == 'r3.mapcalc':
+            self.rast3d = True
+            title = _('GRASS GIS 3D Raster Map Calculator')
+            
+        wx.Frame.__init__(self, parent, id = id, title = title, **kwargs)
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        
+        self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
+        self.CreateStatusBar()
+        
+        #
+        # variables
+        #
+        self.heading = _('mapcalc statement')
+        self.funct_dict = {
+            'abs(x)':'abs()',
+            'acos(x)':'acos()',
+            'asin(x)':'asin()',
+            'atan(x)':'atan()',
+            'atan(x,y)':'atan( , )',
+            'cos(x)':'cos()',
+            'double(x)':'double()',
+            'eval([x,y,...,]z)':'eval()',
+            'exp(x)':'exp()',
+            'exp(x,y)':'exp( , )',
+            'float(x)':'float()',
+            'graph(x,x1,y1[x2,y2..])':'graph( , , )',
+            'if(x)':'if()',
+            'if(x,a)':'if( , )',
+            'if(x,a,b)':'if( , , )',
+            'if(x,a,b,c)':'if( , , , )',
+            'int(x)':'if()',
+            'isnull(x)':'isnull()',
+            'log(x)':'log(',
+            'log(x,b)':'log( , )',
+            'max(x,y[,z...])':'max( , )',
+            'median(x,y[,z...])':'median( , )',
+            'min(x,y[,z...])':'min( , )',
+            'mode(x,y[,z...])':'mode( , )',
+            'not(x)':'not()',
+            'pow(x,y)':'pow( , )',
+            'rand(a,b)':'rand( , )',
+            'round(x)':'round()',
+            'sin(x)':'sin()',
+            'sqrt(x)':'sqrt()',
+            'tan(x)':'tan()',
+            'xor(x,y)':'xor( , )',
+            'row()':'row()',
+            'col()':'col()',
+            'x()':'x()',
+            'y()':'y()',
+            'ewres()':'ewres()',
+            'nsres()':'nsres()',
+            'null()':'null()'
+            }
+        
+        if self.rast3d:
+            self.funct_dict['z()'] = 'z()'
+            self.funct_dict['tbres()'] = 'tbres()'
+            element = 'rast3d'
+        else:
+            element = 'cell'
+        
+        self.operatorBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                        label=" %s " % _('Operators'))
+        self.operandBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                       label=" %s " % _('Operands'))
+        self.expressBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
+                                       label=" %s " % _('Expression'))
+        
+        #
+        # Buttons
+        #
+        self.btn_clear = wx.Button(parent = self.panel, id = wx.ID_CLEAR)
+        self.btn_help = wx.Button(parent = self.panel, id = wx.ID_HELP)
+        self.btn_run = wx.Button(parent = self.panel, id = wx.ID_ANY, label = _("&Run"))
+        self.btn_run.SetForegroundColour(wx.Colour(35, 142, 35))
+        self.btn_run.SetDefault()
+        self.btn_close = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
+        self.btn_save = wx.Button(parent = self.panel, id = wx.ID_SAVE)
+        self.btn_save.SetToolTipString(_('Save expression to file'))
+        self.btn_load = wx.Button(parent = self.panel, id = wx.ID_ANY,
+                                  label = _("&Load"))
+        self.btn_load.SetToolTipString(_('Load expression from file'))
+        
+        self.btn = dict()        
+        self.btn['pow'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "^")
+        self.btn['pow'].SetToolTipString(_('exponent'))
+        self.btn['div'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "/")
+        self.btn['div'].SetToolTipString(_('divide'))
+        self.btn['add'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "+")
+        self.btn['add'].SetToolTipString(_('add'))
+        self.btn['minus'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "-")
+        self.btn['minus'].SetToolTipString(_('subtract'))
+        self.btn['mod'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "%")
+        self.btn['mod'].SetToolTipString(_('modulus'))
+        self.btn['mult'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "*")
+        self.btn['mult'].SetToolTipString(_('multiply'))
+
+        self.btn['parenl'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "(") 
+        self.btn['parenr'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = ")")
+        self.btn['lshift'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "<<")
+        self.btn['lshift'].SetToolTipString(_('left shift'))
+        self.btn['rshift'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = ">>")
+        self.btn['rshift'].SetToolTipString(_('right shift'))
+        self.btn['rshiftu'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = ">>>")
+        self.btn['rshiftu'].SetToolTipString(_('right shift (unsigned)'))
+        self.btn['gt'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = ">")
+        self.btn['gt'].SetToolTipString(_('greater than'))
+        self.btn['gteq'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = ">=")
+        self.btn['gteq'].SetToolTipString(_('greater than or equal to'))
+        self.btn['lt'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "<")
+        self.btn['lt'].SetToolTipString(_('less than'))
+        self.btn['lteq'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "<=")
+        self.btn['lteq'].SetToolTipString(_('less than or equal to'))
+        self.btn['eq'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "==")
+        self.btn['eq'].SetToolTipString(_('equal to'))
+        self.btn['noteq'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "!=")
+        self.btn['noteq'].SetToolTipString(_('not equal to'))
+
+        self.btn['compl'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "~")
+        self.btn['compl'].SetToolTipString(_('one\'s complement'))
+        self.btn['not'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "!")
+        self.btn['not'].SetToolTipString(_('NOT'))
+        self.btn['andbit'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = '&&')
+        self.btn['andbit'].SetToolTipString(_('bitwise AND'))
+        self.btn['orbit'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "|")
+        self.btn['orbit'].SetToolTipString(_('bitwise OR'))
+        self.btn['and'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "&&&&")
+        self.btn['and'].SetToolTipString(_('logical AND'))
+        self.btn['andnull'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "&&&&&&")
+        self.btn['andnull'].SetToolTipString(_('logical AND (ignores NULLs)'))
+        self.btn['or'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "||")
+        self.btn['or'].SetToolTipString(_('logical OR'))
+        self.btn['ornull'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "|||")
+        self.btn['ornull'].SetToolTipString(_('logical OR (ignores NULLs)'))
+        self.btn['cond'] = wx.Button(parent = self.panel, id = wx.ID_ANY, label = "a ? b : c") 
+        self.btn['cond'].SetToolTipString(_('conditional'))
+        
+        #
+        # Text area
+        #
+        self.text_mcalc = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY, size = (-1, 75),
+                                      style = wx.TE_MULTILINE)
+        wx.CallAfter(self.text_mcalc.SetFocus)
+        
+        #
+        # Map and function insertion text and ComboBoxes
+        self.newmaplabel = wx.StaticText(parent = self.panel, id = wx.ID_ANY)
+        if self.rast3d:
+            self.newmaplabel.SetLabel(_('Name for new 3D raster map to create'))
+        else:
+            self.newmaplabel.SetLabel(_('Name for new raster map to create'))
+        self.newmaptxt = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY, size=(250, -1))
+        self.mapsellabel = wx.StaticText(parent = self.panel, id = wx.ID_ANY)
+        if self.rast3d:
+            self.mapsellabel.SetLabel(_('Insert existing 3D raster map'))
+        else:
+            self.mapsellabel.SetLabel(_('Insert existing raster map'))
+        self.mapselect = Select(parent = self.panel, id = wx.ID_ANY, size = (250, -1),
+                                type = element, multiple = False)
+        self.functlabel = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
+                                        label = _('Insert mapcalc function'))
+        self.function = wx.ComboBox(parent = self.panel, id = wx.ID_ANY, 
+                                    size = (250, -1), choices = sorted(self.funct_dict.keys()),
+                                    style = wx.CB_DROPDOWN |
+                                    wx.CB_READONLY | wx.TE_PROCESS_ENTER)
+        
+        self.addbox = wx.CheckBox(parent=self.panel,
+                                  label=_('Add created raster map into layer tree'), style = wx.NO_BORDER)
+        self.addbox.SetValue(UserSettings.Get(group='cmd', key='addNewLayer', subkey='enabled'))
+        if not self.parent or self.parent.GetName() != 'LayerManager':
+            self.addbox.Hide()
+        
+        #
+        # Bindings
+        #
+        for btn in self.btn.keys():
+            self.btn[btn].Bind(wx.EVT_BUTTON, self.AddMark)
+        
+        self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
+        self.btn_clear.Bind(wx.EVT_BUTTON, self.OnClear)
+        self.btn_run.Bind(wx.EVT_BUTTON, self.OnMCalcRun)
+        self.btn_help.Bind(wx.EVT_BUTTON, self.OnHelp)
+        self.btn_save.Bind(wx.EVT_BUTTON, self.OnSaveExpression)
+        self.btn_load.Bind(wx.EVT_BUTTON, self.OnLoadExpression)
+        
+        self.mapselect.Bind(wx.EVT_TEXT, self.OnSelect)
+        self.function.Bind(wx.EVT_COMBOBOX, self._return_funct)
+        self.function.Bind(wx.EVT_TEXT_ENTER, self.OnSelect)
+        self.newmaptxt.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
+        self.text_mcalc.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
+
+        self._layout()
+
+        self.SetMinSize(self.GetBestSize())
+    
+    def _return_funct(self,event):
+	i = event.GetString()
+	self._addSomething(self.funct_dict[i])
+    
+    def _layout(self):
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        controlSizer = wx.BoxSizer(wx.HORIZONTAL)
+        operatorSizer = wx.StaticBoxSizer(self.operatorBox, wx.HORIZONTAL)
+
+        buttonSizer1 = wx.GridBagSizer(5, 1)
+        buttonSizer1.Add(item = self.btn['add'], pos = (0,0))
+        buttonSizer1.Add(item = self.btn['minus'], pos = (0,1))
+        buttonSizer1.Add(item = self.btn['mod'], pos = (5,0))
+        buttonSizer1.Add(item = self.btn['mult'], pos = (1,0))
+        buttonSizer1.Add(item = self.btn['div'], pos = (1,1))
+        buttonSizer1.Add(item = self.btn['pow'], pos = (5,1))
+        buttonSizer1.Add(item = self.btn['gt'], pos = (2,0))
+        buttonSizer1.Add(item = self.btn['gteq'], pos = (2,1))
+        buttonSizer1.Add(item = self.btn['eq'], pos = (4,0))
+        buttonSizer1.Add(item = self.btn['lt'], pos = (3,0))
+        buttonSizer1.Add(item = self.btn['lteq'], pos = (3,1))
+        buttonSizer1.Add(item = self.btn['noteq'], pos = (4,1))
+
+        buttonSizer2 = wx.GridBagSizer(5, 1)
+        buttonSizer2.Add(item = self.btn['and'], pos = (0,0))
+        buttonSizer2.Add(item = self.btn['andbit'], pos = (1,0))
+        buttonSizer2.Add(item = self.btn['andnull'], pos = (2,0))
+        buttonSizer2.Add(item = self.btn['or'], pos = (0,1))
+        buttonSizer2.Add(item = self.btn['orbit'], pos = (1,1))
+        buttonSizer2.Add(item = self.btn['ornull'], pos = (2,1))
+        buttonSizer2.Add(item = self.btn['lshift'], pos = (3,0))
+        buttonSizer2.Add(item = self.btn['rshift'], pos = (3,1))
+        buttonSizer2.Add(item = self.btn['rshiftu'], pos = (4,0))
+        buttonSizer2.Add(item = self.btn['cond'], pos = (5,0))
+        buttonSizer2.Add(item = self.btn['compl'], pos = (5,1))
+        buttonSizer2.Add(item = self.btn['not'], pos = (4,1))
+
+        operandSizer = wx.StaticBoxSizer(self.operandBox, wx.HORIZONTAL)
+        buttonSizer3 = wx.GridBagSizer(7, 1)
+        buttonSizer3.Add(item = self.newmaplabel, pos = (0,0),
+                         span = (1, 2), flag = wx.ALIGN_CENTER)
+        buttonSizer3.Add(item = self.newmaptxt, pos = (1,0),
+                         span = (1, 2))
+        buttonSizer3.Add(item = self.functlabel, pos = (2,0),
+                         span = (1,2), flag = wx.ALIGN_CENTER)
+        buttonSizer3.Add(item = self.function, pos = (3,0),
+                         span = (1,2))                         
+        buttonSizer3.Add(item = self.mapsellabel, pos = (4,0),
+                         span = (1,2), flag = wx.ALIGN_CENTER)
+        buttonSizer3.Add(item = self.mapselect, pos = (5,0),
+                         span = (1,2))
+        threebutton = wx.GridBagSizer(1, 2)
+        threebutton.Add(item = self.btn['parenl'], pos = (0,0),
+                         span = (1,1), flag = wx.ALIGN_LEFT)
+        threebutton.Add(item = self.btn['parenr'], pos = (0,1),
+                         span = (1,1), flag = wx.ALIGN_CENTER)
+        threebutton.Add(item = self.btn_clear, pos = (0,2),
+                         span = (1,1), flag = wx.ALIGN_RIGHT)
+        buttonSizer3.Add(item = threebutton, pos = (6,0),
+	                 span = (1,1), flag = wx.ALIGN_CENTER)
+
+        buttonSizer4 = wx.BoxSizer(wx.HORIZONTAL)
+        buttonSizer4.AddSpacer(10)
+        buttonSizer4.Add(item = self.btn_load,
+                         flag = wx.ALL, border = 5)
+        buttonSizer4.Add(item = self.btn_save,
+                         flag = wx.ALL, border = 5)                         
+        buttonSizer4.AddSpacer(30)
+        buttonSizer4.Add(item = self.btn_help,
+                         flag = wx.ALL, border = 5)
+        buttonSizer4.Add(item = self.btn_run,
+                         flag = wx.ALL, border = 5)
+        buttonSizer4.Add(item = self.btn_close,
+                         flag = wx.ALL, border = 5)
+        
+        operatorSizer.Add(item = buttonSizer1, proportion = 0,
+                          flag = wx.ALL | wx.EXPAND, border = 5)
+        operatorSizer.Add(item = buttonSizer2, proportion = 0,
+                          flag = wx.TOP | wx.BOTTOM | wx.RIGHT | wx.EXPAND, border = 5)
+        
+        operandSizer.Add(item = buttonSizer3, proportion = 0,
+                         flag = wx.TOP | wx.BOTTOM | wx.RIGHT, border = 5)
+        
+        controlSizer.Add(item = operatorSizer, proportion = 1,
+                         flag = wx.RIGHT | wx.EXPAND, border = 5)
+        controlSizer.Add(item = operandSizer, proportion = 0,
+                         flag = wx.EXPAND)
+
+        expressSizer = wx.StaticBoxSizer(self.expressBox, wx.HORIZONTAL)
+        expressSizer.Add(item = self.text_mcalc, proportion = 1,
+                         flag = wx.EXPAND)
+
+        sizer.Add(item = controlSizer, proportion = 0,
+                  flag = wx.EXPAND | wx.ALL,
+                  border = 5)        
+        sizer.Add(item = expressSizer, proportion = 1,
+                  flag = wx.EXPAND | wx.LEFT | wx.RIGHT,
+                  border = 5)
+        sizer.Add(item = buttonSizer4, proportion = 0,
+                  flag = wx.ALIGN_RIGHT | wx.ALL, border = 3)
+        if self.addbox.IsShown():
+            sizer.Add(item = self.addbox, proportion = 0,
+                      flag = wx.LEFT | wx.RIGHT,
+                      border = 5)
+        
+        self.panel.SetAutoLayout(True)
+        self.panel.SetSizer(sizer)
+        sizer.Fit(self.panel)
+        
+        self.Layout()
+        
+    def AddMark(self,event):
+        """!Sends operators to insertion method
+        """
+        if event.GetId() == self.btn['compl'].GetId(): mark = "~"
+        elif event.GetId() == self.btn['not'].GetId(): mark = "!"
+        elif event.GetId() == self.btn['pow'].GetId(): mark = "^"
+        elif event.GetId() == self.btn['div'].GetId(): mark = "/"
+        elif event.GetId() == self.btn['add'].GetId(): mark = "+"
+        elif event.GetId() == self.btn['minus'].GetId(): mark = "-"
+        elif event.GetId() == self.btn['mod'].GetId(): mark = "%"
+        elif event.GetId() == self.btn['mult'].GetId(): mark = "*"
+        elif event.GetId() == self.btn['lshift'].GetId(): mark = "<<"
+        elif event.GetId() == self.btn['rshift'].GetId(): mark = ">>"
+        elif event.GetId() == self.btn['rshiftu'].GetId(): mark = ">>>"
+        elif event.GetId() == self.btn['gt'].GetId(): mark = ">"
+        elif event.GetId() == self.btn['gteq'].GetId(): mark = ">="
+        elif event.GetId() == self.btn['lt'].GetId(): mark = "<"
+        elif event.GetId() == self.btn['lteq'].GetId(): mark = "<="
+        elif event.GetId() == self.btn['eq'].GetId(): mark = "=="
+        elif event.GetId() == self.btn['noteq'].GetId(): mark = "!="
+        elif event.GetId() == self.btn['andbit'].GetId(): mark = "&"
+        elif event.GetId() == self.btn['orbit'].GetId(): mark = "|"
+        elif event.GetId() == self.btn['or'].GetId(): mark =  "||"
+        elif event.GetId() == self.btn['ornull'].GetId(): mark = "|||"
+        elif event.GetId() == self.btn['and'].GetId(): mark = "&&"
+        elif event.GetId() == self.btn['andnull'].GetId(): mark = "&&&"
+        elif event.GetId() == self.btn['cond'].GetId(): mark = " ? : "
+        elif event.GetId() == self.btn['parenl'].GetId(): mark = "("
+        elif event.GetId() == self.btn['parenr'].GetId(): mark = ")"
+        self._addSomething(mark)
+        
+    def OnSelect(self, event):
+        """!Gets raster map or function selection and send it to
+        insertion method
+        """
+        item = event.GetString()
+        self._addSomething(item)
+
+    def OnUpdateStatusBar(self, event):
+        """!Update statusbar text"""
+        expr = self.text_mcalc.GetValue().strip().replace("\n", " ")
+        self.SetStatusText("r.mapcalc '%s = %s'" % (self.newmaptxt.GetValue(),
+                                                    expr))
+        event.Skip()
+        
+    def _addSomething(self, what):
+        """!Inserts operators, map names, and functions into text area
+        """
+        self.text_mcalc.SetFocus()
+        mcalcstr  = self.text_mcalc.GetValue()
+        position  = self.text_mcalc.GetInsertionPoint()
+        
+        newmcalcstr = mcalcstr[:position]
+        
+        position_offset = 0
+        try:
+            if newmcalcstr[-1] != ' ':
+                newmcalcstr += ' '
+                position_offset += 1
+        except:
+            pass
+        
+        newmcalcstr += what + ' ' + mcalcstr[position:]
+        position_offset += len(what)
+        
+        self.text_mcalc.SetValue(newmcalcstr)
+        if len(what) > 1 and what[-2:] == '()':
+            position_offset -= 1
+        self.text_mcalc.SetInsertionPoint(position + position_offset)
+        self.text_mcalc.Update()
+        
+    def OnMCalcRun(self,event):
+        """!Builds and runs r.mapcalc statement
+        """
+        name = self.newmaptxt.GetValue().strip()
+        if not name:
+            GError(parent = self,
+                   message = _("You must enter the name of "
+                               "a new raster map to create."))
+            return
+        
+        expr = self.text_mcalc.GetValue().strip().replace("\n", " ")
+        if not expr:
+            GError(parent = self,
+                   message = _("You must enter an expression "
+                               "to create a new raster map."))
+            return
+        
+        if self.log:
+            cmd = [self.cmd, str('%s = %s' % (name, expr))]
+            self.log.RunCmd(cmd, onDone = self.OnDone)
+            self.parent.Raise()
+        else:
+            RunCommand(self.cmd,
+                       "%s=%s" % (name, expr))
+        
+    def OnDone(self, cmd, returncode):
+        """!Add create map to the layer tree"""
+        if not self.addbox.IsChecked():
+            return
+        name = self.newmaptxt.GetValue().strip() + '@' + grass.gisenv()['MAPSET']
+        mapTree = self.parent.GetLayerTree()
+        if not mapTree.GetMap().GetListOfLayers(l_name = name):
+            mapTree.AddLayer(ltype = 'raster',
+                             lname = name,
+                             lcmd = ['d.rast', 'map=%s' % name],
+                             multiple = False)
+        
+        display = self.parent.GetLayerTree().GetMapDisplay()
+        if display and display.IsAutoRendered():
+            display.GetWindow().UpdateMap(render = True)
+        
+    def OnSaveExpression(self, event):
+        """!Saves expression to file
+        """
+        mctxt = self.newmaptxt.GetValue() + ' = ' + self.text_mcalc.GetValue() + os.linesep
+        
+        #dialog
+        dlg = wx.FileDialog(parent = self,
+                            message = _("Choose a file name to save the expression"),
+                            wildcard = _("Expression file (*)|*"),
+                            style = wx.SAVE | wx.FD_OVERWRITE_PROMPT)
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            if not path:
+                dlg.Destroy()
+                return
+            
+            try:
+                fobj = open(path, 'w')
+                fobj.write(mctxt)
+            finally:
+                fobj.close()
+        
+        dlg.Destroy()
+
+    def OnLoadExpression(self, event):
+        """!Load expression from file
+        """
+        dlg = wx.FileDialog(parent = self,
+                            message = _("Choose a file name to load the expression"),
+                            wildcard = _("Expression file (*)|*"),
+                            style = wx.OPEN)
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            if not path:
+                dlg.Destroy()
+                return
+
+            try:
+                fobj = open(path,'r')
+                mctxt = fobj.read()
+            finally:
+                fobj.close()
+            
+            try:
+                result, exp = mctxt.split('=', 1)
+            except ValueError:
+                result = ''
+                exp = mctxt
+            
+            self.newmaptxt.SetValue(result.strip())
+            self.text_mcalc.SetValue(exp.strip())
+            self.text_mcalc.SetFocus()
+            self.text_mcalc.SetInsertionPointEnd()
+        
+        dlg.Destroy()
+                
+    def OnClear(self, event):
+        """!Clears text area
+        """
+        self.text_mcalc.SetValue('')
+        
+    def OnHelp(self, event):
+        """!Launches r.mapcalc help
+        """
+        RunCommand('g.manual', parent = self, entry = self.cmd)
+        
+    def OnClose(self,event):
+        """!Close window"""
+        self.Destroy()
+        
+if __name__ == "__main__":
+    import gettext
+    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+    
+    app = wx.App(0)
+    frame = MapCalcFrame(parent = None, cmd = 'r.mapcalc')
+    frame.Show()
+    app.MainLoop()


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

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


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

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


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/nviz/animation.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/nviz/animation.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/nviz/animation.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,207 @@
+"""!
+ at package nviz.animation
+
+ at brief Nviz (3D view) animation
+
+Classes:
+ - animation::Animation
+
+(C) 2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Kratochvilova <kratochanna gmail.com> 
+"""
+
+import os
+import copy
+
+import wx
+from wx.lib.newevent import NewEvent
+
+wxAnimationFinished, EVT_ANIM_FIN = NewEvent()
+wxAnimationUpdateIndex, EVT_ANIM_UPDATE_IDX = NewEvent()
+
+class Animation:
+    """!Class represents animation as a sequence of states (views).
+    It enables to record, replay the sequence and finally generate
+    all image files. Recording and replaying is based on timer events.
+    There is no frame interpolation like in the Tcl/Tk based Nviz.
+    """
+    def __init__(self, mapWindow, timer):
+        """!Animation constructor
+        
+        @param mapWindow glWindow where rendering takes place
+        @param timer timer for recording and replaying
+        """
+        
+        self.animationList = []         # view states
+        self.timer = timer
+        self.mapWindow = mapWindow
+        self.actions = {'record': self.Record,
+                        'play': self.Play}
+        self.formats = ['ppm', 'tif']   # currently supported formats
+        self.mode = 'record'            # current mode (record, play, save)
+        self.paused = False             # recording/replaying paused
+        self.currentFrame = 0           # index of current frame
+        self.fps = 24 # user settings   # Frames per second
+        
+        self.stopSaving = False         # stop during saving images
+        self.animationSaved = False     # current animation saved or not
+        
+    def Start(self):
+        """!Start recording/playing"""
+        self.timer.Start(self.GetInterval())
+        
+    def Pause(self):
+        """!Pause recording/playing"""
+        self.timer.Stop()
+        
+    def Stop(self):
+        """!Stop recording/playing"""
+        self.timer.Stop()
+        self.PostFinishedEvent()
+        
+    def Update(self):
+        """!Record/play next view state (on timer event)"""
+        self.actions[self.mode]()
+    
+    def Record(self):
+        """!Record new view state"""
+        self.animationList.append({'view' : copy.deepcopy(self.mapWindow.view),
+                                   'iview': copy.deepcopy(self.mapWindow.iview)})
+        self.currentFrame += 1
+        self.PostUpdateIndexEvent(index = self.currentFrame)
+        self.animationSaved = False
+        
+    def Play(self):
+        """!Render next frame"""
+        if not self.animationList:
+            self.Stop()
+            return
+        try:
+            self.IterAnimation()
+        except IndexError:
+            # no more frames
+            self.Stop()
+            
+    def IterAnimation(self):
+        params = self.animationList[self.currentFrame]
+        self.UpdateView(params)
+        self.currentFrame += 1
+        
+        self.PostUpdateIndexEvent(index = self.currentFrame)
+        
+    def UpdateView(self, params):
+        """!Update view data in map window and render"""
+        toolWin = self.mapWindow.GetToolWin()
+        toolWin.UpdateState(view = params['view'], iview = params['iview'])
+        
+        self.mapWindow.UpdateView()
+        
+        self.mapWindow.render['quick'] = True
+        self.mapWindow.Refresh(False)
+        
+    def IsRunning(self):
+        """!Test if timer is running"""
+        return self.timer.IsRunning()
+        
+    def SetMode(self, mode):
+        """!Start animation mode
+        
+        @param mode animation mode (record, play, save)
+        """
+        self.mode = mode
+        
+    def GetMode(self):
+        """!Get animation mode (record, play, save)"""
+        return self.mode
+        
+    def IsPaused(self):
+        """!Test if animation is paused"""
+        return self.paused
+        
+    def SetPause(self, pause):
+        self.paused = pause
+        
+    def Exists(self):
+        """!Returns if an animation has been recorded"""
+        return bool(self.animationList)
+        
+    def GetFrameCount(self):
+        """!Return number of recorded frames"""
+        return len(self.animationList)
+        
+    def Clear(self):
+        """!Clear all records"""
+        self.animationList = []
+        self.currentFrame = 0
+        
+    def GoToFrame(self, index):
+        """!Render frame of given index"""
+        if index >= len(self.animationList):
+            return
+            
+        self.currentFrame = index
+        params = self.animationList[self.currentFrame]
+        self.UpdateView(params)
+        
+    def PostFinishedEvent(self):
+        """!Animation ends"""
+        toolWin = self.mapWindow.GetToolWin()
+        event = wxAnimationFinished(mode = self.mode)
+        wx.PostEvent(toolWin, event)
+        
+    def PostUpdateIndexEvent(self, index):
+        """!Frame index changed, update tool window"""
+        toolWin = self.mapWindow.GetToolWin()
+        event = wxAnimationUpdateIndex(index = index, mode = self.mode)
+        wx.PostEvent(toolWin, event)
+        
+    def StopSaving(self):
+        """!Abort image files generation"""
+        self.stopSaving = True
+        
+    def IsSaved(self):
+        """"!Test if animation has been saved (to images)"""
+        return self.animationSaved
+        
+    def SaveAnimationFile(self, path, prefix, format):
+        """!Generate image files
+        
+        @param path path to direcory
+        @param prefix file prefix
+        @param format index of image file format
+        """
+        w, h = self.mapWindow.GetClientSizeTuple()
+        toolWin = self.mapWindow.GetToolWin()
+        
+        self.currentFrame = 0
+        self.mode = 'save'
+        for params in self.animationList:
+            if not self.stopSaving:
+                self.UpdateView(params)
+                filename = prefix + "_" + str(self.currentFrame) + '.' + self.formats[format]
+                filepath = os.path.join(path, filename)
+                self.mapWindow.SaveToFile(FileName = filepath, FileType = self.formats[format],
+                                                  width = w, height = h)
+                self.currentFrame += 1
+                
+                wx.Yield()
+                toolWin.UpdateFrameIndex(index = self.currentFrame, goToFrame = False)
+            else:
+                self.stopSaving = False
+                break
+        self.animationSaved = True
+        self.PostFinishedEvent()
+
+    def SetFPS(self, fps):
+        """!Set Frames Per Second value
+        @param fps frames per second
+        """
+        self.fps = fps
+    
+    def GetInterval(self):
+        """!Return timer interval in ms"""
+        return 1000. / self.fps


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/nviz/main.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/nviz/main.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/nviz/main.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,40 @@
+"""!
+ at package nviz.main
+
+ at brief Nviz (3D view) module
+
+This module implements 3D visualization mode for map display.
+
+Map Display supports standard 2D view mode ('mapdisp' module) and
+2.5/3D mode ('nviz_mapdisp' module).
+
+(C) 2008, 2010-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
+ at author Anna Kratochvilova <KratochAnna seznam.cz> (Google SoC 2011)
+"""
+
+errorMsg = ''
+
+try:
+    from wx   import glcanvas
+    from nviz import mapwindow
+    from nviz import tools
+    from nviz import workspace
+    import wxnviz
+    haveNviz = True
+except (ImportError, NameError), err:
+    haveNviz = False
+    errorMsg = err
+
+if haveNviz:
+    GLWindow       = mapwindow.GLWindow
+    NvizToolWindow = tools.NvizToolWindow
+    NvizSettings   = workspace.NvizSettings
+else:
+    GLWindow       = None
+    NvizToolWindow = None
+    NvizSettings   = None


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/nviz/mapwindow.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/nviz/mapwindow.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/nviz/mapwindow.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,2481 @@
+"""!
+ at package nviz.mapwindow
+
+ at brief wxGUI 3D view mode (map canvas)
+
+This module implements 3D visualization mode for map display.
+
+List of classes:
+ - mapwindow::NvizThread
+ - mapwindow::GLWindow
+
+(C) 2008-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
+ at author Anna Kratochvilova <kratochanna gmail.com> (Google SoC 2011)
+"""
+
+import os
+import sys
+import time
+import copy
+import math
+import types
+
+from threading import Thread
+
+import wx
+from   wx.lib.newevent import NewEvent
+from   wx              import glcanvas
+
+import grass.script as grass
+
+from core.gcmd          import GMessage, GException, GError
+from core.debug         import Debug
+from gui_core.mapwindow import MapWindow
+from gui_core.goutput   import wxCmdOutput
+from nviz.workspace     import NvizSettings
+from core.settings      import UserSettings
+from nviz.animation     import Animation
+from nviz               import wxnviz
+
+wxUpdateProperties, EVT_UPDATE_PROP  = NewEvent()
+wxUpdateView,       EVT_UPDATE_VIEW  = NewEvent()
+wxUpdateLight,      EVT_UPDATE_LIGHT = NewEvent()
+wxUpdateCPlane,     EVT_UPDATE_CPLANE = NewEvent()
+
+class NvizThread(Thread):
+    def __init__(self, log, progressbar, window):
+        Thread.__init__(self)
+        Debug.msg(5, "NvizThread.__init__():")
+        self.log = log
+        self.progressbar = progressbar
+        self.window = window
+        
+        self._display = None
+        
+        self.setDaemon(True)
+
+    def run(self):
+        self._display = wxnviz.Nviz(self.log, self.progressbar)
+        
+    def GetDisplay(self):
+        """!Get display instance"""
+        return self._display
+
+class GLWindow(MapWindow, glcanvas.GLCanvas):
+    """!OpenGL canvas for Map Display Window"""
+    def __init__(self, parent, id = wx.ID_ANY,
+                 Map = None, tree = None, lmgr = None):
+        self.parent = parent # MapFrame
+        
+        glcanvas.GLCanvas.__init__(self, parent, id)
+        MapWindow.__init__(self, parent, id, 
+                           Map, tree, lmgr)
+        self.Hide()
+        
+        self.init = False
+        self.initView = False
+        
+        # render mode 
+        self.render = { 'quick' : False,
+                        # do not render vector lines in quick mode
+                        'vlines' : False,
+                        'vpoints' : False,
+                        'overlays': False }
+        self.mouse = {
+            'use': 'pointer'
+            }
+        self.cursors = {
+            'default' : wx.StockCursor(wx.CURSOR_ARROW),
+            'cross'   : wx.StockCursor(wx.CURSOR_CROSS),
+            }
+        # list of loaded map layers (layer tree items)
+        self.layers  = list()
+        # list of constant surfaces
+        self.constants = list()
+        # id of base surface (when vector is loaded and no surface exist)
+        self.baseId = -1
+        # list of cutting planes
+        self.cplanes = list()
+        # list of query points
+        self.qpoints = list()
+        # list of past views
+        self.viewhistory  = []
+        self.saveHistory = False
+        # offset for dialog (e.g. DisplayAttributesDialog)
+        self.dialogOffset = 5
+        # overlays
+        self.overlays = {}
+        self.imagelist = []
+        self.overlay = wx.Overlay()
+        #self.pdc = wx.PseudoDC()
+        self.textdict = {}
+        self.dragid = -1
+        self.hitradius = 5
+        # layer manager toolwindow
+        self.toolWin = None
+        
+        if self.lmgr:
+            self.log = self.lmgr.goutput
+            logerr = self.lmgr.goutput.GetLog(err = True)
+            logmsg = self.lmgr.goutput.GetLog()
+        else:
+            self.log = logmsg = sys.stdout
+            logerr = sys.stderr
+        
+        # create nviz instance - use display region instead of computational
+        os.environ['GRASS_REGION'] = self.Map.SetRegion(windres = True)
+        
+        self.nvizThread = NvizThread(logerr,
+                                     self.parent.GetProgressBar(),
+                                     logmsg)
+        self.nvizThread.start()
+        time.sleep(.1)
+        self._display = self.nvizThread.GetDisplay()
+        
+        # GRASS_REGION needed only for initialization
+        del os.environ['GRASS_REGION']
+        
+        self.img = wx.Image(self.Map.mapfile, wx.BITMAP_TYPE_ANY)
+        
+        # size of MapWindow, to avoid resizing if size is the same
+        self.size = (0, 0)
+        
+        # default values
+        self.nvizDefault = NvizSettings()
+        self.view = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'view')) # copy
+        self.iview = UserSettings.Get(group = 'nviz', key = 'view', internal = True)
+        self.light = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'light')) # copy
+        self.decoration = self.nvizDefault.SetDecorDefaultProp(type = 'arrow')
+        self.decoration['scalebar'] = []
+        self.decoration['arrow']['size'] = self._getDecorationSize()
+        self.fly = self.InitFly()
+        
+        # timer for flythrough
+        self.timerFly = wx.Timer(self, id = wx.NewId())
+        # timer for animations
+        self.timerAnim = wx.Timer(self, id = wx.NewId())
+        self.animation = Animation(mapWindow = self, timer = self.timerAnim)        
+        
+        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
+        self.Bind(wx.EVT_SIZE,             self.OnSize)
+        self.Bind(wx.EVT_PAINT,            self.OnPaint)
+        self._bindMouseEvents()
+        
+        self.Bind(EVT_UPDATE_PROP,   self.UpdateMapObjProperties)
+        self.Bind(EVT_UPDATE_VIEW,   self.OnUpdateView)
+        self.Bind(EVT_UPDATE_LIGHT,  self.UpdateLight)
+        self.Bind(EVT_UPDATE_CPLANE, self.UpdateCPlane)
+        
+        self.Bind(wx.EVT_TIMER, self.OnTimerAnim, self.timerAnim)
+        self.Bind(wx.EVT_TIMER, self.OnTimerFly, self.timerFly)
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
+        self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
+        
+        self.Bind(wx.EVT_CLOSE, self.OnClose)
+        
+        # cplanes cannot be initialized now
+        wx.CallAfter(self.InitCPlanes)
+
+    def InitFly(self):
+        """!Initialize fly through dictionary"""
+        fly = {'interval' : 10,             # interval for timerFly
+               'value': [0, 0, 0],          # calculated values for navigation
+               'mode' : 0,                  # fly through mode (0, 1)
+               'exag' : {                   # sensitivity
+                    'move' : UserSettings.Get(group = 'nviz', key = 'fly', subkey = ['exag', 'move']),
+                    'turn' : UserSettings.Get(group = 'nviz', key = 'fly', subkey = ['exag', 'turn'])},
+               'exagMultiplier' : 3,        # speed up by Shift
+               'flySpeed' : 4,              # speed of flying
+               'mouseControl' : None,       # if mouse or keys are used
+               'pos' : {'x' : 0, 'y' : 0},  # virtual mouse position when using arrows
+               'arrowStep' : 50,            # step in pixels (when using arrows)
+               'flySpeedStep' : 2,
+            }
+            
+        return fly
+        
+    def OnTimerFly(self, event):
+        """!Fly event was emitted, move the scene"""
+        if self.mouse['use'] != 'fly':
+            return
+        
+        if self.fly['mouseControl']:
+            mx, my = self.ComputeMxMy(*self.mouse['tmp'])
+        else:
+            mx, my = self.ComputeMxMy(self.fly['pos']['x'], self.fly['pos']['y'])
+            
+        self.ComputeFlyValues(mx = mx, my = my)
+        self._display.FlyThrough(flyInfo = self.fly['value'], mode = self.fly['mode'],
+                                 exagInfo = self.fly['exag'])
+        self.ChangeInnerView()                                 
+        self.render['quick'] = True
+        self.Refresh(False)
+        
+    def ComputeMxMy(self, x, y):
+        """!Compute values for flythrough navigation 
+        (ComputeFlyValues should follow). 
+        
+        Based on visualization/nviz/src/togl_flythrough.c.
+        @param x,y screen coordinates
+        """
+        sx, sy = self.GetClientSizeTuple()
+        dx = dy = 0.01
+        
+        mx = 2 * (float(x) / sx) - 1
+        my = 2 * (float(y) / sy) - 1
+    
+        if mx < - dx:
+            mx += dx
+        elif mx > dx:
+            mx -= dx
+        else:
+            mx = 0.0 # ?
+        if my < - dy:
+            my += dy
+        elif my > dy:
+            my -= dy
+        else:
+            my = 0.0
+    
+        mx = mx / (1.0 - dx)
+        my = my / (1.0 - dy)
+    
+        # Quadratic seems smoother 
+        mx *= abs(mx)
+        my *= abs(my)
+        
+        return mx, my
+        
+    def ComputeFlyValues(self, mx, my):
+        """!Compute parameters for fly-through navigation
+        
+        @params mx,my results from ComputeMxMy method
+        """
+        self.fly['value'] = [0, 0, 0]
+        
+        if self.fly['mode'] == 0:
+            self.fly['value'][0] = self.fly['flySpeed'] * self.fly['interval'] / 1000. # forward */
+            self.fly['value'][1] = mx * 0.1 * self.fly['interval'] / 1000. # heading 
+            self.fly['value'][2] = my * 0.1 * self.fly['interval'] / 1000. # pitch 
+        else:
+            self.fly['value'][0] = mx * 100.0 * self.fly['interval'] /1000.
+            self.fly['value'][2] = - my * 100.0 * self.fly['interval'] /1000.
+    
+    def ChangeFlySpeed(self, increase):
+        """!Increase/decrease flight spped"""
+        if increase:
+            self.fly['flySpeed'] += self.fly['flySpeedStep']
+        else:
+            self.fly['flySpeed'] -= self.fly['flySpeedStep']
+        
+    def __del__(self):
+        """!Stop timers if running, unload data"""
+        self.StopTimer(self.timerAnim)
+        self.StopTimer(self.timerFly)
+        self.UnloadDataLayers(force = True)
+    
+    def StopTimer(self, timer):
+        """!Stop timer if running"""
+        if timer.IsRunning():
+            timer.Stop()
+            
+    def _bindMouseEvents(self):
+        self.Bind(wx.EVT_MOUSE_EVENTS,     self.OnMouseAction)
+        self.Bind(wx.EVT_MOTION,           self.OnMotion)
+        
+    def InitCPlanes(self):
+        """!Initialize cutting planes list"""
+        for i in range(self._display.GetCPlanesCount()):
+            cplane = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'cplane'))
+            cplane['on'] = False
+            self.cplanes.append(cplane)
+        
+    def SetToolWin(self, toolWin):
+        """!Sets reference to nviz toolwindow in layer manager"""
+        self.toolWin = toolWin
+        
+    def GetToolWin(self):
+        """!Returns reference to nviz toolwindow in layer manager"""
+        return self.toolWin
+            
+    def OnClose(self, event):
+        self.StopTimer(self.timerAnim)
+        self.StopTimer(self.timerFly)
+        # cleanup when window actually closes (on quit) and not just is hidden
+        self.UnloadDataLayers(force = True)
+        
+    def OnEraseBackground(self, event):
+        pass # do nothing, to avoid flashing on MSW
+    
+    def OnSize(self, event):
+        size = self.GetClientSize()
+        if self.size != size \
+            and self.GetContext():
+            Debug.msg(3, "GLCanvas.OnSize(): w = %d, h = %d" % \
+                      (size.width, size.height))
+            self.SetCurrent()
+            self._display.ResizeWindow(size.width,
+                                       size.height)
+        
+            # reposition checkbox in statusbar
+            self.parent.StatusbarReposition()
+            
+            # update statusbar
+            self.parent.StatusbarUpdate()
+            
+        self.size = size
+        
+        event.Skip()
+       
+    def OnPaint(self, event):
+        Debug.msg(1, "GLCanvas.OnPaint()")
+        
+        self.render['overlays'] = True
+        dc = wx.PaintDC(self)
+        self.DoPaint()
+        
+
+    def DoPaint(self):
+        self.SetCurrent()
+        
+        if not self.initView:
+            self._display.InitView()
+            self.initView = True
+        
+        self.LoadDataLayers()
+        self.UnloadDataLayers()
+        
+        if not self.init:
+            self.ResetView()
+            
+            if hasattr(self.lmgr, "nviz"):
+                self.lmgr.nviz.UpdatePage('view')
+                self.lmgr.nviz.UpdatePage('light')
+                self.lmgr.nviz.UpdatePage('cplane')
+                self.lmgr.nviz.UpdatePage('decoration')
+                self.lmgr.nviz.UpdatePage('animation')
+                layer = self.GetSelectedLayer()
+                if layer:
+                    if layer.type ==  'raster':
+                        self.lmgr.nviz.UpdatePage('surface')
+                        self.lmgr.nviz.UpdatePage('fringe')
+                    elif layer.type ==  'vector':
+                        self.lmgr.nviz.UpdatePage('vector')
+                
+                self.lmgr.nviz.UpdateSettings()
+                
+                # update widgets
+                win = self.lmgr.nviz.FindWindowById( \
+                    self.lmgr.nviz.win['vector']['lines']['surface'])
+                win.SetItems(self.GetLayerNames('raster'))
+            
+            self.init = True
+        
+        self.UpdateMap()
+        
+    def DrawImages(self):
+        """!Draw overlay image"""
+        for texture in self.imagelist:
+            if texture.IsActive():
+                texture.Draw()
+            
+    def GetLegendRect(self):
+        """!Estimates legend size for dragging"""
+        size = None
+        if 1 in self.overlays:
+            for param in self.overlays[1]['cmd'][1:]:
+                if param.startswith("at="):
+                    size = map(int, param.split("=")[-1].split(','))
+                    break
+        if size:
+            wSize = self.GetClientSizeTuple()
+            x, y = size[2]/100. * wSize[0], wSize[1] - (size[1]/100. * wSize[1])
+            x += self.overlays[1]['coords'][0]
+            y += self.overlays[1]['coords'][1]
+            w = (size[3] - size[2])/100. * wSize[0]
+            h = (size[1] - size[0])/100. * wSize[1]
+            
+            rect = wx.Rect(x, y, w, h)
+            return rect
+        
+        return wx.Rect()        
+        
+    def DrawTextImage(self, textDict, relCoords):
+        """!Draw overlay text"""
+        bmp = wx.EmptyBitmap(textDict['bbox'][2], textDict['bbox'][3])
+        memDC = wx.MemoryDC()
+        memDC.SelectObject(bmp)
+        
+        mask = self.view['background']['color']
+        if mask == textDict['color']:
+            mask = wx.WHITE
+        memDC.SetBackground(wx.Brush(mask))
+        memDC.Clear()
+        memDC.SetFont(textDict['font'])
+        memDC.SetTextForeground(textDict['color'])
+        if textDict['rotation'] == 0:
+            memDC.DrawText(textDict['text'], 0, 0)
+        else:
+            memDC.DrawRotatedText(textDict['text'], relCoords[0], relCoords[1],
+                                  textDict['rotation'])
+        bmp.SetMaskColour(mask)
+        memDC.DrawBitmap(bmp, 0, 0, 1)
+        
+        filename = tempfile.mktemp() + '.png'
+        bmp.SaveFile(filename, wx.BITMAP_TYPE_PNG)
+        memDC.SelectObject(wx.NullBitmap)
+        
+        return filename
+        
+    def UpdateOverlays(self):
+        """!Converts rendered overlay files and text labels to wx.Image
+            and then to textures so that they can be rendered by OpenGL.
+            Updates self.imagelist"""
+        self.Map.ChangeMapSize(self.GetClientSize())
+        self.Map.RenderOverlays(force = True)
+        
+        # delete textures
+        for texture in self.imagelist:
+            # inactive overlays, remove text labels
+            if texture.GetId() < 100:
+                if not self.overlays[texture.GetId()]['layer'].IsActive():
+                    texture.SetActive(False)
+                else:
+                    texture.SetActive(True)
+            else: # text label
+                if texture.GetId() not in self.textdict:
+                    self.imagelist.remove(texture)
+                    
+        # update images (only legend so far)
+        for oid, overlay in self.overlays.iteritems():
+            layer = overlay['layer']
+            if not layer.IsActive() or oid == 0: # 0 for barscale
+                continue
+            if oid not in [t.GetId() for t in self.imagelist]: # new
+                self.CreateTexture(overlay = layer)
+            else:
+                for t in self.imagelist:
+                    if t.GetId() == oid: # check if it is the same
+                        if not t.Corresponds(layer):
+                            self.imagelist.remove(t)
+                            t = self.CreateTexture(overlay = layer)
+                        # always set coordinates, needed for synchr. 2D and 3D modes
+                        t.SetCoords(overlay['coords'])
+
+                    
+        # update text labels
+        for textId in self.textdict.keys():
+            if textId not in [t.GetId() for t in self.imagelist]:# new
+                self.CreateTexture(textId = textId)
+            else:
+                for t in self.imagelist:
+                    if t.GetId() == textId: # check if it is the same
+                        self.textdict[textId]['bbox'] = t.textDict['bbox']
+                        if not t.Corresponds(self.textdict[textId]):
+                            self.imagelist.remove(t)
+                            t = self.CreateTexture(textId = textId)
+                        # always set coordinates, needed for synchr. 2D and 3D modes
+                        t.SetCoords(self.textdict[textId]['coords'])
+            
+    def CreateTexture(self, overlay = None, textId = None):
+        """!Create texture from overlay image or from textdict"""
+        if overlay: # legend  
+            texture = wxnviz.ImageTexture(filepath = overlay.mapfile, overlayId = overlay.id,
+                                          coords = list(self.overlays[overlay.id]['coords']),
+                                          cmd = overlay.GetCmd())
+            if overlay.id == 1: # legend
+                texture.SetBounds(self.GetLegendRect())
+        else: # text
+            coords, bbox, relCoords = self.TextBounds(self.textdict[textId])
+            self.textdict[textId]['coords'] = coords
+            self.textdict[textId]['bbox'] = bbox
+            file = self.DrawTextImage(self.textdict[textId], relCoords)
+            texture = wxnviz.TextTexture(filepath = file, overlayId = textId,
+                                         coords = coords, textDict = self.textdict[textId])
+            bbox.OffsetXY(*relCoords)
+            texture.SetBounds(bbox)
+            
+        if not texture.textureId: # texture too big
+            GMessage(parent = self, message = 
+                     _("Image is too large, your OpenGL implementation "
+                       "supports maximum texture size %d px.") % texture.maxSize)
+            return texture
+            
+        self.imagelist.append(texture)
+        
+        return texture
+        
+    def FindObjects(self, mouseX, mouseY, radius):
+        """Find object which was clicked on"""
+        for texture in self.imagelist:
+            if texture.HitTest(mouseX, mouseY, radius):
+                return texture.id
+        return -1
+        
+    def OnTimerAnim(self, event):
+         self.animation.Update()
+         
+    def GetAnimation(self):
+         return self.animation
+         
+    def OnKeyDown(self, event):
+        """!Key was pressed.
+        
+        Used for fly-through mode.
+        """
+        if not self.mouse['use'] == 'fly':
+            return
+            
+        key = event.GetKeyCode()
+        if key == wx.WXK_CONTROL: # Mac ?
+            self.fly['mode'] = 1
+            
+        elif key == wx.WXK_SHIFT: 
+            self.fly['exag']['move'] *= self.fly['exagMultiplier']
+            self.fly['exag']['turn'] *= self.fly['exagMultiplier']
+            
+        elif key == wx.WXK_ESCAPE and self.timerFly.IsRunning() and not self.fly['mouseControl']:
+            self.StopTimer(self.timerFly)
+            self.fly['mouseControl'] = None
+            self.render['quick'] = False
+            self.Refresh(False)
+            
+        elif key in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT, wx.WXK_RIGHT):
+            if not self.fly['mouseControl']:
+                if not self.timerFly.IsRunning():
+                    sx, sy = self.GetClientSizeTuple()
+                    self.fly['pos']['x'] = sx / 2
+                    self.fly['pos']['y'] = sy / 2
+                    self.fly['mouseControl'] = False # controlled by keyboard
+                    self.timerFly.Start(self.fly['interval'])
+    
+                self.ProcessFlyByArrows(keyCode = key)
+                
+            # change speed of flight when using mouse
+            else:
+                if key == wx.WXK_UP:
+                    self.ChangeFlySpeed(increase = True)
+                elif key == wx.WXK_DOWN:
+                    self.ChangeFlySpeed(increase = False)
+        
+        elif key in (wx.WXK_HOME, wx.WXK_PAGEUP) and self.timerFly.IsRunning():
+            self.ChangeFlySpeed(increase = True)
+        elif key in (wx.WXK_END, wx.WXK_PAGEDOWN) and self.timerFly.IsRunning():
+            self.ChangeFlySpeed(increase = False)
+            
+        event.Skip()
+        
+    def ProcessFlyByArrows(self, keyCode):
+        """!Process arrow key during fly-through"""
+        step = self.fly['arrowStep']
+        if keyCode == wx.WXK_UP:
+            self.fly['pos']['y'] -= step
+        elif keyCode == wx.WXK_DOWN:
+            self.fly['pos']['y'] += step
+        elif keyCode == wx.WXK_LEFT:
+            self.fly['pos']['x'] -= step
+        elif keyCode == wx.WXK_RIGHT:
+            self.fly['pos']['x'] += step
+            
+    def OnKeyUp(self, event):
+        """!Key was released.
+        
+        Used for fly-through mode.
+        """
+        if not self.mouse['use'] == 'fly':
+            return
+            
+        key = event.GetKeyCode()
+        if key == wx.WXK_CONTROL: # Mac ?
+            self.fly['mode'] = 0
+        elif key == wx.WXK_SHIFT: 
+            self.fly['exag']['move'] = math.floor(self.fly['exag']['move'] / self.fly['exagMultiplier'])
+            self.fly['exag']['turn'] = math.floor(self.fly['exag']['turn'] / self.fly['exagMultiplier'])
+        
+        event.Skip()
+        
+    def OnMouseAction(self, event):
+        """!Handle mouse events"""
+        # 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.OnDClick(event)
+        
+        event.Skip()
+
+    def OnMouseWheel(self, event):
+        """!Change perspective"""
+        if not UserSettings.Get(group = 'display',
+                                key = 'mouseWheelZoom',
+                                subkey = 'enabled'):
+            event.Skip()
+            return
+            
+        wheel = event.GetWheelRotation()
+        Debug.msg (5, "GLWindow.OnMouseMotion(): wheel = %d" % wheel)
+        if self.timerFly.IsRunning() and self.fly['mouseControl']:
+            if wheel > 0:
+                self.ChangeFlySpeed(increase = True)
+            else:
+                self.ChangeFlySpeed(increase = False)
+        else:
+            if UserSettings.Get(group = 'display',
+                                key = 'mouseWheelZoom',
+                                subkey = 'selection'):
+                wheel *= -1
+            self.DoZoom(zoomtype = wheel, pos = event.GetPositionTuple())
+            
+        # update statusbar
+        ### self.parent.StatusbarUpdate()
+            
+    def OnLeftDown(self, event):
+        """!On left mouse down"""
+        self.mouse['begin'] = event.GetPositionTuple()
+        self.mouse['tmp'] = event.GetPositionTuple()
+        if self.mouse['use'] == "lookHere":
+            size = self.GetClientSize()
+            self._display.LookHere(self.mouse['begin'][0], size[1] - self.mouse['begin'][1])
+            focus = self._display.GetFocus()
+            for i, coord in enumerate(('x', 'y', 'z')):
+                self.iview['focus'][coord] = focus[i]
+            self.saveHistory = True
+            self.Refresh(False)
+            toggle = self.lmgr.nviz.FindWindowByName('here')
+            toggle.SetValue(False)
+            self.mouse['use'] = 'pointer'
+            self.SetCursor(self.cursors['default'])
+                
+        if self.mouse['use'] == 'arrow':
+            pos = event.GetPosition()
+            size = self.GetClientSize()
+            self.SetDrawArrow((pos[0], size[1] - pos[1]))
+                
+        if self.mouse['use'] == 'scalebar':
+            pos = event.GetPosition()
+            size = self.GetClientSize()
+            self.SetDrawScalebar((pos[0], size[1] - pos[1]))
+        
+        if self.mouse['use'] == 'pointer':
+            # get decoration or text id
+            self.dragid = self.FindObjects(self.mouse['tmp'][0], self.mouse['tmp'][1],
+                                          self.hitradius)
+                
+        if self.mouse['use'] == 'fly':
+            if not self.timerFly.IsRunning():
+                self.timerFly.Start(self.fly['interval'])
+                self.fly['mouseControl'] = True
+            
+        event.Skip()    
+        
+    def OnDragging(self, event):
+        if self.mouse['use'] == 'pointer':
+            if self.dragid > 0:
+                
+                self.DragItem(self.dragid, event)
+            
+        if self.mouse['use'] == 'rotate':    
+            dx, dy = event.GetX() - self.mouse['tmp'][0], event.GetY() - self.mouse['tmp'][1]
+            
+            angle, x, y, z = self._display.GetRotationParameters(dx, dy)
+            self._display.Rotate(angle, x, y, z)
+            
+            self.render['quick'] = True
+            self.Refresh(False)
+            
+        if self.mouse['use'] == 'pan':
+            self.FocusPanning(event)
+            
+        self.mouse['tmp'] = event.GetPositionTuple()
+                
+        event.Skip()
+            
+    def Pixel2Cell(self, (x, y)):
+        """!Convert image coordinates to real word coordinates
+
+        @param x, y image coordinates
+        
+        @return easting, northing
+        @return None on error
+        """
+        size = self.GetClientSize()
+        # UL -> LL
+        sid, x, y, z = self._display.GetPointOnSurface(x, size[1] - y)
+        
+        if not sid:
+            return None
+        
+        return (x, y)
+    
+    def DoZoom(self, zoomtype, pos):
+        """!Change perspective and focus"""
+        
+        prev_value = self.view['persp']['value']
+        if zoomtype > 0:
+            value = -1 * self.view['persp']['step']
+        else:
+            value = self.view['persp']['step']
+        self.view['persp']['value'] +=  value
+        if self.view['persp']['value'] < 1:
+            self.view['persp']['value'] = 1
+        elif self.view['persp']['value'] > 180:
+            self.view['persp']['value'] = 180
+        
+        if prev_value !=  self.view['persp']['value']:
+            if hasattr(self.lmgr, "nviz"):
+                self.lmgr.nviz.UpdateSettings()
+                x, y = pos[0], self.GetClientSize()[1] - pos[1]
+                result = self._display.GetPointOnSurface(x, y)
+                if result[0]:
+                    self._display.LookHere(x, y)
+                    focus = self._display.GetFocus()
+                    for i, coord in enumerate(('x', 'y', 'z')):
+                        self.iview['focus'][coord] = focus[i]
+                self._display.SetView(self.view['position']['x'], self.view['position']['y'],
+                                      self.iview['height']['value'],
+                                      self.view['persp']['value'],
+                                      self.view['twist']['value'])
+                self.saveHistory = True
+            # redraw map
+            self.DoPaint()
+            
+    def OnLeftUp(self, event):
+        self.mouse['end'] = event.GetPositionTuple()
+        if self.mouse["use"] == "query":
+            # querying
+            layers = self.GetSelectedLayer(multi = True)
+            isRaster = False
+            nVectors = 0
+            for l in layers:
+                if l.GetType() == 'raster':
+                    isRaster = True
+                    break
+                if l.GetType() == 'vector':
+                    nVectors += 1
+            
+            if isRaster or nVectors > 1:
+                self.OnQueryMap(event)
+            else:
+                self.OnQueryVector(event)
+                    
+        elif self.mouse["use"] in ('arrow', 'scalebar'):
+            self.lmgr.nviz.FindWindowById(
+                    self.lmgr.nviz.win['decoration'][self.mouse["use"]]['place']).SetValue(False)
+            self.mouse['use'] = 'pointer'
+            self.SetCursor(self.cursors['default'])
+        elif self.mouse['use'] == 'pointer':
+            if self.dragid > 0:
+                dx = self.mouse['end'][0] - self.mouse['begin'][0]
+                dy = self.mouse['end'][1] - self.mouse['begin'][1]
+                if self.dragid < 99:
+                    coords = self.overlays[self.dragid]['coords']
+                    self.overlays[self.dragid]['coords'] = [coords[0] + dx, coords[1] + dy]
+                else: # text
+                    coords = self.textdict[self.dragid]['coords']
+                    self.textdict[self.dragid]['coords'] = [coords[0] + dx, coords[1] + dy]
+                self.dragid = -1
+                self.render['quick'] = False
+                self.Refresh(False)
+            
+        elif self.mouse['use'] == 'rotate':
+            self._display.UnsetRotation()
+            self.iview['rotation'] = self._display.GetRotationMatrix()
+            self.saveHistory = True
+            self.render['quick'] = False
+            self.Refresh(False)
+            
+        elif self.mouse['use'] == 'pan':
+            self.saveHistory = True
+            self.render['quick'] = False
+            self.Refresh(False)
+            
+        elif self.mouse['use'] == 'fly':
+            if self.fly['mouseControl']:
+                self.StopTimer(self.timerFly)
+                self.fly['mouseControl'] = None
+                #for key in self.iview['dir'].keys():
+                    #self.iview[''][key] = -1
+                # this causes sudden change, but it should be there
+                #if hasattr(self.lmgr, "nviz"):
+                    #self.lmgr.nviz.UpdateSettings()
+                    
+                self.render['quick'] = False
+                self.Refresh(False)
+            
+        elif self.mouse['use'] == 'zoom':
+            self.DoZoom(zoomtype = self.zoomtype, pos = self.mouse['end'])
+        event.Skip()
+            
+    def OnDClick(self, event):
+        """!On mouse double click"""
+        if self.mouse['use'] != 'pointer': return
+        pos = event.GetPositionTuple()
+        self.dragid = self.FindObjects(pos[0], pos[1], self.hitradius)
+        
+        if self.dragid == 1:
+            self.parent.OnAddLegend(None)
+        elif self.dragid > 100:
+            self.parent.OnAddText(None)
+        else:
+            return
+    
+    def FocusPanning(self, event):
+        """!Simulation of panning using focus"""
+        size = self.GetClientSizeTuple()
+        id1, x1, y1, z1 = self._display.GetPointOnSurface(
+                      self.mouse['tmp'][0], size[1] - self.mouse['tmp'][1])
+        id2, x2, y2, z2 = self._display.GetPointOnSurface(
+                      event.GetX(), size[1] - event.GetY())
+        if id1 and id1 == id2:
+            dx, dy, dz = x2 - x1, y2 - y1, z2 - z1
+            focus = self.iview['focus']
+            focus['x'], focus['y'], focus['z'] = self._display.GetFocus()
+            focus['x'] -= dx
+            focus['y'] -= dy
+            focus['z'] -= dz
+            
+            #update properties
+            self.PostViewEvent()
+            
+            self.mouse['tmp'] = event.GetPositionTuple()
+            self.render['quick'] = True
+            self.Refresh(False)
+            
+    def HorizontalPanning(self, event):
+        """!Move all layers in horizontal (x, y) direction.
+        Currently not used.
+        """
+        size = self.GetClientSizeTuple()
+        id1, x1, y1, z1 = self._display.GetPointOnSurface(
+                      self.mouse['tmp'][0], size[1] - self.mouse['tmp'][1])
+        id2, x2, y2, z2 = self._display.GetPointOnSurface(
+                      event.GetX(), size[1] - event.GetY())
+        
+        if id1 and id1 == id2:
+            dx, dy = x2 - x1, y2 - y1
+            # find raster and volume
+            for item in self.layers:
+                mapLayer = self.tree.GetPyData(item)[0]['maplayer']
+                  
+                data = self.tree.GetPyData(item)[0]['nviz']
+                if mapLayer.GetType() == 'raster':
+                    data['surface']['position']['x'] += dx
+                    data['surface']['position']['y'] += dy
+                    data['surface']['position']['update'] = None
+                    
+                    #update properties
+                    evt = wxUpdateProperties(data = data)
+                    wx.PostEvent(self, evt)
+                    
+                    if event.CmdDown() and id1 == data['surface']['object']['id']:
+                        break
+                    
+                elif mapLayer.GetType() == '3d-raster':
+                    if 'x' not in data['volume']['position']:
+                        data['volume']['position']['x'] = 0
+                        data['volume']['position']['y'] = 0
+                        data['volume']['position']['z'] = 0
+                    data['volume']['position']['x'] += dx
+                    data['volume']['position']['y'] += dy
+                    data['volume']['position']['update'] = None
+                    
+                    #update properties
+                    evt = wxUpdateProperties(data = data)
+                    wx.PostEvent(self, evt)
+                
+            self.mouse['tmp'] = event.GetPositionTuple()
+            self.render['quick'] = True
+            self.Refresh(False)
+            
+    def DragItem(self, id, event):
+        """!Drag an overlay decoration item
+        """
+        if not id: return
+        Debug.msg (5, "GLWindow.DragItem(): id=%d" % id)
+        x, y = self.mouse['tmp']
+        dx = event.GetX() - x
+        dy = event.GetY() - y
+        for texture in self.imagelist:
+            if texture.id == id:
+                texture.MoveTexture(dx, dy)
+
+
+        self.render['quick'] = True
+        self.Refresh(False)
+        
+        self.mouse['tmp'] = (event.GetX(), event.GetY()) 
+        
+    def ZoomBack(self):
+        """!Set previous view in history list
+        """
+        view = {}
+        if len(self.viewhistory) > 1:
+            self.viewhistory.pop()
+            view = copy.deepcopy(self.viewhistory[-1])
+        
+        # disable tool if stack is empty
+        if len(self.viewhistory) < 2: # disable tool
+            toolbar = self.parent.GetMapToolbar()
+            toolbar.Enable('zoomback', enable = False)
+            
+        # set view and update nviz view page
+        self.lmgr.nviz.UpdateState(view = view[0], iview = view[1])
+        self.lmgr.nviz.UpdatePage('view')
+        # update map
+        self.Refresh(False)
+
+    def ViewHistory(self, view, iview):
+        """!Manages a list of last 10 views
+        
+        @param view view dictionary
+        @param iview view dictionary (internal)
+        
+        @return removed history item if exists (or None)
+        """
+        removed = None
+        hview = copy.deepcopy(view)
+        hiview = copy.deepcopy(iview)
+        
+        if not (self.viewhistory and self.viewhistory[-1] == (hview, hiview)):  
+            self.viewhistory.append((hview, hiview))
+            
+        if len(self.viewhistory) > 10:
+            removed = self.viewhistory.pop(0)
+        
+        if removed:
+            Debug.msg(4, "GLWindow.ViewHistory(): hist=%s, removed=%s" %
+                      (self.viewhistory, removed))
+        else:
+            Debug.msg(4, "GLWindow.ViewHistory(): hist=%s" %
+                      (self.viewhistory))
+        
+        # update toolbar
+        if len(self.viewhistory) > 1:
+            enable = True
+        else:
+            enable = False
+        
+        toolbar = self.parent.GetMapToolbar()
+        toolbar.Enable('zoomback', enable)
+        
+        return removed     
+    
+    def ResetViewHistory(self):
+        """!Reset view history"""
+        self.viewhistory = list()
+    
+    def GoTo(self, e, n):
+        """!Focus on given point"""
+        w = self.Map.region['w']
+        s = self.Map.region['s']
+        e -= w
+        n -= s
+        focus = self.iview['focus']
+        focus['x'], focus['y'] = e, n
+        self.saveHistory = True
+        #update properties
+        self.PostViewEvent()
+        
+        self.render['quick'] = False
+        self.Refresh(False)
+    
+    def OnQueryMap(self, event):
+        """!Query raster and vector maps"""
+        self.OnQuerySurface(event)
+        self.parent.QueryMap(event.GetX(), event.GetY())
+        
+    def OnQuerySurface(self, event):
+        """!Query surface on given position"""
+        size = self.GetClientSizeTuple()
+        result = self._display.QueryMap(event.GetX(), size[1] - event.GetY())
+        if result:
+            self.qpoints.append((result['x'], result['y'], result['z']))
+            self.log.WriteLog("%-30s: %.3f" % (_("Easting"),   result['x']))
+            self.log.WriteLog("%-30s: %.3f" % (_("Northing"),  result['y']))
+            self.log.WriteLog("%-30s: %.3f" % (_("Elevation"), result['z']))
+            name = ''
+            for item in self.layers:
+                self.tree.GetPyData(item)[0]['nviz']
+                if self.tree.GetPyData(item)[0]['maplayer'].type == 'raster' and\
+                    self.tree.GetPyData(item)[0]['nviz']['surface']['object']['id'] == result['id']:
+                    name = self.tree.GetPyData(item)[0]['maplayer'].name
+            self.log.WriteLog("%-30s: %s" % (_("Surface map name"), name))
+            self.log.WriteLog("%-30s: %s" % (_("Surface map elevation"), result['elevation']))
+            self.log.WriteLog("%-30s: %s" % (_("Surface map color"), result['color']))
+            if len(self.qpoints) > 1:
+                prev = self.qpoints[-2]
+                curr = self.qpoints[-1]
+                dxy = math.sqrt(pow(prev[0]-curr[0], 2) +
+                                pow(prev[1]-curr[1], 2))
+                dxyz = math.sqrt(pow(prev[0]-curr[0], 2) +
+                                 pow(prev[1]-curr[1], 2) +
+                                 pow(prev[2]-curr[2], 2))
+                self.log.WriteLog("%-30s: %.3f" % (_("XY distance from previous"), dxy))
+                self.log.WriteLog("%-30s: %.3f" % (_("XYZ distance from previous"), dxyz))
+                self.log.WriteLog("%-30s: %.3f" % (_("Distance along surface"),
+                                              self._display.GetDistanceAlongSurface(result['id'],
+                                                                                    (curr[0], curr[1]),
+                                                                                    (prev[0], prev[1]),
+                                                                                    useExag = False)))
+                self.log.WriteLog("%-30s: %.3f" % (_("Distance along exag. surface"),
+                                              self._display.GetDistanceAlongSurface(result['id'],
+                                                                                    (curr[0], curr[1]),
+                                                                                    (prev[0], prev[1]),
+                                                                                      useExag = True)))
+            self.log.WriteCmdLog('-' * 80)
+        else:
+            self.log.WriteLog(_("No point on surface"))
+            self.log.WriteCmdLog('-' * 80)
+    
+    def PostViewEvent(self, zExag = False):
+        """!Change view settings"""
+        event = wxUpdateView(zExag = zExag)
+        wx.PostEvent(self, event)
+        
+    def OnQueryVector(self, event):
+        """!Query vector on given position"""
+        self.parent.QueryVector(*event.GetPosition())
+
+    def ChangeInnerView(self):
+        """!Get current viewdir and viewpoint and set view"""
+        view = self.view
+        iview = self.iview
+        (view['position']['x'], view['position']['y'],
+        iview['height']['value']) = self._display.GetViewpointPosition()
+        for key, val in zip(('x', 'y', 'z'), self._display.GetViewdir()):
+            iview['dir'][key] = val
+        
+        iview['dir']['use'] = True
+        
+    def OnUpdateView(self, event):
+        """!Change view settings"""
+        if event:
+                self.UpdateView(zexag = event.zExag)
+                
+        self.saveHistory = True
+        if event:
+            event.Skip()
+            
+            
+    def UpdateView(self, zexag = False):
+        """!Change view settings"""
+        view = self.view
+        iview = self.iview
+        if zexag and 'value' in view['z-exag']:
+            self._display.SetZExag(self.iview['z-exag']['original'] * view['z-exag']['value'])
+        
+        
+        self._display.SetView(view['position']['x'], view['position']['y'],
+                              iview['height']['value'],
+                              view['persp']['value'],
+                              view['twist']['value'])
+        
+        if iview['dir']['use']:
+            self._display.SetViewdir(iview['dir']['x'], iview['dir']['y'], iview['dir']['z'])
+        
+        elif iview['focus']['x'] != -1:
+            self._display.SetFocus(self.iview['focus']['x'], self.iview['focus']['y'],
+                                   self.iview['focus']['z'])
+                                       
+        if 'rotation' in iview:
+            if iview['rotation']:
+                self._display.SetRotationMatrix(iview['rotation'])
+            else:
+                self._display.ResetRotation()
+        
+    def UpdateLight(self, event):
+        """!Change light settings"""
+        data = self.light
+        self._display.SetLight(x = data['position']['x'], y = data['position']['y'],
+                               z = data['position']['z'] / 100., color = data['color'],
+                               bright = data['bright'] / 100.,
+                               ambient = data['ambient'] / 100.)
+        self._display.DrawLightingModel()
+        if event.refresh:
+            self.Refresh(False)
+        
+    def UpdateMap(self, render = True):
+        """!Updates the canvas anytime there is a change to the
+        underlaying images or to the geometry of the canvas.
+        
+        @param render re-render map composition
+        """
+        start = time.clock()
+        
+        self.resize = False
+        
+        if self.render['quick'] is False:
+            self.parent.GetProgressBar().Show()
+            self.parent.GetProgressBar().SetRange(2)
+            self.parent.GetProgressBar().SetValue(0)
+        
+        if self.render['quick'] is False:
+            self.parent.GetProgressBar().SetValue(1)
+            self._display.Draw(False, -1)
+            if self.saveHistory:
+                self.ViewHistory(view = self.view, iview = self.iview)
+                self.saveHistory = False
+        elif self.render['quick'] is True:
+            # quick
+            mode = wxnviz.DRAW_QUICK_SURFACE | wxnviz.DRAW_QUICK_VOLUME
+            if self.render['vlines']:
+                mode |=  wxnviz.DRAW_QUICK_VLINES
+            if self.render['vpoints']:
+                mode |=  wxnviz.DRAW_QUICK_VPOINTS
+            self._display.Draw(True, mode)
+        else: # None -> reuse last rendered image
+            pass # TODO
+            
+        self.SwapBuffers()
+        # draw fringe after SwapBuffers, otherwise it don't have to be visible
+        # on some computers
+        if self.render['quick'] is False:
+            self._display.DrawFringe()
+            if self.decoration['arrow']['show']:
+                self._display.DrawArrow()
+            if self.decoration['scalebar']:
+                self._display.DrawScalebar()
+        if self.imagelist:
+            if ((self.render['quick'] and self.dragid > -1) or # during dragging
+                (not self.render['quick'] and self.dragid < 0)): # redraw
+                self._display.Start2D()
+                self.DrawImages()
+                
+            
+            
+        stop = time.clock()
+        
+        if self.render['quick'] is False:
+            self.parent.GetProgressBar().SetValue(2)
+            # hide process bar
+            self.parent.GetProgressBar().Hide()
+        
+        Debug.msg(3, "GLWindow.UpdateMap(): quick = %d, -> time = %g" % \
+                      (self.render['quick'], (stop-start)))
+        
+    def EraseMap(self):
+        """!Erase the canvas
+        """
+        self._display.EraseMap()
+        self.SwapBuffers()
+    
+    def _getDecorationSize(self):
+        """!Get initial size of north arrow/scalebar"""
+        size = self._display.GetLongDim() / 8.
+        coef = 0.01
+        if size < 1:
+            coef = 100.
+        return int(size * coef)/coef
+    
+    def SetDrawArrow(self, pos):
+        
+        if self._display.SetArrow(pos[0], pos[1], 
+                                 self.decoration['arrow']['size'],
+                                 self.decoration['arrow']['color']):
+            self._display.DrawArrow()
+            # update
+            self.decoration['arrow']['show'] = True
+            self.decoration['arrow']['position']['x'] = pos[0]
+            self.decoration['arrow']['position']['y'] = pos[1]
+            self.Refresh(False)
+    
+    def SetDrawScalebar(self, pos):
+        """!Add scale bar, sets properties and draw"""
+        if len(self.decoration['scalebar']) == 0:
+            self.decoration['scalebar'].append(
+                    self.nvizDefault.SetDecorDefaultProp(type = 'scalebar')['scalebar'])
+            self.decoration['scalebar'][0]['size'] = self._getDecorationSize()
+        else:
+            self.decoration['scalebar'].append(copy.deepcopy(self.decoration['scalebar'][-1]))
+            self.decoration['scalebar'][-1]['id'] += 1
+        
+        ret = self._display.SetScalebar(self.decoration['scalebar'][-1]['id'], pos[0], pos[1], 
+                                 self.decoration['scalebar'][-1]['size'],
+                                 self.decoration['scalebar'][-1]['color'])
+        if ret:
+            self._display.DrawScalebar()
+            # update
+            self.decoration['scalebar'][-1]['position']['x'] = pos[0]
+            self.decoration['scalebar'][-1]['position']['y'] = pos[1]
+            self.Refresh(False)
+        
+    def IsLoaded(self, item):
+        """!Check if layer (item) is already loaded
+        
+        @param item layer item
+        """
+        layer = self.tree.GetPyData(item)[0]['maplayer']
+        data = self.tree.GetPyData(item)[0]['nviz']
+        
+        if not data:
+            return 0
+        
+        if layer.type ==  'raster':
+            if 'object' not in data['surface']:
+                return 0
+        elif layer.type ==  'vector':
+            if 'object' not in data['vlines'] and \
+                    'object' not in data['points']:
+                return 0
+        
+        return 1
+
+    def _GetDataLayers(self, item, litems):
+        """!Return get list of enabled map layers"""
+        # load raster & vector maps
+        while item and item.IsOk():
+            type = self.tree.GetPyData(item)[0]['type']
+            if type ==  'group':
+                subItem = self.tree.GetFirstChild(item)[0]
+                self._GetDataLayers(subItem, litems)
+                item = self.tree.GetNextSibling(item)
+                
+            if not item.IsChecked() or \
+                    type not in ('raster', 'vector', '3d-raster'):
+                item = self.tree.GetNextSibling(item)
+                continue
+            
+            litems.append(item)
+            
+            item = self.tree.GetNextSibling(item)
+        
+    def LoadDataLayers(self):
+        """!Load raster/vector from current layer tree
+        
+        @todo volumes
+        """
+        if not self.tree:
+            return
+        
+        listOfItems = []
+        item = self.tree.GetFirstChild(self.tree.root)[0]
+        self._GetDataLayers(item, listOfItems)
+        
+        start = time.time()
+        
+        while(len(listOfItems) > 0):
+            item = listOfItems.pop()
+            type = self.tree.GetPyData(item)[0]['type']
+            if item in self.layers:
+                continue
+            # "raster (double click to set properties)" - tries to load this 
+            # layer - no idea how to fix it
+            if ' ' in self.tree.GetPyData(item)[0]['maplayer'].name:
+                return
+            try:
+                if type ==  'raster':
+                    self.LoadRaster(item)
+                elif type ==  '3d-raster':
+                    self.LoadRaster3d(item)
+                elif type ==  'vector':
+                    # data = self.tree.GetPyData(item)[0]['nviz']
+                    # vecType = []
+                    # if data and 'vector' in data:
+                    #     for v in ('lines', 'points'):
+                    #         if data['vector'][v]:
+                    #             vecType.append(v)
+                    layer = self.tree.GetPyData(item)[0]['maplayer']
+                    npoints, nlines, nfeatures, mapIs3D = self.lmgr.nviz.VectorInfo(layer)
+                    if npoints > 0:
+                        self.LoadVector(item, points = True)
+                    if nlines > 0:
+                        self.LoadVector(item, points = False)
+            except GException, e:
+                GError(parent = self,
+                       message = e.value)
+        
+        stop = time.time()
+        
+        Debug.msg(1, "GLWindow.LoadDataLayers(): time = %f" % (stop-start))
+                
+    def UnloadDataLayers(self, force = False):
+        """!Unload any layers that have been deleted from layer tree
+
+        @param force True to unload all data layers
+        """
+        if not self.tree:
+            return
+        
+        listOfItems = []
+        if not force:
+            item = self.tree.GetFirstChild(self.tree.root)[0]
+            self._GetDataLayers(item, listOfItems)
+        
+        start = time.time()
+        
+        update = False
+        layersTmp = self.layers[:]
+        for layer in layersTmp:
+            if layer in listOfItems:
+                continue
+            ltype = self.tree.GetPyData(layer)[0]['type']
+            try:
+                if ltype ==  'raster':
+                    self.UnloadRaster(layer)
+                elif ltype ==  '3d-raster':
+                    self.UnloadRaster3d(layer) 
+                elif ltype ==  'vector':
+                    maplayer = self.tree.GetPyData(layer)[0]['maplayer']
+                    npoints, nlines, nfeatures, mapIs3D = self.lmgr.nviz.VectorInfo(maplayer)
+                    if npoints > 0:
+                        self.UnloadVector(layer, points = True)
+                    if nlines > 0:
+                        self.UnloadVector(layer, points = False)
+                        
+            except GException, e:
+                GError(parent = self,
+                       message = e.value)
+        
+        if force and self.baseId > 0: # unload base surface when quitting
+            ret = self._display.UnloadSurface(self.baseId)
+            self.baseId = -1
+        if update:
+            self.lmgr.nviz.UpdateSettings()        
+            self.UpdateView(None)
+        
+        stop = time.time()
+        
+        Debug.msg(1, "GLWindow.UnloadDataLayers(): time = %f" % (stop-start))        
+        
+    def SetVectorSurface(self, data):
+        """!Set reference surfaces of vector"""
+        data['mode']['surface'] = {}
+        data['mode']['surface']['value'] = list()
+        data['mode']['surface']['show'] = list()
+        for name in self.GetLayerNames('raster'):
+            data['mode']['surface']['value'].append(name)
+            data['mode']['surface']['show'].append(True)
+        
+    def SetVectorFromCmd(self, item, data):
+        """!Set 3D view properties from cmd (d.vect)
+
+        @param item Layer Tree item
+        @param nviz data
+        """
+        cmd = self.tree.GetPyData(item)[0]['cmd']
+        if cmd[0] != 'd.vect':
+            return
+        for opt in cmd[1:]:
+            try:
+                key, value = opt.split('=')
+            except ValueError:
+                continue
+            if key == 'color':
+                data['lines']['color']['value'] = value
+                data['points']['color']['value'] = value
+
+    def SetMapObjProperties(self, item, id, nvizType):
+        """!Set map object properties
+        
+        Properties must be afterwards updated by
+        UpdateMapObjProperties().
+        
+        @param item layer item
+        @param id nviz layer id (or -1)
+        @param nvizType nviz data type (surface, points, vector)
+        """
+        if nvizType != 'constant':
+            mapType = self.tree.GetPyData(item)[0]['maplayer'].type
+            # reference to original layer properties (can be None)
+            data = self.tree.GetPyData(item)[0]['nviz']
+        else:
+            mapType = nvizType
+            data = self.constants[item]
+        
+        if not data:
+            # init data structure
+            if nvizType != 'constant':
+                self.tree.GetPyData(item)[0]['nviz'] = {}
+                data = self.tree.GetPyData(item)[0]['nviz']
+            
+            if mapType ==  'raster':
+                # reset to default properties
+                data[nvizType] = self.nvizDefault.SetSurfaceDefaultProp()
+                        
+            elif mapType ==  'vector':
+                # reset to default properties (lines/points)
+                data['vector'] = self.nvizDefault.SetVectorDefaultProp()
+                self.SetVectorFromCmd(item, data['vector'])
+                self.SetVectorSurface(data['vector']['points'])
+                self.SetVectorSurface(data['vector']['lines'])
+                
+            elif mapType ==  '3d-raster':
+                # reset to default properties 
+                data[nvizType] = self.nvizDefault.SetVolumeDefaultProp()
+                
+            elif mapType == 'constant':
+                data['constant'] = self.nvizDefault.SetConstantDefaultProp()
+        
+        else:
+            # complete data (use default values), not sure if this is necessary
+            if mapType ==  'raster':
+                if not data['surface']:
+                    data['surface'] = self.nvizDefault.SetSurfaceDefaultProp()
+            if mapType ==  'vector':
+                if not data['vector']['lines']:
+                    self.nvizDefault.SetVectorLinesDefaultProp(data['vector']['lines'])
+                if not data['vector']['points']:
+                    self.nvizDefault.SetVectorPointsDefaultProp(data['vector']['points'])     
+            # set updates
+            for sec in data.keys():
+                for sec1 in data[sec].keys():
+                    if sec1 == 'position':
+                        data[sec][sec1]['update'] = None
+                        continue
+                    if type(data[sec][sec1]) == types.DictType:
+                        for sec2 in data[sec][sec1].keys():
+                            if sec2 not in ('all', 'init', 'id'):
+                                data[sec][sec1][sec2]['update'] = None
+                    elif type(data[sec][sec1]) == types.ListType:
+                        for i in range(len(data[sec][sec1])):
+                            for sec2 in data[sec][sec1][i].keys():
+                                data[sec][sec1][i][sec2]['update'] = None
+            event = wxUpdateProperties(data = data)
+            wx.PostEvent(self, event)
+        
+        # set id
+        if id > 0:
+            if mapType in ('raster', '3d-raster'):
+                data[nvizType]['object'] = { 'id' : id,
+                                            'init' : False }
+            elif mapType ==  'vector':
+                data['vector'][nvizType]['object'] = { 'id' : id,
+                                                       'init' : False }
+            elif mapType ==  'constant':
+                data[nvizType]['object'] = { 'id' : id,
+                                             'init' : False }
+        
+        return data
+
+    def LoadRaster(self, item):
+        """!Load 2d raster map and set surface attributes
+        
+        @param layer item
+        """
+        return self._loadRaster(item)
+    
+    def LoadRaster3d(self, item):
+        """!Load 3d raster map and set surface attributes
+        
+        @param layer item
+        """
+        return self._loadRaster(item)
+    
+    def _loadRaster(self, item):
+        """!Load 2d/3d raster map and set its attributes
+        
+        @param layer item
+        """
+        layer = self.tree.GetPyData(item)[0]['maplayer']
+        
+        if layer.type not in ('raster', '3d-raster'):
+            return
+        
+        if layer.type ==  'raster':
+            id = self._display.LoadSurface(str(layer.name), None, None)
+            nvizType = 'surface'
+            errorMsg = _("Loading raster map")
+        elif layer.type ==  '3d-raster':
+            id = self._display.LoadVolume(str(layer.name), None, None)
+            nvizType = 'volume'
+            errorMsg = _("Loading 3d raster map")
+        else:
+            id = -1
+        
+        if id < 0:
+            if layer.type in ('raster', '3d-raster'):
+                self.log.WriteError("%s <%s> %s" % (errorMsg, layer.name, _("failed")))
+            else:
+                self.log.WriteError(_("Unsupported layer type '%s'") % layer.type)
+        
+        self.layers.append(item)
+        
+        # set default/workspace layer properties
+        data = self.SetMapObjProperties(item, id, nvizType)
+        
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self, event)
+        
+        # update tools window
+        if hasattr(self.lmgr, "nviz") and \
+                item ==  self.GetSelectedLayer(type = 'item'):
+            toolWin = self.lmgr.nviz
+            if layer.type ==  'raster':
+                win = toolWin.FindWindowById( \
+                    toolWin.win['vector']['lines']['surface'])
+                win.SetItems(self.GetLayerNames(layer.type))
+            
+            #toolWin.UpdatePage(nvizType)
+            #toolWin.SetPage(nvizType)
+        
+        return id
+    
+    def NewConstant(self):
+        """!Create new constant"""
+        index = len(self.constants)
+        try:
+            name = self.constants[-1]['constant']['object']['name'] + 1
+        except IndexError:
+            name = 1
+        data = dict()
+        self.constants.append(data)
+        data = self.SetMapObjProperties(item = index, id = -1, nvizType = 'constant')
+        self.AddConstant(data, name)
+        return name
+        
+    def AddConstant(self, data, name):
+        """!Add new constant"""
+        id = self._display.AddConstant(value = data['constant']['value'], color = data['constant']['color'])
+        self._display.SetSurfaceRes(id, data['constant']['resolution'], data['constant']['resolution'])
+        data['constant']['object'] = { 'id' : id,
+                                       'name': name,
+                                       'init' : False }
+    
+    def DeleteConstant(self, index):
+        """!Delete constant layer"""
+        id = self.constants[index]['constant']['object']['id']
+        self._display.UnloadSurface(id)
+        del self.constants[index]
+    
+    def SelectCPlane(self, index):
+        """!Select cutting plane"""
+        for plane in range (self._display.GetCPlanesCount()):
+            if plane == index:
+                self._display.SelectCPlane(plane)
+                self.cplanes[plane]['on'] = True
+                self._display.SetFenceColor(self.cplanes[plane]['shading'])
+            else:
+                self._display.UnselectCPlane(plane)
+                try:
+                    self.cplanes[plane]['on'] = False
+                except IndexError:
+                    pass
+                    
+    def UpdateCPlane(self, event):
+        """!Change cutting plane settings"""
+        current = event.current
+        for each in event.update:
+            if each == 'rotation':
+                self._display.SetCPlaneRotation(0, self.cplanes[current]['rotation']['tilt'],
+                                                   self.cplanes[current]['rotation']['rot'])
+            if each == 'position':
+                self._display.SetCPlaneTranslation(self.cplanes[current]['position']['x'],
+                                                   self.cplanes[current]['position']['y'],
+                                                   self.cplanes[current]['position']['z'])
+            if each == 'shading':
+                self._display.SetFenceColor(self.cplanes[current]['shading'])
+            
+    def UnloadRaster(self, item):
+        """!Unload 2d raster map
+        
+        @param layer item
+        """
+        return self._unloadRaster(item)
+    
+    def UnloadRaster3d(self, item):
+        """!Unload 3d raster map
+        
+        @param layer item
+        """
+        return self._unloadRaster(item)
+    
+    def _unloadRaster(self, item):
+        """!Unload 2d/3d raster map
+        
+        @param item layer item
+        """
+        layer = self.tree.GetPyData(item)[0]['maplayer']
+        
+        if layer.type not in ('raster', '3d-raster'):
+            return
+        
+        data = self.tree.GetPyData(item)[0]['nviz']
+        
+        if layer.type ==  'raster':
+            nvizType = 'surface'
+            unloadFn = self._display.UnloadSurface
+            errorMsg = _("Unable to unload raster map")
+            successMsg = _("Raster map")
+        else:
+            nvizType = 'volume'
+            unloadFn = self._display.UnloadVolume
+            errorMsg = _("Unable to unload 3d raster map")
+            successMsg = _("3d raster map")
+        
+        try:
+            id = data[nvizType]['object']['id']
+        except KeyError:
+            return
+        
+        if unloadFn(id) ==  0:
+            self.log.WriteError("%s <%s>" % (errorMsg, layer.name))
+        else:
+            self.log.WriteLog("%s <%s> %s" % (successMsg, layer.name, _("unloaded successfully")))
+        
+        data[nvizType].pop('object')
+        
+        self.layers.remove(item)
+        
+        # update tools window
+        if hasattr(self.lmgr, "nviz"):
+            toolWin = self.lmgr.nviz
+            if layer.type ==  'raster':
+                win = toolWin.FindWindowById(toolWin.win['vector']['lines']['surface'])
+                win.SetItems(self.GetLayerNames(layer.type))
+                win = toolWin.FindWindowById(toolWin.win['surface']['map'])
+                win.SetValue('')
+            if layer.type ==  '3d-raster':
+                win = toolWin.FindWindowById(toolWin.win['volume']['map'])
+                win.SetValue('')
+            if layer.type ==  'vector':
+                win = toolWin.FindWindowById(toolWin.win['vector']['map'])
+                win.SetValue('')
+        
+    def LoadVector(self, item, points = None, append = True):
+        """!Load 2D or 3D vector map overlay
+        
+        @param item layer item
+        @param points True to load points, False to load lines, None
+        to load both
+        @param append append vector to layer list
+        """
+        layer = self.tree.GetPyData(item)[0]['maplayer']
+        if layer.type !=  'vector':
+            return
+        
+        # set default properties
+        if points is None:
+            self.SetMapObjProperties(item, -1, 'lines')
+            self.SetMapObjProperties(item, -1, 'points')
+            vecTypes = ('points', 'lines')
+        elif points:
+            self.SetMapObjProperties(item, -1, 'points')
+            vecTypes = ('points', )
+        else:
+            self.SetMapObjProperties(item, -1, 'lines')
+            vecTypes = ('lines', )
+        
+        id = -1
+        for vecType in vecTypes:
+            if vecType == 'lines':
+                id, baseId = self._display.LoadVector(str(layer.GetName()), False)
+            else:
+                id, baseId = self._display.LoadVector(str(layer.GetName()), True)
+            if id < 0:
+                self.log.WriteError(_("Loading vector map <%(name)s> (%(type)s) failed") % \
+                    { 'name' : layer.name, 'type' : vecType })
+            # update layer properties
+            self.SetMapObjProperties(item, id, vecType)
+        if baseId > 0:
+            self.baseId = baseId # id of base surface (when no surface is loaded)
+        if append:
+            self.layers.append(item)
+        
+        # update properties
+        data = self.tree.GetPyData(item)[0]['nviz']
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self, event)
+        
+        # update tools window
+        if hasattr(self.lmgr, "nviz") and \
+                item ==  self.GetSelectedLayer(type = 'item'):
+            toolWin = self.lmgr.nviz
+            
+            toolWin.UpdatePage('vector')
+            ### toolWin.SetPage('vector')
+        
+        return id
+
+    def UnloadVector(self, item, points = None, remove = True):
+        """!Unload vector map overlay
+        
+        @param item layer item
+        @param points,lines True to unload given feature type
+        @param remove remove layer from list
+        """
+        layer = self.tree.GetPyData(item)[0]['maplayer']
+        data = self.tree.GetPyData(item)[0]['nviz']['vector']
+        
+        # if vecType is None:
+        #     vecType = []
+        #     for v in ('lines', 'points'):
+        #         if UserSettings.Get(group = 'nviz', key = 'vector',
+        #                             subkey = [v, 'show']):
+        #             vecType.append(v)
+        
+        if points is None:
+            vecTypes = ('points', 'lines')
+        elif points:
+            vecTypes = ('points', )
+        else:
+            vecTypes = ('lines', )
+        
+        for vecType in vecTypes:
+            if 'object' not in data[vecType]:
+                continue
+
+            id = data[vecType]['object']['id']
+            
+            if vecType ==  'lines':
+                ret = self._display.UnloadVector(id, False)
+            else:
+                ret = self._display.UnloadVector(id, True)
+            if ret ==  0:
+                self.log.WriteError(_("Unable to unload vector map <%(name)s> (%(type)s)") % \
+                    { 'name': layer.name, 'type' : vecType })
+            else:
+                self.log.WriteLog(_("Vector map <%(name)s> (%(type)s) unloaded successfully") % \
+                    { 'name' : layer.name, 'type' : vecType })
+            
+            data[vecType].pop('object')
+            
+        if remove and item in self.layers:
+            self.layers.remove(item)
+
+    def ResetView(self):
+        """!Reset to default view"""
+        self.iview['z-exag']['original'], \
+            self.iview['height']['value'], \
+            self.iview['height']['min'], \
+            self.iview['height']['max'] = self._display.SetViewDefault()
+            
+        ## set initial z-exag value at 1X    
+        self.view['z-exag']['value'] = 1.0
+        
+        self.view['z-exag']['min'] = UserSettings.Get(group = 'nviz', key = 'view',
+                                                      subkey = ('z-exag', 'min'))        
+        zexagMax = UserSettings.Get(group = 'nviz', key = 'view',
+                                    subkey = ('z-exag', 'max'))        
+        
+        self.view['position']['x'] = UserSettings.Get(group = 'nviz', key = 'view',
+                                                 subkey = ('position', 'x'))
+        self.view['position']['y'] = UserSettings.Get(group = 'nviz', key = 'view',
+                                                 subkey = ('position', 'y'))
+        self.view['persp']['value'] = UserSettings.Get(group = 'nviz', key = 'view',
+                                                       subkey = ('persp', 'value'))
+        
+        self.view['twist']['value'] = UserSettings.Get(group = 'nviz', key = 'view',
+                                                       subkey = ('twist', 'value'))
+        self._display.ResetRotation()
+        self.iview['rotation'] = None
+        self._display.LookAtCenter()
+        focus = self.iview['focus']
+        focus['x'], focus['y'], focus['z'] = self._display.GetFocus()
+        
+        self.PostViewEvent()
+        
+    def UpdateMapObjProperties(self, event):
+        """!Generic method to update data layer properties"""
+        data = event.data
+        
+        if 'surface' in data:
+            try:
+                id = data['surface']['object']['id']
+            except KeyError:
+                return
+            self.UpdateSurfaceProperties(id, data['surface'])
+            # -> initialized
+            data['surface']['object']['init'] = True
+            
+        elif 'constant' in data:
+            id = data['constant']['object']['id']
+            self.UpdateConstantProperties(id, data['constant'])
+            # -> initialized
+            data['constant']['object']['init'] = True  
+              
+        elif 'volume' in data:
+            id = data['volume']['object']['id']
+            self.UpdateVolumeProperties(id, data['volume'])
+            # -> initialized
+            data['volume']['object']['init'] = True
+            
+        elif 'vector' in data:
+            for type in ('lines', 'points'):
+                if 'object' in data['vector'][type]:
+                    id = data['vector'][type]['object']['id']
+                    self.UpdateVectorProperties(id, data['vector'], type)
+                    # -> initialized
+                    data['vector'][type]['object']['init'] = True
+    
+    def UpdateConstantProperties(self, id, data):
+        """!Update surface map object properties"""
+        self._display.SetSurfaceColor(id = id, map = False, value = data['color'])
+        self._display.SetSurfaceTopo(id = id, map = False, value = data['value'])
+        self._display.SetSurfaceRes(id, data['resolution'], data['resolution'])
+        if data['transp'] == 0:
+            self._display.UnsetSurfaceTransp(id)
+        else:
+            self._display.SetSurfaceTransp(id, map = False, value = data['transp'])
+            
+    def UpdateSurfaceProperties(self, id, data):
+        """!Update surface map object properties"""
+        # surface attributes
+        for attrb in ('color', 'mask',
+                     'transp', 'shine'):
+            if attrb not in data['attribute'] or \
+                    'update' not in data['attribute'][attrb]:
+                continue
+            
+            map = data['attribute'][attrb]['map']
+            value = data['attribute'][attrb]['value']
+            
+            if map is None: # unset
+                # only optional attributes
+                if attrb ==  'mask':
+                    # TODO: invert mask
+                    # TODO: broken in NVIZ
+                    self._display.UnsetSurfaceMask(id)
+                elif attrb ==  'transp':
+                    self._display.UnsetSurfaceTransp(id) 
+            else:
+                if type(value) == types.StringType and \
+                        len(value) <=  0: # ignore empty values (TODO: warning)
+                    continue
+                if attrb ==  'color':
+                    self._display.SetSurfaceColor(id, map, str(value))
+                elif attrb ==  'mask':
+                    # TODO: invert mask
+                    # TODO: broken in NVIZ
+                    self._display.SetSurfaceMask(id, False, str(value))
+                elif attrb ==  'transp':
+                    self._display.SetSurfaceTransp(id, map, str(value)) 
+                elif attrb ==  'shine':
+                    self._display.SetSurfaceShine(id, map, str(value)) 
+            data['attribute'][attrb].pop('update')
+        
+        # draw res
+        if 'update' in data['draw']['resolution']:
+            coarse = data['draw']['resolution']['coarse']
+            fine   = data['draw']['resolution']['fine']
+            
+            if data['draw']['all']:
+                self._display.SetSurfaceRes(-1, fine, coarse)
+            else:
+                self._display.SetSurfaceRes(id, fine, coarse)
+            data['draw']['resolution'].pop('update')
+        
+        # draw style
+        if 'update' in data['draw']['mode']:
+            if data['draw']['mode']['value'] < 0: # need to calculate
+                data['draw']['mode']['value'] = \
+                    self.nvizDefault.GetDrawMode(mode = data['draw']['mode']['desc']['mode'],
+                                                 style = data['draw']['mode']['desc']['style'],
+                                                 shade = data['draw']['mode']['desc']['shading'],
+                                                 string = True)
+            style = data['draw']['mode']['value']
+            if data['draw']['all']:
+                self._display.SetSurfaceStyle(-1, style)
+            else:
+                self._display.SetSurfaceStyle(id, style)
+            data['draw']['mode'].pop('update')
+        
+        # wire color
+        if 'update' in data['draw']['wire-color']:
+            color = data['draw']['wire-color']['value']
+            if data['draw']['all']:
+                self._display.SetWireColor(-1, str(color))
+            else:
+                self._display.SetWireColor(id, str(color))
+            data['draw']['wire-color'].pop('update')
+        
+        # position
+        if 'update' in data['position']:
+            x = data['position']['x']
+            y = data['position']['y']
+            z = data['position']['z']
+            self._display.SetSurfacePosition(id, x, y, z)
+            data['position'].pop('update')
+        data['draw']['all'] = False
+        
+    def UpdateVolumeProperties(self, id, data, isosurfId = None):
+        """!Update volume (isosurface/slice) map object properties"""
+        if 'update' in data['draw']['resolution']:
+            if data['draw']['mode']['value'] == 0:
+                self._display.SetIsosurfaceRes(id, data['draw']['resolution']['isosurface']['value'])
+            else:
+                self._display.SetSliceRes(id, data['draw']['resolution']['slice']['value'])                
+            data['draw']['resolution'].pop('update')
+        
+        if 'update' in data['draw']['shading']:
+            if data['draw']['mode']['value'] == 0:
+                if data['draw']['shading']['isosurface']['value'] < 0: # need to calculate
+                    mode = data['draw']['shading']['isosurface']['value'] = \
+                        self.nvizDefault.GetDrawMode(shade = data['draw']['shading']['isosurface'],
+                                                     string = False)
+                    self._display.SetIsosurfaceMode(id, mode)
+            else:
+                if data['draw']['shading']['slice']['value'] < 0: # need to calculate
+                    mode = data['draw']['shading']['slice']['value'] = \
+                        self.nvizDefault.GetDrawMode(shade = data['draw']['shading']['slice'],
+                                                     string = False)
+                    self._display.SetSliceMode(id, mode)
+            data['draw']['shading'].pop('update')
+        
+        #
+        # isosurface attributes
+        #
+        isosurfId = 0
+        for isosurf in data['isosurface']:
+            self._display.AddIsosurface(id, 0, isosurf_id = isosurfId)
+            for attrb in ('topo', 'color', 'mask',
+                          'transp', 'shine'):
+                if attrb not in isosurf or \
+                        'update' not in isosurf[attrb]:
+                    continue
+                map = isosurf[attrb]['map']
+                value = isosurf[attrb]['value']
+                
+                if map is None: # unset
+                    # only optional attributes
+                    if attrb == 'topo' :
+                        self._display.SetIsosurfaceTopo(id, isosurfId, map, str(value))
+                    elif attrb ==  'mask':
+                        # TODO: invert mask
+                        # TODO: broken in NVIZ
+                        self._display.UnsetIsosurfaceMask(id, isosurfId)
+                    elif attrb ==  'transp':
+                        self._display.UnsetIsosurfaceTransp(id, isosurfId) 
+                else:
+                    if type(value) == types.StringType and \
+                            len(value) <=  0: # ignore empty values (TODO: warning)
+                        continue
+                    elif attrb ==  'color':
+                        self._display.SetIsosurfaceColor(id, isosurfId, map, str(value))
+                    elif attrb ==  'mask':
+                        # TODO: invert mask
+                        # TODO: broken in NVIZ
+                        self._display.SetIsosurfaceMask(id, isosurfId, False, str(value))
+                    elif attrb ==  'transp':
+                        self._display.SetIsosurfaceTransp(id, isosurfId, map, str(value)) 
+                    elif attrb ==  'shine':
+                        self._display.SetIsosurfaceShine(id, isosurfId, map, str(value))  
+                isosurf[attrb].pop('update')
+            isosurfId +=  1
+        #
+        # slice attributes
+        #
+        sliceId = 0
+        for slice in data['slice']:
+            ret = self._display.AddSlice(id, slice_id = sliceId)
+            if 'update' in slice['position']:
+                pos = slice['position']
+                ret = self._display.SetSlicePosition(id, sliceId, pos['x1'], pos['x2'],
+                                               pos['y1'], pos['y2'], pos['z1'], pos['z2'], pos['axis'])
+                
+                slice['position'].pop('update')
+            if 'update' in slice['transp']:
+                tr = slice['transp']['value']
+                self._display.SetSliceTransp(id, sliceId, tr)
+            sliceId += 1
+                
+        # position
+        if 'update' in data['position'] and 'x' in data['position']:
+            x = data['position']['x']
+            y = data['position']['y']
+            z = data['position']['z']
+            self._display.SetVolumePosition(id, x, y, z)
+            data['position'].pop('update')
+            
+    def UpdateVectorProperties(self, id, data, type):
+        """!Update vector layer properties
+        
+        @param id layer id
+        @param data properties
+        @param type lines/points
+        """
+        if type ==  'points':
+            self.UpdateVectorPointsProperties(id, data[type])
+        else:
+            self.UpdateVectorLinesProperties(id, data[type])
+        
+    def UpdateVectorLinesProperties(self, id, data):
+        """!Update vector line map object properties"""
+        # mode
+        if 'update' in data['color'] or \
+                'update' in data['width'] or \
+                'update' in data['mode']:
+            width = data['width']['value']
+            color = data['color']['value']
+            if data['mode']['type'] ==  'flat':
+                flat = True
+                if 'surface' in data['mode']:
+                    data['mode'].pop('surface')
+            else:
+                flat = False
+            
+            self._display.SetVectorLineMode(id, color,
+                                            width, flat)
+            
+            if 'update' in data['color']:
+                data['color'].pop('update')
+            if 'update' in data['width']:
+                data['width'].pop('update')
+        
+        # height
+        if 'update' in data['height']:
+            self._display.SetVectorLineHeight(id,
+                                              data['height']['value'])
+            data['height'].pop('update')
+            
+        # surface
+        if 'surface' in data['mode'] and 'update' in data['mode']:
+            for item in range(len(data['mode']['surface']['value'])):
+                for type in ('raster', 'constant'):
+                    sid = self.GetLayerId(type = type,
+                                          name = data['mode']['surface']['value'][item])
+                    if sid > -1:
+                        if data['mode']['surface']['show'][item]:
+                            self._display.SetVectorLineSurface(id, sid)
+                        else:
+                            self._display.UnsetVectorLineSurface(id, sid)
+                        break
+                
+        if 'update' in data['mode']:
+                data['mode'].pop('update')
+        
+    def UpdateVectorPointsProperties(self, id, data):
+        """!Update vector point map object properties"""
+        if 'update' in data['size'] or \
+                'update' in data['width'] or \
+                'update' in data['marker'] or \
+                'update' in data['color']:
+                
+            ret = self._display.SetVectorPointMode(id, data['color']['value'],
+                                                   data['width']['value'], float(data['size']['value']),
+                                                   data['marker']['value'] + 1)
+            
+            error = None
+            if ret ==  -1:
+                error = _("Vector point layer not found (id = %d)") % id
+            elif ret ==  -2:
+                error = _("Unable to set data layer properties (id = %d)") % id
+
+            if error:
+                raise GException(_("Setting data layer properties failed.\n\n%s") % error)
+            
+            for prop in ('size', 'width', 'marker', 'color'):
+                if 'update' in data[prop]:
+                    data[prop].pop('update')
+        
+        # height
+        if 'update' in data['height']:
+            self._display.SetVectorPointHeight(id,
+                                               data['height']['value'])
+            data['height'].pop('update')
+            
+        # surface
+        if 'update' in data['mode'] and 'surface' in data['mode']:
+            for item in range(len(data['mode']['surface']['value'])):
+                for type in ('raster', 'constant'):
+                    sid = self.GetLayerId(type = type,
+                                          name = data['mode']['surface']['value'][item])
+                    if sid > -1:
+                        if data['mode']['surface']['show'][item]:
+                            self._display.SetVectorPointSurface(id, sid)
+                        else:
+                            self._display.UnsetVectorPointSurface(id, sid)   
+                        break
+            data['mode'].pop('update')
+            
+    def GetLayerNames(self, type):
+        """!Return list of map layer names of given type"""
+        layerName = []
+        
+        if type == 'constant':
+            for item in self.constants:
+                layerName.append(_("constant#") + str(item['constant']['object']['name']))
+        else:    
+            for item in self.layers:
+                mapLayer = self.tree.GetPyData(item)[0]['maplayer']
+                if type !=  mapLayer.GetType():
+                    continue
+                
+                layerName.append(mapLayer.GetName())
+        
+        return layerName
+    
+    def GetLayerId(self, type, name, vsubtyp = None):
+        """!Get layer object id or -1"""
+        if len(name) < 1:
+            return -1
+        
+        if type == 'constant':
+            for item in self.constants:
+                if _("constant#") + str(item['constant']['object']['name']) == name:
+                    return item['constant']['object']['id']
+                
+        
+        for item in self.layers:
+            mapLayer = self.tree.GetPyData(item)[0]['maplayer']
+            if type !=  mapLayer.GetType() or \
+                    name !=  mapLayer.GetName():
+                continue
+            
+            data = self.tree.GetPyData(item)[0]['nviz']
+            
+            try:
+                if type ==  'raster':
+                    return data['surface']['object']['id']
+                elif type ==  'vector':
+                    if vsubtyp == 'vpoint':
+                        return data['vector']['points']['object']['id']
+                    elif vsubtyp ==  'vline':
+                        return data['vector']['lines']['object']['id']
+                elif type ==  '3d-raster':
+                    return data['volume']['object']['id']
+            except KeyError:
+                return -1
+        return -1
+    
+    def ReloadLayersData(self):
+        """!Delete nviz data of all loaded layers and reload them from current settings"""
+        for item in self.layers:
+            type = self.tree.GetPyData(item)[0]['type']
+            layer = self.tree.GetPyData(item)[0]['maplayer']
+            data = self.tree.GetPyData(item)[0]['nviz']
+            
+            if type == 'raster':
+                self.nvizDefault.SetSurfaceDefaultProp(data['surface'])
+            if type == 'vector':
+                npoints, nlines, nfeatures, mapIs3D = self.lmgr.nviz.VectorInfo(layer)
+                if npoints > 0:
+                    self.nvizDefault.SetVectorPointsDefaultProp(data['vector']['points'])
+                if nlines > 0:
+                    self.nvizDefault.SetVectorLinesDefaultProp(data['vector']['lines'])
+            
+    def NvizCmdCommand(self):
+        """!Generate command for m.nviz.image according to current state"""
+        cmd = 'm.nviz.image '
+        
+        rasters = []
+        vectors = []
+        volumes = []
+        for item in self.layers:
+            if self.tree.GetPyData(item)[0]['type'] == 'raster':
+                rasters.append(item)
+            elif self.tree.GetPyData(item)[0]['type'] == '3d-raster':
+                volumes.append(item)
+            elif self.tree.GetPyData(item)[0]['type'] == 'vector':
+                vectors.append(item)
+        if not rasters and not self.constants:
+            return _("At least one raster map required")
+        # elevation_map/elevation_value
+        if self.constants:
+            subcmd = "elevation_value="
+            for constant in self.constants:
+                subcmd += "%d," % constant['constant']['value']
+            subcmd = subcmd.strip(', ') + ' '
+            cmd += subcmd
+        if rasters:
+            subcmd = "elevation_map="
+            for item in rasters:
+                subcmd += "%s," % self.tree.GetPyData(item)[0]['maplayer'].GetName()
+            subcmd = subcmd.strip(', ') + ' '
+            cmd += subcmd
+            #
+            # draw mode
+            #
+            cmdMode = "mode="
+            cmdFine = "resolution_fine="
+            cmdCoarse = "resolution_coarse="
+            cmdShading = "shading="
+            cmdStyle = "style="
+            cmdWire = "wire_color="
+            # test -a flag
+            flag_a = "-a "
+            nvizDataFirst = self.tree.GetPyData(rasters[0])[0]['nviz']['surface']['draw']
+            for item in rasters:
+                nvizData = self.tree.GetPyData(item)[0]['nviz']['surface']['draw']
+                if nvizDataFirst != nvizData:
+                    flag_a = ""
+            cmd += flag_a
+            for item in rasters:
+                nvizData = self.tree.GetPyData(item)[0]['nviz']['surface']['draw']
+                
+                cmdMode += "%s," % nvizData['mode']['desc']['mode']
+                cmdFine += "%s," % nvizData['resolution']['fine']
+                cmdCoarse += "%s," % nvizData['resolution']['coarse']
+                cmdShading += "%s," % nvizData['mode']['desc']['shading']
+                cmdStyle += "%s," % nvizData['mode']['desc']['style']
+                cmdWire += "%s," % nvizData['wire-color']['value']
+            for item in self.constants:
+                cmdMode += "fine,"
+                cmdFine += "%s," % item['constant']['resolution']
+                cmdCoarse += "%s," % item['constant']['resolution']
+                cmdShading += "gouraud,"
+                cmdStyle += "surface,"
+                cmdWire += "0:0:0,"
+            mode = []
+            for subcmd in (cmdMode, cmdFine, cmdCoarse, cmdShading, cmdStyle, cmdWire):
+                if flag_a:
+                    mode.append(subcmd.split(',')[0] + ' ')
+                else:
+                    subcmd = subcmd.strip(', ') + ' '
+                    cmd += subcmd
+            if flag_a:# write only meaningful possibilities
+                cmd += mode[0]
+                if 'fine' in mode[0]:
+                    cmd += mode[1]
+                elif 'coarse' in mode[0]:
+                    cmd += mode[2]            
+                elif 'both' in mode[0]:
+                    cmd += mode[2]
+                    cmd += mode[1]
+                if 'flat' in mode[3]:
+                    cmd += mode[3]
+                if 'wire' in mode[4]:
+                    cmd += mode[4]
+                if 'coarse' in mode[0] or 'both' in mode[0] and 'wire' in mode[3]:
+                    cmd += mode[5]
+            #
+            # attributes
+            #
+            cmdColorMap = "color_map="
+            cmdColorVal = "color="
+            for item in rasters:
+                nvizData = self.tree.GetPyData(item)[0]['nviz']['surface']['attribute']
+                if 'color' not in nvizData:
+                    cmdColorMap += "%s," % self.tree.GetPyData(item)[0]['maplayer'].GetName()
+                else:
+                    if nvizData['color']['map']:
+                        cmdColorMap += "%s," % nvizData['color']['value']
+                    else:
+                        cmdColorVal += "%s," % nvizData['color']['value']
+                        #TODO
+                        # transparency, shine, mask
+            for item in self.constants:
+                cmdColorVal += "%s," % item['constant']['color']
+            if cmdColorMap.split("=")[1]:
+                cmd += cmdColorMap.strip(', ') + ' '
+            if cmdColorVal.split("=")[1]:
+                cmd += cmdColorVal.strip(', ') + ' '
+            cmd += "\\\n"
+        #
+        # vlines
+        #
+        if vectors:
+            cmdLines = cmdLWidth = cmdLHeight = cmdLColor = cmdLMode = cmdLPos = \
+            cmdPoints = cmdPWidth = cmdPSize = cmdPColor = cmdPMarker = cmdPPos = cmdPLayer = ""
+            markers = ['x', 'box', 'sphere', 'cube', 'diamond',
+                       'dec_tree', 'con_tree', 'aster', 'gyro', 'histogram']
+            for vector in vectors:
+                npoints, nlines, nfeatures, mapIs3D = self.lmgr.nviz.VectorInfo(
+                                                      self.tree.GetPyData(vector)[0]['maplayer'])
+                nvizData = self.tree.GetPyData(vector)[0]['nviz']['vector']
+                if nlines > 0:
+                    cmdLines += "%s," % self.tree.GetPyData(vector)[0]['maplayer'].GetName()
+                    cmdLWidth += "%d," % nvizData['lines']['width']['value']
+                    cmdLHeight += "%d," % nvizData['lines']['height']['value']
+                    cmdLColor += "%s," % nvizData['lines']['color']['value']
+                    cmdLMode += "%s," % nvizData['lines']['mode']['type']
+                    cmdLPos += "0,0,%d," % nvizData['lines']['height']['value']
+                if npoints > 0:    
+                    cmdPoints += "%s," % self.tree.GetPyData(vector)[0]['maplayer'].GetName()
+                    cmdPWidth += "%d," % nvizData['points']['width']['value']
+                    cmdPSize += "%d," % nvizData['points']['size']['value']
+                    cmdPColor += "%s," % nvizData['points']['color']['value']
+                    cmdPMarker += "%s," % markers[nvizData['points']['marker']['value']]
+                    cmdPPos += "0,0,%d," % nvizData['points']['height']['value']
+                    cmdPLayer += "1,1,"
+            if cmdLines:
+                cmd += "vline=" + cmdLines.strip(',') + ' '
+                cmd += "vline_width=" + cmdLWidth.strip(',') + ' '
+                cmd += "vline_color=" + cmdLColor.strip(',') + ' '
+                cmd += "vline_height=" + cmdLHeight.strip(',') + ' '
+                cmd += "vline_mode=" + cmdLMode.strip(',') + ' '
+                cmd += "vline_position=" + cmdLPos.strip(',') + ' '
+            if cmdPoints:
+                cmd += "vpoint=" + cmdPoints.strip(',') + ' '
+                cmd += "vpoint_width=" + cmdPWidth.strip(',') + ' '
+                cmd += "vpoint_color=" + cmdPColor.strip(',') + ' '
+                cmd += "vpoint_size=" + cmdPSize.strip(',') + ' '
+                cmd += "vpoint_marker=" + cmdPMarker.strip(',') + ' '
+                cmd += "vpoint_position=" + cmdPPos.strip(',') + ' '
+            cmd += "\\\n"
+            
+        #
+        # volumes
+        #
+        if volumes:
+            cmdName = cmdShade = cmdRes = cmdPos = cmdIso = ""
+            cmdIsoColorMap = cmdIsoColorVal = cmdIsoTrMap = cmdIsoTrVal = ""
+            cmdSlice = cmdSliceTransp = cmdSlicePos = ""
+            for i, volume in enumerate(volumes):
+                nvizData = self.tree.GetPyData(volume)[0]['nviz']['volume']
+                cmdName += "%s," % self.tree.GetPyData(volume)[0]['maplayer'].GetName()
+                cmdShade += "%s," % nvizData['draw']['shading']['isosurface']['desc']
+                cmdRes += "%d," % nvizData['draw']['resolution']['isosurface']['value']
+                if nvizData['position']:
+                    cmdPos += "%d,%d,%d," % (nvizData['position']['x'], nvizData['position']['y'],
+                                            nvizData['position']['z'])
+                for iso in nvizData['isosurface']:
+                    level = iso['topo']['value']
+                    cmdIso += "%d:%s," % (i + 1, level)
+                    if iso['color']['map']:
+                        cmdIsoColorMap += "%s," % iso['color']['value']
+                    else:
+                        cmdIsoColorVal += "%s," % iso['color']['value']
+                    if 'transp' in iso:
+                        if iso['transp']['map']:
+                            cmdIsoTrMap += "%s," % iso['transp']['value']
+                        else:
+                            cmdIsoTrVal += "%s," % iso['transp']['value']     
+                            
+                for slice in nvizData['slice']:
+                    axis = ('x','y','z')[slice['position']['axis']]
+                    cmdSlice += "%d:%s," % (i + 1, axis)
+                    for coord in ('x1', 'x2', 'y1', 'y2', 'z1', 'z2'):
+                        cmdSlicePos += "%f," % slice['position'][coord]
+                    cmdSliceTransp += "%s," % slice['transp']['value']
+                        
+            cmd += "volume=" + cmdName.strip(',') + ' '
+            cmd += "volume_shading=" + cmdShade.strip(',') + ' '
+            cmd += "volume_resolution=" + cmdRes.strip(',') + ' '
+            if nvizData['position']:
+                cmd += "volume_position=" + cmdPos.strip(',') + ' '
+            if cmdIso:
+                cmd += "isosurf_level=" + cmdIso.strip(',') + ' '
+                if cmdIsoColorMap:
+                    cmd += "isosurf_color_map=" + cmdIsoColorMap.strip(',') + ' '
+                if cmdIsoColorVal:
+                    cmd += "isosurf_color_value=" + cmdIsoColorVal.strip(',') + ' ' 
+                if cmdIsoTrMap:
+                    cmd += "isosurf_transparency_map=" + cmdIsoTrMap.strip(',') + ' '
+                if cmdIsoTrVal:
+                    cmd += "isosurf_transparency_value=" + cmdIsoTrVal.strip(',') + ' '
+            if cmdSlice:
+                cmd += "slice=" + cmdSlice.strip(',') + ' '
+                cmd += "slice_position=" + cmdSlicePos.strip(',') + ' '
+                cmd += "slice_transparency=" + cmdSliceTransp.strip(',') + ' '
+                
+        #
+        # cutting planes
+        #
+        cplane = self.lmgr.nviz.FindWindowById(self.lmgr.nviz.win['cplane']['planes']).GetStringSelection()
+        try:
+            planeIndex = int(cplane.split()[-1]) - 1
+        except (IndexError, ValueError):
+            planeIndex = None
+        if planeIndex is not None:
+            shading = ['clear', 'top', 'bottom', 'blend', 'shaded']
+            cmd += "cplane=%d " % planeIndex
+            cmd += "cplane_rotation=%d " % self.cplanes[planeIndex]['rotation']['rot']
+            cmd += "cplane_tilt=%d " % self.cplanes[planeIndex]['rotation']['tilt']
+            cmd += "cplane_position=%d,%d,%d " % (self.cplanes[planeIndex]['position']['x'],
+                                           self.cplanes[planeIndex]['position']['y'],
+                                           self.cplanes[planeIndex]['position']['z'])
+            cmd += "cplane_shading=%s " % shading[self.cplanes[planeIndex]['shading']]
+            cmd += "\\\n"                                        
+        # 
+        # viewpoint
+        #
+        subcmd  = "position=%.2f,%.2f " % (self.view['position']['x'], self.view['position']['y'])
+        subcmd += "height=%d " % (self.iview['height']['value'])
+        subcmd += "perspective=%d " % (self.view['persp']['value'])
+        subcmd += "twist=%d " % (self.view['twist']['value'])
+        subcmd += "zexag=%d " % (self.view['z-exag']['value'] * self.iview['z-exag']['original'])
+        subcmd += "focus=%d,%d,%d " % (self.iview['focus']['x'],self.iview['focus']['y'],self.iview['focus']['z'])
+        cmd += subcmd
+        
+        # background
+        subcmd  = "bgcolor=%d:%d:%d " % (self.view['background']['color'][:3])
+        if self.view['background']['color'] != (255, 255, 255):
+            cmd += subcmd
+        cmd += "\\\n"
+        # light
+        subcmd  = "light_position=%.2f,%.2f,%.2f " % (self.light['position']['x'],
+                                                      self.light['position']['y'],
+                                                      self.light['position']['z']/100.)
+        subcmd += "light_brightness=%d " % (self.light['bright'])
+        subcmd += "light_ambient=%d " % (self.light['ambient'])
+        subcmd += "light_color=%d:%d:%d " % (self.light['color'][:3])
+        cmd += subcmd
+        cmd += "\\\n"
+        # fringe
+        toolWindow = self.lmgr.nviz
+        direction = ''
+        for dir in ('nw', 'ne', 'sw', 'se'):
+            if toolWindow.FindWindowById(toolWindow.win['fringe'][dir]).IsChecked():
+                direction += "%s," % dir
+        if direction:
+            subcmd = "fringe=%s " % (direction.strip(','))
+            color = toolWindow.FindWindowById(toolWindow.win['fringe']['color']).GetValue()
+            subcmd += "fringe_color=%d:%d:%d " % (color[0], color[1], color[2])
+            subcmd += "fringe_elevation=%d " % (toolWindow.FindWindowById(toolWindow.win['fringe']['elev']).GetValue())
+            cmd += subcmd
+            cmd += "\\\n"
+        # north arrow
+        if self.decoration['arrow']['show']:
+            subcmd = "arrow_position=%d,%d " % (self.decoration['arrow']['position']['x'],
+                                                self.decoration['arrow']['position']['y'])
+            subcmd += "arrow_color=%s " % self.decoration['arrow']['color']
+            subcmd += "arrow_size=%d " % self.decoration['arrow']['size']
+            cmd += subcmd
+            
+        # output
+        subcmd = 'output=nviz_output '
+        subcmd += 'format=ppm '
+        subcmd += 'size=%d,%d ' % self.GetClientSizeTuple()
+        cmd += subcmd
+        
+        return cmd
+    
+    def OnNvizCmd(self):
+        """!Generate and write command to command output"""
+        self.log.WriteLog(self.NvizCmdCommand(), switchPage = True)
+        
+    def SaveToFile(self, FileName, FileType, width, height):
+        """!This draws the DC to a buffer that can be saved to a file.
+        
+        @todo fix BufferedPaintDC
+        
+        @param FileName file name
+        @param FileType type of bitmap
+        @param width image width
+        @param height image height
+        """
+        self._display.SaveToFile(FileName, width, height, FileType)
+                
+        # pbuffer = wx.EmptyBitmap(max(1, self.Map.width), max(1, self.Map.height))
+        # dc = wx.BufferedPaintDC(self, pbuffer)
+        # dc.Clear()
+        # self.SetCurrent()
+        # self._display.Draw(False, -1)
+        # pbuffer.SaveFile(FileName, FileType)
+        # self.SwapBuffers()
+        
+    def GetDisplay(self):
+        """!Get display instance"""
+        return self._display
+        
+    def ZoomToMap(self):
+        """!Reset view
+        """
+        self.lmgr.nviz.OnResetView(None)
+        
+    def TextBounds(self, textinfo):
+        """!Return text boundary data
+        
+        @param textinfo text metadata (text, font, color, rotation)
+        """
+        return self.parent.MapWindow2D.TextBounds(textinfo, relcoords = True)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/nviz/preferences.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/nviz/preferences.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/nviz/preferences.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,630 @@
+"""
+ at package nviz.preferences
+
+ at brief Nviz (3D view) preferences window
+
+Classes:
+ - preferences::NvizPreferencesDialog
+
+(C) 2008-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
+ at author Enhancements by Michael Barton <michael.barton asu.edu>
+ at author Anna Kratochvilova <KratochAnna seznam.cz> (Google SoC 2011)
+"""
+
+import os
+import copy
+
+import wx
+import wx.lib.colourselect as csel
+
+from core                 import globalvar
+from core.settings        import UserSettings
+from gui_core.preferences import PreferencesBaseDialog
+
+class NvizPreferencesDialog(PreferencesBaseDialog):
+    """!Nviz preferences dialog"""
+    def __init__(self, parent, title = _("3D view settings"),
+                 settings = UserSettings):
+        PreferencesBaseDialog.__init__(self, parent = parent, title = title,
+                                       settings = settings)
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_nviz.ico'), wx.BITMAP_TYPE_ICO))
+
+        self.toolWin = self.parent.nviz
+        
+        # create notebook pages
+        self._createViewPage(self.notebook)
+        self._createFlyPage(self.notebook)
+        self._createLightPage(self.notebook)
+        self._createSurfacePage(self.notebook)
+        self._createVectorPage(self.notebook)
+        
+        self.SetMinSize(self.GetBestSize())
+        self.SetSize(self.size)
+        self.btnDefault.SetToolTipString(_("Revert settings to default, changes are not applied"))
+        
+    def _createViewPage(self, notebook):
+        """!Create notebook page for view settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        
+        notebook.AddPage(page = panel,
+                         text = " %s " % _("View"))
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("View")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        row = 0
+        # perspective
+        pvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'persp')
+        ipvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'persp', internal = True)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Perspective:")),
+                      pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("value:")),
+                      pos = (row, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        pval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = pvals['value'],
+                           min = ipvals['min'],
+                           max = ipvals['max'])
+        self.winId['nviz:view:persp:value'] = pval.GetId()
+        gridSizer.Add(item = pval, pos = (row, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("step:")),
+                      pos = (row, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        pstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = pvals['step'],
+                           min = ipvals['min'],
+                           max = ipvals['max']-1)
+        self.winId['nviz:view:persp:step'] = pstep.GetId()
+        gridSizer.Add(item = pstep, pos = (row, 4),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        row += 1
+        
+        # position
+        posvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'position')
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Position:")),
+                      pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("x:")),
+                      pos = (row, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        px = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = posvals['x'] * 100,
+                           min = 0,
+                           max = 100)
+        self.winId['nviz:view:position:x'] = px.GetId()
+        gridSizer.Add(item = px, pos = (row, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = "y:"),
+                      pos = (row, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        py = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = posvals['y'] * 100,
+                           min = 0,
+                           max = 100)
+        self.winId['nviz:view:position:y'] = py.GetId()
+        gridSizer.Add(item = py, pos = (row, 4),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        row += 1
+        
+        # height is computed dynamically
+        
+        # twist
+        tvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'twist')
+        itvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'twist', internal = True)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Twist:")),
+                      pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("value:")),
+                      pos = (row, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        tval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = tvals['value'],
+                           min = itvals['min'],
+                           max = itvals['max'])
+        self.winId['nviz:view:twist:value'] = tval.GetId()
+        gridSizer.Add(item = tval, pos = (row, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        row += 1
+        
+        # z-exag
+        zvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'z-exag')
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Z-exag:")),
+                      pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("value:")),
+                      pos = (row, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        zval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = zvals['value'],
+                           min = -1e6,
+                           max = 1e6)
+        self.winId['nviz:view:z-exag:value'] = zval.GetId()
+        gridSizer.Add(item = zval, pos = (row, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+
+        box = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                           label = " %s " % (_("Image Appearance")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        # background color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Background color:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = UserSettings.Get(group = 'nviz', key = 'view',
+                                                            subkey = ['background', 'color']),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        color.SetName('GetColour')
+        self.winId['nviz:view:background:color'] = color.GetId()
+        gridSizer.Add(item = color, pos = (0, 1))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 5)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
+        
+    def _createFlyPage(self, notebook):
+        """!Create notebook page for view settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        
+        notebook.AddPage(page = panel,
+                         text = " %s " % _("Fly-through"))
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        # fly throuhg mode
+        box = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                           label = " %s " % (_("Fly-through mode")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        # move exag
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Move exag:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        moveExag = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 20, 
+                                  initial = UserSettings.Get(group = 'nviz', key = 'fly',
+                                                             subkey = ['exag', 'move']),
+                                  size = (65, -1))
+        self.winId['nviz:fly:exag:move'] = moveExag.GetId()
+        gridSizer.Add(item = moveExag, pos = (0, 1))
+        
+        # turn exag
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Turn exag:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        turnExag = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 20, 
+                                  initial = UserSettings.Get(group = 'nviz', key = 'fly',
+                                                             subkey = ['exag', 'turn']),
+                                  size = (65, -1))
+        self.winId['nviz:fly:exag:turn'] = turnExag.GetId()
+        gridSizer.Add(item = turnExag, pos = (1, 1))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 5)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
+        
+    def _createLightPage(self, notebook):
+        """!Create notebook page for light settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        
+        notebook.AddPage(page = panel,
+                         text = " %s " % _("Lighting"))
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Light")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+
+        
+        # position
+        posvals = UserSettings.Get(group = 'nviz', key = 'light', subkey = 'position')
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Position:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("x:")),
+                      pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        px = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = posvals['x'] * 100,
+                           min = -100,
+                           max = 100)
+        self.winId['nviz:light:position:x'] = px.GetId()
+        gridSizer.Add(item = px, pos = (0, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = "y:"),
+                      pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        py = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = posvals['y'] * 100,
+                           min = -100,
+                           max = 100)
+        self.winId['nviz:light:position:y'] = py.GetId()
+        gridSizer.Add(item = py, pos = (0, 4),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+                    
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("z:")),
+                      pos = (0, 5), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        pz = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = posvals['z'],
+                           min = 0,
+                           max = 100)
+        self.winId['nviz:light:position:z'] = pz.GetId()
+        gridSizer.Add(item = pz, pos = (0, 6),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+                    
+        # brightness
+        brightval = UserSettings.Get(group = 'nviz', key = 'light', subkey = 'bright')
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Brightness:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        bright = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = brightval,
+                           min = 0,
+                           max = 100)
+        self.winId['nviz:light:bright'] = bright.GetId()
+        gridSizer.Add(item = bright, pos = (1, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+                    
+        # ambient
+        ambval = UserSettings.Get(group = 'nviz', key = 'light', subkey = 'ambient')
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Ambient:")),
+                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        amb = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = ambval,
+                           min = 0,
+                           max = 100)
+        self.winId['nviz:light:ambient'] = amb.GetId()
+        gridSizer.Add(item = amb, pos = (2, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+                    
+        # light color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Color:")),
+                      pos = (3, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = UserSettings.Get(group = 'nviz', key = 'light',
+                                                            subkey = 'color'),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        color.SetName('GetColour')
+        self.winId['nviz:light:color'] = color.GetId()
+        gridSizer.Add(item = color, pos = (3, 2))
+        
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 5)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
+    
+    def _createSurfacePage(self, notebook):
+        """!Create notebook page for surface settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        
+        notebook.AddPage(page = panel,
+                         text = " %s " % _("Surface"))
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        # draw
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Draw")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
+        # mode
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                        label = _("Mode:")), flag = wx.ALIGN_CENTER_VERTICAL,
+                        pos = (0, 0))
+        mode = wx.Choice(parent = panel, id = wx.ID_ANY, size = (-1, -1),
+                          choices = [_("coarse"),
+                                     _("fine"),
+                                     _("both")])
+        self.winId['nviz:surface:draw:mode'] = mode.GetId()
+        mode.SetName('GetSelection')
+        mode.SetSelection(UserSettings.Get(group = 'nviz', key = 'surface',
+                                            subkey = ['draw', 'mode']))
+        gridSizer.Add(item = mode, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (0, 1))
+                    
+        # fine
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                        label = _("Fine mode:")), flag = wx.ALIGN_CENTER_VERTICAL,
+                        pos = (1, 0))
+        res = UserSettings.Get(group = 'nviz', key = 'surface', subkey = ['draw','res-fine'])
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                        label = _("resolution:")), flag = wx.ALIGN_CENTER_VERTICAL,
+                        pos = (1, 1))
+        fine = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = res,
+                           min = 1,
+                           max = 100)
+        self.winId['nviz:surface:draw:res-fine'] = fine.GetId()
+        
+        gridSizer.Add(item = fine, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (1, 2))
+                    
+        # coarse
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                        label = _("Coarse mode:")), flag = wx.ALIGN_CENTER_VERTICAL,
+                        pos = (2, 0))
+        res = UserSettings.Get(group = 'nviz', key = 'surface', subkey = ['draw','res-coarse'])
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                        label = _("resolution:")), flag = wx.ALIGN_CENTER_VERTICAL,
+                        pos = (2, 1))
+        coarse = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = res,
+                           min = 1,
+                           max = 100)
+        self.winId['nviz:surface:draw:res-coarse'] = coarse.GetId()
+        
+        gridSizer.Add(item = coarse, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (2, 2))
+        #style
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+            label = _("style:")), flag = wx.ALIGN_CENTER_VERTICAL,
+            pos = (3, 1))
+        style = wx.Choice(parent = panel, id = wx.ID_ANY, size = (-1, -1),
+                          choices = [_("wire"),
+                                     _("surface")])
+        self.winId['nviz:surface:draw:style'] = style.GetId()
+        style.SetName('GetSelection')
+        style.SetSelection(UserSettings.Get(group = 'nviz', key = 'surface',
+                                            subkey = ['draw', 'style']))
+        self.winId['nviz:surface:draw:style'] = style.GetId()
+        
+        gridSizer.Add(item = style, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (3, 2))
+        #wire color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+            label = _("wire color:")), flag = wx.ALIGN_CENTER_VERTICAL,
+            pos = (4, 1))
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = UserSettings.Get(group = 'nviz', key = 'surface',
+                                                            subkey = ['draw', 'wire-color']),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        color.SetName('GetColour')
+        self.winId['nviz:surface:draw:wire-color'] = color.GetId() 
+        gridSizer.Add(item = color, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (4, 2))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)            
+        pageSizer.Add(item = boxSizer, proportion = 0,
+              flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+              border = 5)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
+    
+    def _createVectorPage(self, notebook):
+        """!Create notebook page for vector settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        
+        notebook.AddPage(page = panel,
+                         text = " %s " % _("Vector"))
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        # vector lines
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Vector lines")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
+        row = 0
+        # icon size
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Width:")),
+                      pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        iwidth = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                            initial = 12,
+                            min = 1,
+                            max = 100)
+        self.winId['nviz:vector:lines:width'] = iwidth.GetId()
+        iwidth.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
+                                        subkey = ['lines', 'width']))
+        gridSizer.Add(item = iwidth, pos = (row, 1),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # icon color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Color:")),
+                      pos = (row, 4), flag = wx.ALIGN_CENTER_VERTICAL)
+        icolor = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        icolor.SetName('GetColour')
+        self.winId['nviz:vector:lines:color'] = icolor.GetId()
+        icolor.SetColour(UserSettings.Get(group = 'nviz', key = 'vector',
+                                          subkey = ['lines', 'color']))
+        gridSizer.Add(item = icolor, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 5))
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 5)
+        
+        # vector points
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Vector points")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 5)
+        
+        row = 0
+        # icon size
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Size:")),
+                      pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        isize = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                            initial = 100,
+                            min = 1,
+                            max = 1e6)
+        self.winId['nviz:vector:points:size'] = isize.GetId()
+        isize.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
+                                        subkey = ['points', 'size']))
+        gridSizer.Add(item = isize, pos = (row, 1),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # icon symbol
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Marker:")),
+                      pos = (row, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        isym = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
+                          choices = UserSettings.Get(group = 'nviz', key = 'vector',
+                                                   subkey = ['points', 'marker'], internal = True))
+        isym.SetName("GetSelection")
+        self.winId['nviz:vector:points:marker'] = isym.GetId()
+        isym.SetSelection(UserSettings.Get(group = 'nviz', key = 'vector',
+                                           subkey = ['points', 'marker']))
+        gridSizer.Add(item = isym, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 3))
+        
+        # icon color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Color:")),
+                      pos = (row, 4), flag = wx.ALIGN_CENTER_VERTICAL)
+        icolor = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        icolor.SetName('GetColour')
+        self.winId['nviz:vector:points:color'] = icolor.GetId()
+        icolor.SetColour(UserSettings.Get(group = 'nviz', key = 'vector',
+                                          subkey = ['points', 'color']))
+        gridSizer.Add(item = icolor, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 5))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 5)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
+
+    def OnDefault(self, event):
+        """!Button 'Set to default' pressed"""
+        self.settings.userSettings = copy.deepcopy(self.settings.defaultSettings)
+        
+        # update widgets
+        for gks in self.winId.keys():
+            subkey1 = None
+            try:
+                group, key, subkey = gks.split(':')
+                value = self.settings.Get(group, key, subkey)
+            except ValueError:
+                group, key, subkey, subkey1 = gks.split(':')
+                value = self.settings.Get(group, key, [subkey, subkey1])
+            if subkey == 'position':
+                if subkey1 in ('x', 'y'):
+                    value = float(value) * 100
+            win = self.FindWindowById(self.winId[gks])
+            if win.GetName() == 'GetSelection':
+                value = win.SetSelection(value)
+            else:
+                value = win.SetValue(value)        
+        
+    def OnApply(self, event):
+        """Apply Nviz settings for current session"""
+        for item in self.winId.keys():
+            try:
+                group, key, subkey = item.split(':')
+                subkey1 = None
+            except ValueError:
+                group, key, subkey, subkey1 = item.split(':')
+            
+            id = self.winId[item]
+            win = self.FindWindowById(id)
+            if win.GetName() == 'GetSelection':
+                value = win.GetSelection()
+            elif win.GetName() == 'GetColour':
+                value = tuple(win.GetValue())
+            else:
+                value = win.GetValue()
+            
+            if subkey == 'position':
+                if subkey1 in ('x', 'y'):
+                    value = float(value) / 100
+            if subkey1:
+                self.settings.Set(group, value, key, [subkey, subkey1])
+            else:
+                self.settings.Set(group, value, key, subkey)
+                
+        self.toolWin.LoadSettings()
+        
+        
+    def OnSave(self, event):
+        """!Save button pressed
+        
+        Apply changes and save settings to configuration file
+        """
+        self.OnApply(None)
+        fileSettings = {}
+        UserSettings.ReadSettingsFile(settings = fileSettings)
+        fileSettings['nviz'] = UserSettings.Get(group = 'nviz')
+        
+        UserSettings.SaveToFile(fileSettings)
+        self.parent.goutput.WriteLog(
+                _('3D view settings saved to file <%s>.') % UserSettings.filePath)
+        
+        self.Destroy()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/nviz/tools.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/nviz/tools.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/nviz/tools.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,4883 @@
+"""!
+ at package nviz.tools
+
+ at brief Nviz (3D view) tools window
+
+Classes:
+ - tools::NvizToolWindow
+ - tools::PositionWindow
+ - tools::ViewPositionWindow
+ - tools::LightPositionWindow
+
+(C) 2008-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
+ at author Enhancements by Michael Barton <michael.barton asu.edu>
+ at author Anna Kratochvilova <kratochanna gmail.com> (Google SoC 2011)
+"""
+
+import os
+import copy
+import types
+
+import wx
+import wx.lib.colourselect  as csel
+import wx.lib.scrolledpanel as SP
+import wx.lib.filebrowsebutton as filebrowse
+try:
+    import wx.lib.agw.flatnotebook as FN
+except ImportError:
+    import wx.lib.flatnotebook as FN
+try:
+    from agw import foldpanelbar as fpb
+except ImportError: # if it's not there locally, try the wxPython lib.
+    try:
+        import wx.lib.agw.foldpanelbar as fpb
+    except ImportError:
+        import wx.lib.foldpanelbar as fpb # versions <=2.5.5.1
+        
+import grass.script as grass
+
+from core               import globalvar
+from core.gcmd          import GMessage, RunCommand
+from core.settings      import UserSettings
+from nviz.animation     import EVT_ANIM_FIN, EVT_ANIM_UPDATE_IDX
+from gui_core.widgets   import ScrolledPanel, NumTextCtrl, FloatSlider, SymbolButton
+from gui_core.gselect   import Select
+from core.debug         import Debug
+try:
+    from nviz.mapwindow import wxUpdateProperties, wxUpdateView,\
+                               wxUpdateLight, wxUpdateCPlane
+    import wxnviz
+except ImportError:
+    pass
+
+class NvizToolWindow(FN.FlatNotebook):
+    """!Nviz (3D view) tools panel
+    """
+    def __init__(self, parent, display, id = wx.ID_ANY,
+                 style = globalvar.FNPageStyle|FN.FNB_NO_X_BUTTON,
+                 **kwargs):
+        Debug.msg(5, "NvizToolWindow.__init__()")
+        self.parent     = parent # GMFrame
+        self.mapDisplay = display
+        self.mapWindow  = display.GetWindow()
+        self._display   = self.mapWindow.GetDisplay()
+         
+        if globalvar.hasAgw:
+            kwargs['agwStyle'] = style
+        else:
+            kwargs['style'] = style
+        FN.FlatNotebook.__init__(self, parent, id, **kwargs)
+        self.SetTabAreaColour(globalvar.FNPageColor)
+        
+        self.win  = {} # window ids
+        self.page = {} # page ids
+
+        # view page
+        self.AddPage(page = self._createViewPage(),
+                     text = " %s " % _("View"))
+
+        # data page
+        self.AddPage(page = self._createDataPage(),
+                     text = " %s " % _("Data"))
+        
+        # appearance page
+        self.AddPage(page = self._createAppearancePage(),
+                     text = " %s " % _("Appearance"))
+                    
+        # analysis page
+        self.AddPage(page = self._createAnalysisPage(),
+                     text = " %s " % _("Analysis"))
+        # view page
+        self.AddPage(page = self._createAnimationPage(),
+                     text = " %s " % _("Animation"))
+        
+        self.UpdateSettings()
+        
+        self.mapWindow.SetToolWin(self)
+        
+        self.pageChanging = False
+        self.vetoGSelectEvt = False #when setting map, event is invoked
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.Refresh(False)
+        
+        # bindings
+        self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+        
+        self.Bind(EVT_ANIM_FIN, self.OnAnimationFinished)
+        self.Bind(EVT_ANIM_UPDATE_IDX, self.OnAnimationUpdateIndex)
+        
+        Debug.msg(3, "NvizToolWindow.__init__()")
+        
+        self.Update()
+        wx.CallAfter(self.SetPage, 'view')
+        wx.CallAfter(self.UpdateScrolling, (self.foldpanelData, self.foldpanelAppear,
+                                            self.foldpanelAnalysis))       
+        wx.CallAfter(self.SetInitialMaps)
+        
+    def SetInitialMaps(self):
+        """!Set initial raster and vector map"""
+        for l_type in ('raster', 'vector', '3d-raster'):
+            selectedLayer = self.mapWindow.GetSelectedLayer()
+            layers = self.mapWindow.Map.GetListOfLayers(l_type = l_type, l_active = True)
+            if selectedLayer in layers:
+                selection = selectedLayer.GetName()
+            else:
+                try:
+                    selection = layers[0].GetName()
+                except:
+                    continue
+            if l_type == 'raster':
+                self.FindWindowById(self.win['surface']['map']).SetValue(selection)
+                self.FindWindowById(self.win['fringe']['map']).SetValue(selection)
+            elif l_type == 'vector':
+                self.FindWindowById(self.win['vector']['map']).SetValue(selection)
+            elif l_type == '3d-raster':
+                self.FindWindowById(self.win['volume']['map']).SetValue(selection)
+               
+    def UpdateState(self, **kwargs):
+        if 'view' in kwargs:
+            self.mapWindow.view = kwargs['view']
+            self.FindWindowById(self.win['view']['position']).data = kwargs['view']
+            self.FindWindowById(self.win['view']['position']).PostDraw()
+        if 'iview' in kwargs:
+            self.mapWindow.iview = kwargs['iview']
+        if 'light' in kwargs:
+            self.mapWindow.light = kwargs['light']  
+            self.FindWindowById(self.win['light']['position']).data = kwargs['light']  
+            self.FindWindowById(self.win['light']['position']).PostDraw()
+        if 'fly' in kwargs:
+            self.mapWindow.fly['exag'] = kwargs['fly']['exag']
+    
+    def LoadSettings(self):
+        """!Load Nviz settings and apply to current session"""
+        view = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'view')) # copy
+        light = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'light')) # copy
+        fly = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'fly')) # copy
+        self.UpdateState(view = view, light = light, fly = fly)
+        self.PostViewEvent(zExag = True)
+        self.PostLightEvent()
+        self.UpdatePage('view')
+        self.UpdatePage('light')
+        
+        self.mapWindow.ReloadLayersData()
+        self.UpdatePage('surface')
+        self.UpdatePage('vector')
+        self.UpdateSettings()
+               
+    def OnPageChanged(self, event):
+        new = event.GetSelection()
+        # self.ChangeSelection(new)
+        
+    def PostViewEvent(self, zExag = False):
+        """!Change view settings"""
+        event = wxUpdateView(zExag = zExag)
+        wx.PostEvent(self.mapWindow, event)
+        
+    def PostLightEvent(self, refresh = False): 
+        """!Change light settings"""   
+        event = wxUpdateLight(refresh = refresh)
+        wx.PostEvent(self.mapWindow, event)
+        
+    def OnSize(self, event):
+        """!After window is resized, update scrolling"""
+        # workaround to resize captionbars of foldpanelbar
+        wx.CallAfter(self.UpdateScrolling, (self.foldpanelData, self.foldpanelAppear,
+                                            self.foldpanelAnalysis)) 
+        event.Skip()
+           
+    def OnPressCaption(self, event):
+        """!When foldpanel item collapsed/expanded, update scrollbars"""
+        foldpanel = event.GetBar().GetGrandParent().GetParent()
+        wx.CallAfter(self.UpdateScrolling, (foldpanel,))
+        event.Skip()
+        
+    def UpdateScrolling(self, foldpanels):
+        """!Update scrollbars in foldpanel"""
+        for foldpanel in foldpanels:
+            length = foldpanel.GetPanelsLength(collapsed = 0, expanded = 0)
+            # virtual width is set to fixed value to suppress GTK warning
+            foldpanel.GetParent().SetVirtualSize((100, length[2]))
+            foldpanel.GetParent().Layout()
+        
+    def _createViewPage(self):
+        """!Create view settings page"""
+        panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
+        panel.SetupScrolling(scroll_x = False)
+        self.page['view'] = { 'id' : 0,
+                              'notebook' : self.GetId()}
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Control View")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 10)
+        
+        self.win['view'] = {}
+        
+        # position
+        posSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
+        self._createCompass(panel = panel, sizer = posSizer, type = 'view')
+        
+        view = ViewPositionWindow(panel, size = (175, 175),
+                                  mapwindow = self.mapWindow)
+        self.win['view']['position'] = view.GetId()
+        posSizer.Add(item = view,
+                     pos = (1, 1), flag = wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = posSizer, pos = (0, 0))
+                  
+        # perspective
+        # set initial defaults here (or perhaps in a default values file), not in user settings
+        #todo: consider setting an absolute max at 360 instead of undefined. (leave the default max value at pi)
+        self._createControl(panel, data = self.win['view'], name = 'persp',
+                            range = (1,180),
+                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedText))
+        
+        gridSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Perspective:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER)
+        gridSizer.Add(item = self.FindWindowById(self.win['view']['persp']['slider']), pos = (2, 0),
+                      flag = wx.ALIGN_CENTER)
+        gridSizer.Add(item = self.FindWindowById(self.win['view']['persp']['text']), pos = (3, 0),
+                      flag = wx.ALIGN_CENTER)        
+        
+        # twist
+        self._createControl(panel, data = self.win['view'], name = 'twist',
+                            range = (-180,180),
+                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedText))
+        gridSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Twist:")),
+                      pos = (1, 1), flag = wx.ALIGN_CENTER)
+        gridSizer.Add(item = self.FindWindowById(self.win['view']['twist']['slider']), pos = (2, 1))
+        gridSizer.Add(item = self.FindWindowById(self.win['view']['twist']['text']), pos = (3, 1),
+                      flag = wx.ALIGN_CENTER)        
+        
+        # height + z-exag
+        self._createControl(panel, data = self.win['view'], name = 'height', sliderHor = False,
+                            range = (0, 1),
+                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedText))
+        self._createControl(panel, data = self.win['view'], name = 'z-exag', sliderHor = False,
+                            range = (0, 10), floatSlider = True,
+                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedText))
+        self.FindWindowById(self.win['view']['z-exag']['slider']).SetValue(1)
+        self.FindWindowById(self.win['view']['z-exag']['text']).SetValue(1)
+        
+        heightSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        heightSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Height:")),
+                      pos = (0, 0), flag = wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL, span = (1, 2))
+        heightSizer.Add(item = self.FindWindowById(self.win['view']['height']['slider']),
+                        flag = wx.ALIGN_RIGHT, pos = (1, 0))
+        heightSizer.Add(item = self.FindWindowById(self.win['view']['height']['text']),
+                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT, pos = (1, 1))
+        heightSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Z-exag:")),
+                      pos = (0, 2), flag = wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL, span = (1, 2))
+        heightSizer.Add(item = self.FindWindowById(self.win['view']['z-exag']['slider']),
+                        flag = wx.ALIGN_RIGHT, pos = (1, 2))
+        heightSizer.Add(item = self.FindWindowById(self.win['view']['z-exag']['text']),
+                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT | wx.TOP |
+                        wx.BOTTOM | wx.RIGHT, pos = (1, 3))
+        
+        gridSizer.Add(item = heightSizer, pos = (0, 1), flag = wx.ALIGN_CENTER)
+        
+        # view setup + reset
+        viewSizer = wx.BoxSizer(wx.HORIZONTAL)
+        viewSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY,
+                                           label = _("Look:")),
+                      flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL,
+                      border = 5)
+        here = wx.ToggleButton(panel, id = wx.ID_ANY, label = _("here"))
+        here.Bind(wx.EVT_TOGGLEBUTTON, self.OnLookAt)
+        here.SetName('here')
+        viewSizer.Add(item = here, flag = wx.TOP|wx.BOTTOM|wx.LEFT|wx.ALIGN_CENTER_VERTICAL,
+                      border = 5)
+                    
+        center = wx.Button(panel, id = wx.ID_ANY, label = _("center"))
+        center.Bind(wx.EVT_BUTTON, self.OnLookAt)
+        center.SetName('center')
+        viewSizer.Add(item = center, flag = wx.TOP|wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL,
+                      border = 5)
+                    
+        top = wx.Button(panel, id = wx.ID_ANY, label = _("top"))
+        top.Bind(wx.EVT_BUTTON, self.OnLookAt)
+        top.SetName('top')
+        viewSizer.Add(item = top, flag = wx.TOP|wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL,
+                      border = 5)
+                    
+        reset = wx.Button(panel, id = wx.ID_ANY, label = _("reset"))
+        reset.SetToolTipString(_("Reset to default view"))
+        reset.Bind(wx.EVT_BUTTON, self.OnResetView)
+        viewSizer.Add(item = reset, proportion = 0,
+                      flag = wx.TOP|wx.BOTTOM|wx.RIGHT| wx.ALIGN_RIGHT,
+                      border = 5)
+        
+        gridSizer.AddGrowableCol(2)
+        gridSizer.Add(item = viewSizer, pos = (4, 0), span = (1, 3),
+                      flag = wx.EXPAND)
+        
+        # body
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 2)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 3)
+        
+        box = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                           label = " %s " % (_("Image Appearance")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        # background color
+        self.win['view']['background'] = {}
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Background color:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = UserSettings.Get(group = 'nviz', key = 'view',
+                                                            subkey = ['background', 'color']),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        self.win['view']['background']['color'] = color.GetId()
+        color.Bind(csel.EVT_COLOURSELECT, self.OnBgColor)
+        gridSizer.Add(item = color, pos = (0, 1))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT,
+                      border = 3)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
+        
+    def _createAnimationPage(self):
+        """!Create view settings page"""
+        panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
+        panel.SetupScrolling(scroll_x = False)
+        self.page['animation'] = { 'id' : 0,
+                                   'notebook' : self.GetId()}
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Animation")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        hSizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        self.win['anim'] = {}
+        # animation help text
+        help = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                             label = _("Press 'Record' button and start changing the view. "
+                                       "It is recommended to use fly-through mode "
+                                       "(Map Display toolbar) to achieve smooth motion."))
+        self.win['anim']['help'] = help.GetId()
+        hSizer.Add(item = help, proportion = 0)
+        boxSizer.Add(item = hSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 5)
+                     
+        # animation controls
+        hSizer = wx.BoxSizer(wx.HORIZONTAL)
+        record = SymbolButton(parent = panel, id = wx.ID_ANY,
+                                    usage = "record", label = _("Record"))
+        play = SymbolButton(parent = panel, id = wx.ID_ANY,
+                                  usage = "play", label = _("Play"))
+        pause = SymbolButton(parent = panel, id = wx.ID_ANY,
+                                   usage = "pause", label = _("Pause"))
+        stop = SymbolButton(parent = panel, id = wx.ID_ANY,
+                                  usage = "stop", label = _("Stop"))
+        
+        self.win['anim']['record'] = record.GetId()
+        self.win['anim']['play'] = play.GetId()
+        self.win['anim']['pause'] = pause.GetId()
+        self.win['anim']['stop'] = stop.GetId()
+                            
+        self._createControl(panel, data = self.win['anim'], name = 'frameIndex',
+                            range = (0, 1), floatSlider = False,
+                            bind = (self.OnFrameIndex, None, self.OnFrameIndexText))
+        frameSlider = self.FindWindowById(self.win['anim']['frameIndex']['slider'])
+        frameText = self.FindWindowById(self.win['anim']['frameIndex']['text'])
+        infoLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Total number of frames :"))
+        info = wx.StaticText(parent = panel, id = wx.ID_ANY)
+        self.win['anim']['info'] = info.GetId()
+        
+        fpsLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Frame rate (FPS):"))
+        fps = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = UserSettings.Get(group = 'nviz', key = 'animation', subkey = 'fps'),
+                           min = 1,
+                           max = 50)
+        self.win['anim']['fps'] = fps.GetId()
+        fps.SetToolTipString(_("Frames are recorded with given frequency (FPS). "))
+                            
+        record.Bind(wx.EVT_BUTTON, self.OnRecord)
+        play.Bind(wx.EVT_BUTTON, self.OnPlay)
+        stop.Bind(wx.EVT_BUTTON, self.OnStop)
+        pause.Bind(wx.EVT_BUTTON, self.OnPause)
+        fps.Bind(wx.EVT_SPINCTRL, self.OnFPS)
+        
+        hSizer.Add(item = record, proportion = 0)
+        hSizer.Add(item = play, proportion = 0)
+        hSizer.Add(item = pause, proportion = 0)
+        hSizer.Add(item = stop, proportion = 0)
+        boxSizer.Add(item = hSizer, proportion = 0,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        
+        sliderBox = wx.BoxSizer(wx.HORIZONTAL)
+        sliderBox.Add(item = frameSlider, proportion = 1, border = 5, flag = wx.EXPAND | wx.RIGHT)
+        sliderBox.Add(item = frameText, proportion = 0, border = 5, flag = wx.EXPAND| wx.RIGHT | wx.LEFT)
+        boxSizer.Add(item = sliderBox, proportion = 0, flag = wx.EXPAND)
+        
+        # total number of frames
+        hSizer = wx.BoxSizer(wx.HORIZONTAL)
+        hSizer.Add(item = infoLabel, proportion = 0, flag = wx.RIGHT, border = 5)
+        hSizer.Add(item = info, proportion = 0, flag = wx.LEFT, border = 5)
+        
+        boxSizer.Add(item = hSizer, proportion = 0,
+                     flag = wx.ALL | wx.EXPAND, border = 5)
+                     
+        # frames per second
+        hSizer = wx.BoxSizer(wx.HORIZONTAL)
+        hSizer.Add(item = fpsLabel, proportion = 0, flag = wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, border = 5)
+        hSizer.Add(item = fps, proportion = 0, flag = wx.LEFT | wx.ALIGN_CENTER_VERTICAL, border = 5)
+        
+        boxSizer.Add(item = hSizer, proportion = 0,
+                     flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+                      
+        # save animation
+        self.win['anim']['save'] = {}
+        self.win['anim']['save']['image'] = {}
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Save image sequence")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        vSizer = wx.BoxSizer(wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 10)
+        
+        pwd = os.getcwd()
+        dir = filebrowse.DirBrowseButton(parent = panel, id = wx.ID_ANY,
+                                         labelText = _("Choose a directory:"),
+                                         dialogTitle = _("Choose a directory for images"),
+                                         buttonText = _('Browse'),
+                                         startDirectory = pwd)
+        dir.SetValue(pwd)
+        prefixLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("File prefix:"))
+        prefixCtrl = wx.TextCtrl(parent = panel, id = wx.ID_ANY, size = (100, -1),
+                                 value = UserSettings.Get(group = 'nviz',
+                                                          key = 'animation', subkey = 'prefix'))
+        prefixCtrl.SetToolTipString(_("Generated files names will look like this: prefix_1.ppm, prefix_2.ppm, ..."))
+        fileTypeLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("File format:"))
+        fileTypeCtrl = wx.Choice(parent = panel, id = wx.ID_ANY, choices = ["PPM", "TIF"])
+        
+        save = wx.Button(parent = panel, id = wx.ID_ANY,
+                         label = "Save")
+                         
+        self.win['anim']['save']['image']['dir'] = dir.GetId()
+        self.win['anim']['save']['image']['prefix'] = prefixCtrl.GetId()
+        self.win['anim']['save']['image']['format'] = fileTypeCtrl.GetId()
+        self.win['anim']['save']['image']['confirm'] = save.GetId()
+        
+        boxSizer.Add(item = dir, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 3)
+        
+        gridSizer.Add(item = prefixLabel, pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+        gridSizer.Add(item = prefixCtrl, pos = (0, 1), flag = wx.EXPAND )
+        gridSizer.Add(item = fileTypeLabel, pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+        gridSizer.Add(item = fileTypeCtrl, pos = (1, 1), flag = wx.EXPAND )
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 5)
+        boxSizer.Add(item = save, proportion = 0, flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
+        
+        save.Bind(wx.EVT_BUTTON, self.OnSaveAnimation)
+        
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 
+                      border = 3)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
+        
+    def _createDataPage(self):
+        """!Create data (surface, vector, volume) settings page"""
+
+        self.mainPanelData = ScrolledPanel(parent = self)
+        self.mainPanelData.SetupScrolling(scroll_x = False)
+##        style = fpb.CaptionBarStyle()
+##        style.SetCaptionStyle(fpb.CAPTIONBAR_FILLED_RECTANGLE)
+##        style.SetFirstColour(wx.Color(250,250,250))
+        try:# wxpython <= 2.8.10
+            self.foldpanelData = fpb.FoldPanelBar(parent = self.mainPanelData, id = wx.ID_ANY,
+                                                  style = fpb.FPB_DEFAULT_STYLE,
+                                                  extraStyle = fpb.FPB_SINGLE_FOLD)
+        except:
+            try:# wxpython >= 2.8.11
+                self.foldpanelData = fpb.FoldPanelBar(parent = self.mainPanelData, id = wx.ID_ANY,                               
+                                                      agwStyle = fpb.FPB_SINGLE_FOLD)
+            except: # to be sure
+                self.foldpanelData = fpb.FoldPanelBar(parent = self.mainPanelData, id = wx.ID_ANY,                               
+                                                      style = fpb.FPB_SINGLE_FOLD)
+            
+                     
+        self.foldpanelData.Bind(fpb.EVT_CAPTIONBAR, self.OnPressCaption)
+
+
+        
+        # surface page
+        self.surfacePanel = self.foldpanelData.AddFoldPanel(_("Surface"), collapsed = False)
+        self.foldpanelData.AddFoldPanelWindow(self.surfacePanel, 
+            window = self._createSurfacePage(parent = self.surfacePanel), flags = fpb.FPB_ALIGN_WIDTH)
+        self.EnablePage("surface", enabled = False)
+        
+        # constant page
+        constantPanel = self.foldpanelData.AddFoldPanel(_("Constant surface"), collapsed = True)
+        self.foldpanelData.AddFoldPanelWindow(constantPanel,
+            window = self._createConstantPage(parent = constantPanel), flags = fpb.FPB_ALIGN_WIDTH)
+        self.EnablePage("constant", enabled = False)
+        # vector page
+        vectorPanel = self.foldpanelData.AddFoldPanel(_("Vector"), collapsed = True)
+        self.foldpanelData.AddFoldPanelWindow(vectorPanel, 
+            window = self._createVectorPage(parent = vectorPanel), flags = fpb.FPB_ALIGN_WIDTH)
+        self.EnablePage("vector", enabled = False)
+        
+        # volume page
+        volumePanel = self.foldpanelData.AddFoldPanel(_("Volume"), collapsed = True)
+        self.foldpanelData.AddFoldPanelWindow(volumePanel,
+            window = self._createVolumePage(parent = volumePanel), flags = fpb.FPB_ALIGN_WIDTH)
+        self.EnablePage("volume", enabled = False)
+        
+##        self.foldpanelData.ApplyCaptionStyleAll(style)
+        
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        sizer.Add(self.foldpanelData, proportion = 1, flag = wx.EXPAND)
+        self.mainPanelData.SetSizer(sizer)
+        self.mainPanelData.Layout()
+        self.mainPanelData.Fit()
+        
+        return self.mainPanelData
+        
+        
+    def _createAppearancePage(self):
+        """!Create data (surface, vector, volume) settings page"""
+        self.mainPanelAppear = ScrolledPanel(parent = self)
+        self.mainPanelAppear.SetupScrolling(scroll_x = False)
+        
+        try:# wxpython <= 2.8.10
+            self.foldpanelAppear = fpb.FoldPanelBar(parent = self.mainPanelAppear, id = wx.ID_ANY,
+                                                  style = fpb.FPB_DEFAULT_STYLE,
+                                                  extraStyle = fpb.FPB_SINGLE_FOLD)
+        except:
+            try:# wxpython >= 2.8.11
+                self.foldpanelAppear = fpb.FoldPanelBar(parent = self.mainPanelAppear, id = wx.ID_ANY,                               
+                                                      agwStyle = fpb.FPB_SINGLE_FOLD)
+            except: # to be sure
+                self.foldpanelAppear = fpb.FoldPanelBar(parent = self.mainPanelAppear, id = wx.ID_ANY,                               
+                                                      style = fpb.FPB_SINGLE_FOLD)
+            
+        self.foldpanelAppear.Bind(fpb.EVT_CAPTIONBAR, self.OnPressCaption)
+        # light page
+        lightPanel = self.foldpanelAppear.AddFoldPanel(_("Lighting"), collapsed = False)
+        self.foldpanelAppear.AddFoldPanelWindow(lightPanel, 
+            window = self._createLightPage(parent = lightPanel), flags = fpb.FPB_ALIGN_WIDTH)
+    
+        # fringe page
+        fringePanel = self.foldpanelAppear.AddFoldPanel(_("Fringe"), collapsed = True)
+        self.foldpanelAppear.AddFoldPanelWindow(fringePanel, 
+            window = self._createFringePage(parent = fringePanel), flags = fpb.FPB_ALIGN_WIDTH)
+        
+        self.EnablePage('fringe', False)
+        
+        # decoration page
+        decorationPanel = self.foldpanelAppear.AddFoldPanel(_("Decorations"), collapsed = True)
+        self.foldpanelAppear.AddFoldPanelWindow(decorationPanel, 
+            window = self._createDecorationPage(parent = decorationPanel), flags = fpb.FPB_ALIGN_WIDTH)
+        
+        
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        sizer.Add(self.foldpanelAppear, proportion = 1, flag = wx.EXPAND)
+        self.mainPanelAppear.SetSizer(sizer)
+        self.mainPanelAppear.Layout()
+        self.mainPanelAppear.Fit()
+        return self.mainPanelAppear
+    
+    def _createAnalysisPage(self):
+        """!Create data analysis (cutting planes, ...) page"""
+        self.mainPanelAnalysis = ScrolledPanel(parent = self)
+        self.mainPanelAnalysis.SetupScrolling(scroll_x = False)
+        self.foldpanelAnalysis = fpb.FoldPanelBar(parent = self.mainPanelAnalysis, id = wx.ID_ANY,
+                                                  style = fpb.FPB_SINGLE_FOLD)
+        self.foldpanelAnalysis.Bind(fpb.EVT_CAPTIONBAR, self.OnPressCaption)
+        # cutting planes page
+        cplanePanel = self.foldpanelAnalysis.AddFoldPanel(_("Cutting planes"), collapsed = False)
+        self.foldpanelAnalysis.AddFoldPanelWindow(cplanePanel, 
+            window = self._createCPlanePage(parent = cplanePanel), flags = fpb.FPB_ALIGN_WIDTH)
+        
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        sizer.Add(self.foldpanelAnalysis, proportion = 1, flag = wx.EXPAND)
+        self.mainPanelAnalysis.SetSizer(sizer)
+        self.mainPanelAnalysis.Layout()
+        self.mainPanelAnalysis.Fit()
+        return self.mainPanelAnalysis
+        
+    def _createSurfacePage(self, parent):
+        """!Create view settings page"""
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        self.page['surface'] = { 'id' : 0,
+                                 'notebook' : self.foldpanelData.GetId() }
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        self.win['surface'] = {}
+        
+        # selection
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Raster map")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        rmaps = Select(parent = panel, type = 'raster',
+                       onPopup = self.GselectOnPopup)
+        rmaps.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnSetRaster)
+        self.win['surface']['map'] = rmaps.GetId()
+        desc = wx.StaticText(parent = panel, id = wx.ID_ANY)
+        self.win['surface']['desc'] = desc.GetId()
+        boxSizer.Add(item = rmaps, proportion = 0,
+                     flag = wx.ALL,
+                     border = 3)
+        boxSizer.Add(item = desc, proportion = 0,
+                     flag = wx.ALL,
+                     border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        #
+        # draw
+        #
+        self.win['surface']['draw'] = {}
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Draw")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(3)
+        
+        # mode
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Mode:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        mode = wx.Choice (parent = panel, id = wx.ID_ANY, size = (-1, -1),
+                          choices = [_("coarse"),
+                                     _("fine"),
+                                     _("both")])
+        mode.SetName("selection")
+        mode.Bind(wx.EVT_CHOICE, self.OnSurfaceMode)
+        self.win['surface']['draw']['mode'] = mode.GetId()
+        gridSizer.Add(item = mode, flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
+                      pos = (0, 1),span = (1, 2))
+        
+        # shading
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Shading:")),
+                      pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
+        shade = wx.Choice (parent = panel, id = wx.ID_ANY, size = (-1, -1),
+                           choices = [_("flat"),
+                                      _("gouraud")])
+        shade.SetName("selection")
+        self.win['surface']['draw']['shading'] = shade.GetId()
+        shade.Bind(wx.EVT_CHOICE, self.OnSurfaceMode)
+        gridSizer.Add(item = shade, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (0, 4))
+        
+        # set to all
+        all = wx.Button(panel, id = wx.ID_ANY, label = _("Set to all"))
+        all.SetToolTipString(_("Use draw settings for all loaded surfaces"))
+        all.Bind(wx.EVT_BUTTON, self.OnSurfaceModeAll)
+        gridSizer.Add(item = all, flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
+                      pos = (3, 4))
+        self.win['surface']['all'] = all.GetId()
+        
+        # resolution coarse
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Coarse mode:")),
+                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                        label = _("resolution:")),
+                     pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        resC = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = 6,
+                           min = 1,
+                           max = 100)
+        resC.SetName("value")
+        self.win['surface']['draw']['res-coarse'] = resC.GetId()
+        resC.Bind(wx.EVT_SPINCTRL, self.OnSurfaceResolution)
+        gridSizer.Add(item = resC, pos = (2, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
+        
+        # Coarse style
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("style:")),
+                      pos = (3, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        style = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
+                          choices = [_("wire"),
+                                     _("surface")])
+        style.SetName("selection")
+        self.win['surface']['draw']['style'] = style.GetId()
+        style.Bind(wx.EVT_CHOICE, self.OnSurfaceMode)
+        gridSizer.Add(item = style, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (3, 2))
+        
+        # color
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        color.SetName("colour")
+        color.Bind(csel.EVT_COLOURSELECT, self.OnSurfaceWireColor)
+        color.SetToolTipString(_("Change wire color"))
+        self.win['surface']['draw']['wire-color'] = color.GetId()
+        gridSizer.Add(item = color, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT,
+                      pos = (3, 3))
+        
+        # resolution fine
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Fine mode:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                        label = _("resolution:")),
+                     pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        resF = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = 3,
+                           min = 1,
+                           max = 100)
+        resF.SetName("value")
+        self.win['surface']['draw']['res-fine'] = resF.GetId()
+        resF.Bind(wx.EVT_SPINCTRL, self.OnSurfaceResolution)
+        gridSizer.Add(item = resF, pos = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        #
+        # surface attributes
+        #
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Surface attributes")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(2)
+        
+        # type 
+        self.win['surface']['attr'] = {}
+        row = 0
+        for code, attrb in (('color', _("Color")),
+                           ('mask', _("Mask")),
+                           ('transp', _("Transparency")),
+                           ('shine', _("Shininess"))):
+            self.win['surface'][code] = {} 
+            gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                             label = attrb + ':'),
+                          pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+            use = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
+                             choices = [_("map")])
+            
+            if code not in ('color', 'shine'):
+                use.Insert(item = _("unset"), pos = 0)
+                self.win['surface'][code]['required'] = False
+            else:
+                self.win['surface'][code]['required'] = True
+            if code != 'mask':
+                use.Append(item = _('constant'))
+            self.win['surface'][code]['use'] = use.GetId()
+            use.Bind(wx.EVT_CHOICE, self.OnMapObjUse)
+            gridSizer.Add(item = use, flag = wx.ALIGN_CENTER_VERTICAL,
+                          pos = (row, 1))
+            
+            map = Select(parent = panel, id = wx.ID_ANY,
+                         # size = globalvar.DIALOG_GSELECT_SIZE,
+                         size = (-1, -1),
+                         type = "raster")
+            self.win['surface'][code]['map'] = map.GetId() - 1 # FIXME
+            map.Bind(wx.EVT_TEXT, self.OnSurfaceMap)
+            gridSizer.Add(item = map, flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
+                          pos = (row, 2))
+            
+            if code == 'color':
+                color = UserSettings.Get(group = 'nviz', key = 'surface', subkey = ['color', 'value'])
+                value = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                          colour = color,
+                                          size = globalvar.DIALOG_COLOR_SIZE)
+                value.Bind(csel.EVT_COLOURSELECT, self.OnSurfaceMap)
+            elif code == 'mask':
+                value = None
+            else:
+                value = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                                    initial = 0)
+                value.SetRange(minVal = 0, maxVal = 100)
+                value.Bind(wx.EVT_TEXT, self.OnSurfaceMap)
+            
+            if value:
+                self.win['surface'][code]['const'] = value.GetId()
+                value.Enable(False)
+                gridSizer.Add(item = value, flag = wx.ALIGN_CENTER_VERTICAL,
+                              pos = (row, 3))
+            else:
+                self.win['surface'][code]['const'] = None
+            
+            self.SetMapObjUseMap(nvizType = 'surface',
+                                 attrb = code) # -> enable map / disable constant
+                
+            row += 1
+        boxSizer.Add(item = gridSizer, proportion = 0,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 3)
+        #
+        # position
+        #
+        self.win['surface']['position'] = {}
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Position")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(3)
+        
+        # position
+        self._createControl(panel, data = self.win['surface'], name = 'position',
+                            range = (-10000, 10000), floatSlider = True,
+                            bind = (self.OnSurfacePosition, self.OnSurfacePositionChanged, self.OnSurfacePositionText))
+        
+        axis = wx.Choice (parent = panel, id = wx.ID_ANY, size = (75, -1),
+                          choices = ["X",
+                                     "Y",
+                                     "Z"])
+                                    
+        reset = wx.Button(panel, id = wx.ID_ANY, label = _("Reset"))
+        reset.SetToolTipString(_("Reset to default position"))
+        reset.Bind(wx.EVT_BUTTON, self.OnResetSurfacePosition)
+        self.win['surface']['position']['reset'] = reset.GetId()
+        
+        self.win['surface']['position']['axis'] = axis.GetId()
+        axis.SetSelection(0)
+        axis.Bind(wx.EVT_CHOICE, self.OnSurfaceAxis)
+        
+        pslide = self.FindWindowById(self.win['surface']['position']['slider'])
+        ptext = self.FindWindowById(self.win['surface']['position']['text'])
+        ptext.SetValue('0')
+        
+        gridSizer.Add(item = axis, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
+        gridSizer.Add(item = pslide, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 1))
+        gridSizer.Add(item = ptext, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 2))
+        gridSizer.Add(item = reset, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, pos = (0, 3))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        box.SetSizer(boxSizer)
+        box.Layout()
+        
+        pageSizer.Add(item = boxSizer, proportion = 1,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        #
+        # mask
+        #
+##        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+##                            label = " %s " % (_("Mask")))
+##        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+##        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+##        
+##        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+##                                         label = _("Mask zeros:")),
+##                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+##        
+##        elev = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+##                           label = _("by elevation"))
+##        elev.Enable(False) # TODO: not implemented yet
+##        gridSizer.Add(item = elev, pos = (0, 1))
+##        
+##        color = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+##                           label = _("by color"))
+##        color.Enable(False) # TODO: not implemented yet
+##        gridSizer.Add(item = color, pos = (0, 2))
+##        
+##        boxSizer.Add(item = gridSizer, proportion = 1,
+##                  flag = wx.ALL | wx.EXPAND, border = 3)
+##        pageSizer.Add(item = boxSizer, proportion = 0,
+##                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+##                      border = 3)
+        
+        
+        panel.SetSizer(pageSizer)
+
+        panel.Layout()
+        panel.Fit()
+        
+        return panel
+    def _createCPlanePage(self, parent):
+        """!Create cutting planes page"""  
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        self.page['cplane'] = { 'id' : 4, 
+                                'notebook' : self.foldpanelData.GetId() }
+        self.win['cplane'] = {}
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Cutting planes")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        horSizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        # planes
+        horSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Active cutting plane:")),
+                     flag = wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+        choice = wx.Choice(parent = panel, id = wx.ID_ANY, choices = [])
+        self.win['cplane']['planes'] = choice.GetId()
+        choice.Bind(wx.EVT_CHOICE, self.OnCPlaneSelection)
+        horSizer.Add(item = choice, flag = wx.ALL, border = 5)
+        
+        # shading
+        horSizer.Add(item = wx.Size(-1, -1), proportion = 1)
+        horSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Shading:")),
+                     flag = wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+        choices = [_("clear"),
+                   _("top color"),
+                   _("bottom color"),
+                   _("blend"),
+                   _("shaded")]
+        choice = wx.Choice(parent = panel, id = wx.ID_ANY, choices = choices)
+        self.win['cplane']['shading'] = choice.GetId()
+        choice.Bind(wx.EVT_CHOICE, self.OnCPlaneShading)
+        horSizer.Add(item = choice, flag = wx.ALL, border = 5)
+        boxSizer.Add(item = horSizer, flag = wx.EXPAND)
+        
+        gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        
+        # cutting plane horizontal x position
+        self.win['cplane']['position'] = {}
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Horizontal X:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        self._createControl(panel, data = self.win['cplane']['position'], name = 'x', size = 250,
+                            range = (-1000, 1000), sliderHor = True, floatSlider = True,
+                            bind = (self.OnCPlaneChanging, self.OnCPlaneChangeDone, self.OnCPlaneChangeText))
+        self.FindWindowById(self.win['cplane']['position']['x']['slider']).SetValue(0)
+        self.FindWindowById(self.win['cplane']['position']['x']['text']).SetValue(0)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['position']['x']['slider']),
+                      pos = (0, 1),  flag = wx.EXPAND|wx.ALIGN_RIGHT)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['position']['x']['text']),
+                      pos = (0, 2),
+                      flag = wx.ALIGN_CENTER)   
+        
+        # cutting plane horizontal y position
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Horizontal Y:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        self._createControl(panel, data = self.win['cplane']['position'], name = 'y', size = 250,
+                            range = (-1000, 1000), sliderHor = True, floatSlider = True,
+                            bind = (self.OnCPlaneChanging, self.OnCPlaneChangeDone, self.OnCPlaneChangeText))
+        self.FindWindowById(self.win['cplane']['position']['y']['slider']).SetValue(0)
+        self.FindWindowById(self.win['cplane']['position']['y']['text']).SetValue(0)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['position']['y']['slider']),
+                      pos = (1, 1),  flag = wx.EXPAND|wx.ALIGN_RIGHT)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['position']['y']['text']),
+                      pos = (1, 2),
+                      flag = wx.ALIGN_CENTER)                         
+        
+        # cutting plane rotation
+        self.win['cplane']['rotation'] = {}
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Rotation:")),
+                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        self._createControl(panel, data = self.win['cplane']['rotation'], name = 'rot', size = 250,
+                            range = (0, 360), sliderHor = True,
+                            bind = (self.OnCPlaneChanging, self.OnCPlaneChangeDone, self.OnCPlaneChangeText))
+        self.FindWindowById(self.win['cplane']['rotation']['rot']['slider']).SetValue(0)
+        self.FindWindowById(self.win['cplane']['rotation']['rot']['text']).SetValue(0)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['rotation']['rot']['slider']),
+                      pos = (2, 1),  flag = wx.EXPAND|wx.ALIGN_RIGHT)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['rotation']['rot']['text']),
+                      pos = (2, 2),
+                      flag = wx.ALIGN_CENTER)
+
+        # cutting plane tilt        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Tilt:")),
+                      pos = (3, 0), flag = wx.ALIGN_CENTER_VERTICAL)                    
+        self._createControl(panel, data = self.win['cplane']['rotation'], name = 'tilt', size = 250,
+                            range = (0, 360), sliderHor = True,
+                            bind = (self.OnCPlaneChanging, self.OnCPlaneChangeDone, self.OnCPlaneChangeText))
+        self.FindWindowById(self.win['cplane']['rotation']['tilt']['slider']).SetValue(0)
+        self.FindWindowById(self.win['cplane']['rotation']['tilt']['text']).SetValue(0)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['rotation']['tilt']['slider']),
+                      pos = (3, 1),  flag = wx.EXPAND|wx.ALIGN_RIGHT)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['rotation']['tilt']['text']),
+                      pos = (3, 2),
+                      flag = wx.ALIGN_CENTER)          
+        
+        # cutting pland height
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Height:")),
+                      pos = (4, 0), flag = wx.ALIGN_CENTER_VERTICAL)        
+        self._createControl(panel, data = self.win['cplane']['position'], name = 'z', size = 250,
+                            range = (-1000, 1000), sliderHor = True,
+                            bind = (self.OnCPlaneChanging, self.OnCPlaneChangeDone, self.OnCPlaneChangeText))
+        self.FindWindowById(self.win['cplane']['position']['z']['slider']).SetValue(0)
+        self.FindWindowById(self.win['cplane']['position']['z']['text']).SetValue(0)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['position']['z']['slider']),
+                      pos = (4, 1),  flag = wx.EXPAND|wx.ALIGN_RIGHT)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['position']['z']['text']),
+                      pos = (4, 2),
+                      flag = wx.ALIGN_CENTER)
+        
+        boxSizer.Add(gridSizer, proportion = 0, flag = wx.EXPAND|wx.ALL, border = 5)
+                    
+        horSizer = wx.BoxSizer(wx.HORIZONTAL)
+        horSizer.Add(item = wx.Size(-1, -1), proportion = 1, flag = wx.ALL, border = 5)  
+        # reset
+        reset = wx.Button(parent = panel, id = wx.ID_ANY, label = _("Reset"))
+        self.win['cplane']['reset'] = reset.GetId()
+        reset.Bind(wx.EVT_BUTTON, self.OnCPlaneReset)
+        horSizer.Add(item = reset, flag = wx.ALL, border = 5)
+        boxSizer.Add(horSizer, proportion = 0, flag = wx.EXPAND)            
+        
+        
+        pageSizer.Add(boxSizer, proportion = 0, flag = wx.EXPAND)
+        
+        panel.SetSizer(pageSizer)
+        panel.Fit()    
+        
+        return panel
+        
+    def _createConstantPage(self, parent):
+        """!Create constant page"""
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        self.page['constant'] = { 'id' : 1, 
+                                'notebook' : self.foldpanelData.GetId() }
+        self.win['constant'] = {}
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Constant surface")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        horsizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        surface = wx.ComboBox(parent = panel, id = wx.ID_ANY, 
+                              style = wx.CB_SIMPLE | wx.CB_READONLY,
+                              choices = [])
+        self.win['constant']['surface'] = surface.GetId()
+        surface.Bind(wx.EVT_COMBOBOX, self.OnConstantSelection)
+        horsizer.Add(surface, proportion = 1, flag = wx.EXPAND|wx.RIGHT, border = 20)
+
+        addNew = wx.Button(panel, id = wx.ID_ANY, label = _("New"))
+        addNew.Bind(wx.EVT_BUTTON, self.OnNewConstant)
+        self.win['constant']['new'] = addNew.GetId()
+
+        delete = wx.Button(panel, id = wx.ID_ANY, label = _("Delete"))
+        delete.Bind(wx.EVT_BUTTON, self.OnDeleteConstant)
+        self.win['constant']['delete'] = delete.GetId()
+        
+        horsizer.Add(item = addNew, proportion = 0, flag = wx.RIGHT|wx.LEFT, border = 3)
+        horsizer.Add(item = delete, proportion = 0, flag = wx.RIGHT|wx.LEFT, border = 3)
+    
+        boxSizer.Add(item = horsizer, proportion = 0, flag = wx.ALL|wx.EXPAND,
+                      border = 5)
+        
+        gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        # fine resolution
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Fine resolution:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        resF = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = 3,
+                           min = 1,
+                           max = 100)
+        resF.SetName("value")
+        self.win['constant']['resolution'] = resF.GetId()
+        resF.Bind(wx.EVT_SPINCTRL, self.OnSetConstantProp)
+        gridSizer.Add(item = resF, pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
+        # value 
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Value:")), pos = (1, 0),
+                                         flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        value = wx.SpinCtrl(panel, id = wx.ID_ANY,
+                                  min = -1e9, max = 1e9,
+                                  size = (65, -1))
+        self.win['constant']['value'] = value.GetId()
+        value.Bind(wx.EVT_SPINCTRL, self.OnSetConstantProp)
+        gridSizer.Add(item = value, pos = (1, 1))
+        
+        # transparency 
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Transparency:")), pos = (2, 0),
+                                         flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        transp = wx.SpinCtrl(panel, id = wx.ID_ANY,
+                                  min = 0, max = 100,
+                                  size = (65, -1))
+        self.win['constant']['transp'] = transp.GetId()
+        transp.Bind(wx.EVT_SPINCTRL, self.OnSetConstantProp)
+        gridSizer.Add(item = transp, pos = (2, 1))
+        
+        # color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Color:")), pos = (3, 0),
+                                         flag = wx.ALIGN_CENTER_VERTICAL)
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = (0,0,0),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        self.win['constant']['color'] = color.GetId()
+        color.Bind(csel.EVT_COLOURSELECT, self.OnSetConstantProp)
+        gridSizer.Add(item = color, pos = (3, 1))
+        boxSizer.Add(item = gridSizer, proportion = 0, flag = wx.ALL,
+                      border = 5)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 3)
+        
+        panel.SetSizer(pageSizer)
+        panel.Fit()    
+        
+        return panel
+        
+    def _createVectorPage(self, parent):
+        """!Create view settings page"""
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        self.page['vector'] = { 'id' : 2,
+                                'notebook' : self.foldpanelData.GetId() }
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        self.win['vector'] = {}
+        
+        # selection
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Vector map")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        vmaps = Select(parent = panel, type = 'vector',
+                       onPopup = self.GselectOnPopup)
+        vmaps.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnSetVector)
+        self.win['vector']['map'] = vmaps.GetId()
+        desc = wx.StaticText(parent = panel, id = wx.ID_ANY)
+        self.win['vector']['desc'] = desc.GetId()
+        boxSizer.Add(item = vmaps, proportion = 0,
+                     flag = wx.ALL,
+                     border = 3)
+        boxSizer.Add(item = desc, proportion = 0,
+                     flag = wx.ALL,
+                     border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        #
+        # vector lines
+        #
+        self.win['vector']['lines'] = {}
+        
+        showLines = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                label = _("Show vector lines"))
+        showLines.SetValue(True)
+        
+        self.win['vector']['lines']['show'] = showLines.GetId()
+        showLines.Bind(wx.EVT_CHECKBOX, self.OnVectorShow)
+        
+        pageSizer.Add(item = showLines, proportion = 0,
+                      flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Vector lines")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        gridSizer.AddGrowableCol(5)
+        
+        # width
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Line:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("width:")),
+                      pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL | 
+                      wx.ALIGN_RIGHT)
+        
+        width = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                            initial = 1,
+                            min = 0,
+                            max = 100)
+        width.SetValue(1)
+        self.win['vector']['lines']['width'] = width.GetId()
+        width.Bind(wx.EVT_SPINCTRL, self.OnVectorLines)
+        gridSizer.Add(item = width, pos = (0, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+        
+        # color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("color:")),
+                      pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALIGN_RIGHT)
+        
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = (0,0,0),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        self.win['vector']['lines']['color'] = color.GetId()
+        color.Bind(csel.EVT_COLOURSELECT, self.OnVectorLines)
+
+        gridSizer.Add(item = color, pos = (0, 4), flag = wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALIGN_LEFT)
+        
+        # display
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Display")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALIGN_LEFT)
+        
+        display = wx.Choice (parent = panel, id = wx.ID_ANY, size = (-1, -1),
+                             choices = [_("on surface(s):"),
+                                        _("flat")])
+        self.win['vector']['lines']['flat'] = display.GetId()
+        display.Bind(wx.EVT_CHOICE, self.OnVectorDisplay)
+        
+        gridSizer.Add(item = display, flag = wx.ALIGN_CENTER_VERTICAL | 
+                      wx.ALIGN_LEFT|wx.EXPAND, pos = (1, 1), span = (1,4))
+        
+        # height
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Height above surface:")),
+                      pos = (2, 5), flag = wx.ALIGN_BOTTOM|wx.EXPAND)
+        
+        surface = wx.CheckListBox(parent = panel, id = wx.ID_ANY, size = (-1, 60),
+                                  choices = [], style = wx.LB_NEEDED_SB)
+        surface.Bind(wx.EVT_CHECKLISTBOX, self.OnVectorSurface)
+        
+        self.win['vector']['lines']['surface'] = surface.GetId()
+        gridSizer.Add(item = surface, 
+                      pos = (2, 0), span = (3, 5),
+                      flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
+        
+        self._createControl(panel, data = self.win['vector']['lines'], name = 'height', size = -1,
+                            range = (0, 500), sliderHor = True,
+                            bind = (self.OnVectorHeight, self.OnVectorHeightFull, self.OnVectorHeightText))
+        self.FindWindowById(self.win['vector']['lines']['height']['slider']).SetValue(0)
+        self.FindWindowById(self.win['vector']['lines']['height']['text']).SetValue(0)
+        gridSizer.Add(item = self.FindWindowById(self.win['vector']['lines']['height']['slider']),
+                      pos = (3, 5),  flag = wx.EXPAND|wx.ALIGN_RIGHT)
+        gridSizer.Add(item = self.FindWindowById(self.win['vector']['lines']['height']['text']),
+                      pos = (4, 5),
+                      flag = wx.ALIGN_CENTER)
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        #
+        # vector points
+        #
+        self.win['vector']['points'] = {}
+        
+        showPoints = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                 label = _("Show vector points"))
+        showPoints.SetValue(True)
+        self.win['vector']['points']['show'] = showPoints.GetId()
+        showPoints.Bind(wx.EVT_CHECKBOX, self.OnVectorShow)
+        
+        pageSizer.Add(item = showPoints, proportion = 0,
+                      flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Vector points")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        vertSizer = wx.BoxSizer(wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        gridSizer.AddGrowableCol(0)
+        gridSizer.AddGrowableCol(2)
+        gridSizer.AddGrowableCol(4)
+        gridSizer.AddGrowableCol(6)
+        
+        # icon size
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Icon:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("size:")),
+                      pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALIGN_RIGHT)
+        
+        isize = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                            initial = 1,
+                            min = 1,
+                            max = 1e6)
+        isize.SetName('value')
+        isize.SetValue(100)
+        self.win['vector']['points']['size'] = isize.GetId()
+        isize.Bind(wx.EVT_SPINCTRL, self.OnVectorPoints)
+        isize.Bind(wx.EVT_TEXT, self.OnVectorPoints)
+        gridSizer.Add(item = isize, pos = (0, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+        
+        # icon color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("color:")),
+                      pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALIGN_RIGHT)
+        icolor = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        icolor.SetName("color")
+        icolor.SetColour((0,0,255))
+        self.win['vector']['points']['color'] = icolor.GetId()
+        icolor.Bind(csel.EVT_COLOURSELECT, self.OnVectorPoints)
+        gridSizer.Add(item = icolor, flag = wx.ALIGN_CENTER_VERTICAL | 
+                      wx.ALIGN_LEFT,
+                      pos = (0, 4))
+
+        # icon width - seems to do nothing
+##        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+##                                           label = _("width")),
+##                      pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL |
+##                      wx.ALIGN_RIGHT)
+##        
+##        iwidth = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+##                             initial = 1,
+##                             min = 1,
+##                             max = 1e6)
+##        iwidth.SetName('value')
+##        iwidth.SetValue(100)
+##        self.win['vector']['points']['width'] = iwidth.GetId()
+##        iwidth.Bind(wx.EVT_SPINCTRL, self.OnVectorPoints)
+##        iwidth.Bind(wx.EVT_TEXT, self.OnVectorPoints)
+##        gridSizer.Add(item = iwidth, pos = (1, 2),
+##                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+        # icon symbol
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("symbol:")),
+                      pos = (0, 5), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        isym = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
+                          choices = UserSettings.Get(group = 'nviz', key = 'vector',
+                                                   subkey = ['points', 'marker'], internal = True))
+        isym.SetName("selection")
+        self.win['vector']['points']['marker'] = isym.GetId()
+        isym.Bind(wx.EVT_CHOICE, self.OnVectorPoints)
+        gridSizer.Add(item = isym, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT,
+                      pos = (0, 6))
+        
+        vertSizer.Add(gridSizer, proportion = 0, flag = wx.EXPAND, border = 0)
+        # high
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        gridSizer.AddGrowableCol(1)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Display on surface(s):")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Height above surface:")),
+                      pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        surface = wx.CheckListBox(parent = panel, id = wx.ID_ANY, size = (-1, 60),
+                                  choices = [], style = wx.LB_NEEDED_SB)
+        surface.Bind(wx.EVT_CHECKLISTBOX, self.OnVectorSurface)
+        self.win['vector']['points']['surface'] = surface.GetId()
+        gridSizer.Add(item = surface, 
+                      pos = (1, 0), span = (3, 1),
+                      flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
+        
+        self._createControl(panel, data = self.win['vector']['points'], name = 'height', size = -1,
+                            range = (0, 500),
+                            bind = (self.OnVectorHeight, self.OnVectorHeightFull, self.OnVectorHeightText))
+        
+        self.FindWindowById(self.win['vector']['points']['height']['slider']).SetValue(0)
+        self.FindWindowById(self.win['vector']['points']['height']['text']).SetValue(0)
+        
+        gridSizer.Add(item = self.FindWindowById(self.win['vector']['points']['height']['slider']),
+                      pos = (2, 1),flag = wx.EXPAND|wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = self.FindWindowById(self.win['vector']['points']['height']['text']),
+                      pos = (3, 1),
+                      flag = wx.ALIGN_CENTER)
+                    
+        vertSizer.Add(gridSizer, proportion = 0, flag = wx.EXPAND, border = 0)
+        boxSizer.Add(item = vertSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        panel.SetSizer(pageSizer)
+        panel.Fit()
+
+        return panel
+
+    def GselectOnPopup(self, ltype, exclude = False):
+        """Update gselect.Select() items"""
+        maps = list()
+        for layer in self.mapWindow.Map.GetListOfLayers(l_type = ltype, l_active = True):
+            maps.append(layer.GetName())
+        return maps, exclude
+    
+    def _createVolumePage(self, parent):
+        """!Create view settings page"""
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        self.page['volume'] = { 'id' : 3,
+                                'notebook' : self.foldpanelData.GetId() }
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        self.win['volume'] = {}
+        
+        # selection
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("3D raster map")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        rmaps = Select(parent = panel, type = '3d-raster',
+                       onPopup = self.GselectOnPopup)
+        rmaps.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnSetRaster3D)
+        self.win['volume']['map'] = rmaps.GetId()
+        desc = wx.StaticText(parent = panel, id = wx.ID_ANY)
+        self.win['volume']['desc'] = desc.GetId()
+        boxSizer.Add(item = rmaps, proportion = 0,
+                     flag = wx.ALL,
+                     border = 3)
+        boxSizer.Add(item = desc, proportion = 0,
+                     flag = wx.ALL,
+                     border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+                
+        #
+        # draw
+        #
+        self.win['volume']['draw'] = {}
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Draw")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+##        gridSizer.AddGrowableCol(4)
+        
+        # mode
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Mode:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        mode = wx.Choice (parent = panel, id = wx.ID_ANY, size = (-1, -1),
+                          choices = [_("isosurfaces"),
+                                     _("slices")])
+        mode.SetSelection(0)
+        mode.SetName("selection")
+        mode.Bind(wx.EVT_CHOICE, self.OnVolumeMode)
+        self.win['volume']['draw']['mode'] = mode.GetId()
+        gridSizer.Add(item = mode, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (0, 1))
+        
+        # shading
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Shading:")),
+                      pos = (0, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        shade = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
+                           choices = [_("flat"),
+                                      _("gouraud")])
+        shade.SetName("selection")
+        self.win['volume']['draw']['shading'] = shade.GetId()
+        shade.Bind(wx.EVT_CHOICE, self.OnVolumeDrawMode)
+        gridSizer.Add(item = shade, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (0, 3))
+        
+        # resolution (mode)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Resolution:")),
+                      pos = (0, 4), flag = wx.ALIGN_CENTER_VERTICAL)
+        resol = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                            initial = 1,
+                            min = 1,
+                            max = 100)
+        resol.SetName("value")
+        self.win['volume']['draw']['resolution'] = resol.GetId()
+        resol.Bind(wx.EVT_SPINCTRL, self.OnVolumeResolution)
+        resol.Bind(wx.EVT_TEXT, self.OnVolumeResolution)
+        gridSizer.Add(item = resol, pos = (0, 5))
+        
+        boxSizer.Add(item = gridSizer, proportion = 0,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 3)
+        
+        #
+        # manage isosurfaces
+        #
+        box = wx.StaticBox(parent = panel, id = wx.ID_ANY, 
+                           label = " %s " % (_("List of isosurfaces")))
+        box.SetName('listStaticBox')
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
+        # list
+        isolevel = wx.CheckListBox(parent = panel, id = wx.ID_ANY,
+                                   size = (300, 150))
+        self.Bind(wx.EVT_CHECKLISTBOX, self.OnVolumeCheck, isolevel)
+        self.Bind(wx.EVT_LISTBOX, self.OnVolumeSelect, isolevel)
+        
+        self.win['volume']['isosurfs'] = isolevel.GetId()
+        self.win['volume']['slices'] = isolevel.GetId()
+        gridSizer.Add(item = isolevel, pos = (0, 0), span = (4, 1))
+        
+        # buttons (add, delete, move up, move down)
+        btnAdd = wx.Button(parent = panel, id = wx.ID_ADD)
+        self.win['volume']['btnAdd'] = btnAdd.GetId()
+        btnAdd.Bind(wx.EVT_BUTTON, self.OnVolumeAdd)
+        gridSizer.Add(item = btnAdd,
+                      pos = (0, 1))
+        btnDelete = wx.Button(parent = panel, id = wx.ID_DELETE)
+        self.win['volume']['btnDelete'] = btnDelete.GetId()
+        btnDelete.Bind(wx.EVT_BUTTON, self.OnVolumeDelete)
+        btnDelete.Enable(False)
+        gridSizer.Add(item = btnDelete,
+                      pos = (1, 1))
+        btnMoveUp = wx.Button(parent = panel, id = wx.ID_UP)
+        self.win['volume']['btnMoveUp'] = btnMoveUp.GetId()
+        btnMoveUp.Bind(wx.EVT_BUTTON, self.OnVolumeMoveUp)
+        btnMoveUp.Enable(False)
+        gridSizer.Add(item = btnMoveUp,
+                      pos = (2, 1))
+        btnMoveDown = wx.Button(parent = panel, id = wx.ID_DOWN)
+        self.win['volume']['btnMoveDown'] = btnMoveDown.GetId()
+        btnMoveDown.Bind(wx.EVT_BUTTON, self.OnVolumeMoveDown)
+        btnMoveDown.Enable(False)
+        gridSizer.Add(item = btnMoveDown,
+                      pos = (3, 1))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        # isosurface/slice 
+        sizer = wx.BoxSizer()
+        self.isoPanel = self._createIsosurfacePanel(panel)
+        self.slicePanel = self._createSlicePanel(panel)
+        sizer.Add(self.isoPanel, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 0)
+        sizer.Add(self.slicePanel, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 0)
+        sizer.Hide(self.slicePanel)
+        pageSizer.Add(item = sizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 3)
+        #
+        # position
+        #
+        self.win['volume']['position'] = {}
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Position")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(3)
+        
+        # position
+        self._createControl(panel, data = self.win['volume'], name = 'position',
+                            range = (-10000, 10000), floatSlider = True,
+                            bind = (self.OnVolumePosition, self.OnVolumePositionChanged, self.OnVolumePositionText))
+        
+        axis = wx.Choice (parent = panel, id = wx.ID_ANY, size = (75, -1),
+                          choices = ["X",
+                                     "Y",
+                                     "Z"])
+                                    
+        reset = wx.Button(panel, id = wx.ID_ANY, label = _("Reset"))
+        reset.SetToolTipString(_("Reset to default position"))
+        reset.Bind(wx.EVT_BUTTON, self.OnResetVolumePosition)
+        self.win['volume']['position']['reset'] = reset.GetId()
+        
+        self.win['volume']['position']['axis'] = axis.GetId()
+        axis.SetSelection(0)
+        axis.Bind(wx.EVT_CHOICE, self.OnVolumeAxis)
+        
+        pslide = self.FindWindowById(self.win['volume']['position']['slider'])
+        ptext = self.FindWindowById(self.win['volume']['position']['text'])
+        ptext.SetValue('0')
+        
+        gridSizer.Add(item = axis, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
+        gridSizer.Add(item = pslide, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 1))
+        gridSizer.Add(item = ptext, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 2))
+        gridSizer.Add(item = reset, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, pos = (0, 3))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        panel.SetSizer(pageSizer)
+        panel.Fit()
+        
+        return panel
+       
+        
+    def _createLightPage(self, parent):
+        """!Create light page"""
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        
+        self.page['light'] = { 'id' : 0, 
+                               'notebook' : self.foldpanelAppear.GetId() }
+        self.win['light'] = {}
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        show = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                           label = _("Show light model"))
+        show.Bind(wx.EVT_CHECKBOX, self.OnShowLightModel)
+        show.SetValue(True)
+        self._display.showLight = True
+        pageSizer.Add(item = show, proportion = 0,
+                      flag = wx.ALL, border = 3)
+##        surface = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+##                              label = _("Follow source viewpoint"))
+##        pageSizer.Add(item = surface, proportion = 0,
+##                      flag = wx.ALL, border = 3)
+        
+        # position
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Light source position")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        posSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
+        self._createCompass(panel = panel, sizer = posSizer, type = 'light')
+        
+        pos = LightPositionWindow(panel, id = wx.ID_ANY, size = (175, 175),
+                                  mapwindow = self.mapWindow)
+        self.win['light']['position'] = pos.GetId()
+        posSizer.Add(item = pos,
+                     pos = (1, 1), flag = wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = posSizer, pos = (0, 0))
+        
+        # height
+        self._createControl(panel, data = self.win['light'], name = 'z', sliderHor = False,
+                            range = (0, 100),
+                            bind = (self.OnLightChange, self.OnLightChanged, self.OnLightChange))
+        
+        heightSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        heightSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Height:")),
+                      pos = (0, 0), flag = wx.ALIGN_LEFT, span = (1, 2))
+        heightSizer.Add(item = self.FindWindowById(self.win['light']['z']['slider']),
+                        flag = wx.ALIGN_RIGHT, pos = (1, 0))
+        heightSizer.Add(item = self.FindWindowById(self.win['light']['z']['text']),
+                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT | wx.TOP |
+                        wx.BOTTOM | wx.RIGHT, pos = (1, 1))
+        
+        gridSizer.Add(item = heightSizer, pos = (0, 2), flag = wx.ALIGN_RIGHT)
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 2)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        # position
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Light color and intensity")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+
+        gridSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Color:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = UserSettings.Get(group = 'nviz', key = 'light',
+                                                            subkey = 'color'),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        self.win['light']['color'] = color.GetId()
+        color.Bind(csel.EVT_COLOURSELECT, self.OnLightColor)
+        gridSizer.Add(item = color, pos = (0, 2))
+
+        gridSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Brightness:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        self._createControl(panel, data = self.win['light'], name = 'bright', size = 300,
+                            range = (0, 100),
+                            bind = (self.OnLightValue, self.OnLightChanged, self.OnLightValue))
+        gridSizer.Add(item = self.FindWindowById(self.win['light']['bright']['slider']),
+                      pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = self.FindWindowById(self.win['light']['bright']['text']),
+                      pos = (1, 2),
+                      flag = wx.ALIGN_CENTER)
+        gridSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Ambient:")),
+                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        self._createControl(panel, data = self.win['light'], name = 'ambient', size = 300,
+                            range = (0, 100),
+                            bind = (self.OnLightValue, self.OnLightChanged, self.OnLightValue))
+        gridSizer.Add(item = self.FindWindowById(self.win['light']['ambient']['slider']),
+                      pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = self.FindWindowById(self.win['light']['ambient']['text']),
+                      pos = (2, 2),
+                      flag = wx.ALIGN_CENTER)
+
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 2)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        # reset = wx.Button(panel, id = wx.ID_ANY, label = _("Reset"))
+        # reset.SetToolTipString(_("Reset to default view"))
+        # # self.win['reset'] = reset.GetId()
+        # reset.Bind(wx.EVT_BUTTON, self.OnResetView)
+        
+        # viewSizer.Add(item = reset, proportion = 1,
+        #               flag = wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT,
+        #               border = 5)
+        
+        # gridSizer.AddGrowableCol(3)
+        # gridSizer.Add(item = viewSizer, pos = (4, 0), span = (1, 2),
+        #               flag = wx.EXPAND)
+        
+        panel.SetSizer(pageSizer)
+        panel.Layout()
+        panel.Fit()
+        
+        return panel
+
+    def _createFringePage(self, parent):
+        """!Create fringe page"""
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        
+        self.page['fringe'] = { 'id' : 1,
+                                'notebook' : self.foldpanelAppear.GetId() }
+        self.win['fringe'] = {}
+
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        # selection
+        rbox = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                             label = " %s " % (_("Surface")))
+        rboxSizer = wx.StaticBoxSizer(rbox, wx.VERTICAL)
+        rmaps = Select(parent = panel, type = 'raster',
+                       onPopup = self.GselectOnPopup)
+        rmaps.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnSetSurface)
+        self.win['fringe']['map'] = rmaps.GetId()
+        rboxSizer.Add(item = rmaps, proportion = 0,
+                      flag = wx.ALL,
+                      border = 3)
+        pageSizer.Add(item = rboxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        ebox = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                             label = " %s " % (_("Edges with fringe")))
+        eboxSizer = wx.StaticBoxSizer(ebox, wx.HORIZONTAL)
+        for edge in [(_("N && W"), "nw"),
+                     (_("N && E"), "ne"),
+                     (_("S && W"), "sw"),
+                     (_("S && E"), "se")]:
+            chkbox = wx.CheckBox(parent = panel,
+                                 label = edge[0],
+                                 name = edge[1])
+            self.win['fringe'][edge[1]] = chkbox.GetId()
+            eboxSizer.Add(item = chkbox, proportion = 0,
+                         flag = wx.ADJUST_MINSIZE | wx.LEFT | wx.RIGHT, border = 5)
+            chkbox.Bind(wx.EVT_CHECKBOX, self.OnFringe)
+        
+        pageSizer.Add(item = eboxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+
+        sbox = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                             label = " %s " % (_("Settings")))
+        sboxSizer = wx.StaticBoxSizer(sbox, wx.HORIZONTAL)
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        
+        # elevation
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Elevation of fringe from bottom:")),
+                      pos = (0, 0),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        spin = wx.SpinCtrl(parent = panel, id = wx.ID_ANY,
+                           size = (65, -1), min = -1e6, max = 1e6)
+        spin.SetValue(UserSettings.Get(group = 'nviz', key = 'fringe', subkey = 'elev'))
+        spin.Bind(wx.EVT_SPINCTRL, self.OnFringe)
+        self.win['fringe']['elev'] = spin.GetId()
+        gridSizer.Add(item = spin, pos = (0, 1))
+        
+        # color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Color:")),
+                      pos = (1, 0),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        color = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        color.SetColour(UserSettings.Get(group = 'nviz', key = 'fringe',
+                                         subkey = 'color'))
+        color.Bind(csel.EVT_COLOURSELECT, self.OnFringe)
+        self.win['fringe']['color'] = color.GetId()
+        gridSizer.Add(item = color, pos = (1, 1))
+        
+        sboxSizer.Add(item = gridSizer, proportion = 1,
+                      flag = wx.ALL | wx.EXPAND, border = 3)
+        pageSizer.Add(item = sboxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        panel.SetSizer(pageSizer)
+        panel.Layout()
+        panel.Fit()
+
+        return panel
+    
+    def _createDecorationPage(self, parent):
+        """!Create decoration (north arrow, scalebar, legend) page"""
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        
+        self.page['decoration'] = { 'id' : 2,
+                                    'notebook' : self.foldpanelAppear.GetId()}
+        self.win['decoration'] = {}
+
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        # north arrow
+        self.win['decoration']['arrow'] = {}
+        nabox = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                             label = " %s " % (_("North Arrow")))
+        naboxSizer = wx.StaticBoxSizer(nabox, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        # size
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Arrow length (in map units):")),
+                      pos = (0,0), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        sizeCtrl = NumTextCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1), style = wx.TE_PROCESS_ENTER)
+        gridSizer.Add(sizeCtrl, pos = (0, 2))
+        self.win['decoration']['arrow']['size'] = sizeCtrl.GetId()
+        sizeCtrl.Bind(wx.EVT_TEXT_ENTER, self.OnDecorationProp)
+        sizeCtrl.Bind(wx.EVT_KILL_FOCUS, self.OnDecorationProp)
+        
+        # color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Arrow color:")),
+                      pos = (1,0), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        color = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        gridSizer.Add(color, pos = (1, 2))
+        self.win['decoration']['arrow']['color'] = color.GetId()
+        color.Bind(csel.EVT_COLOURSELECT, self.OnDecorationProp)
+        
+        # control
+        toggle = wx.ToggleButton(parent = panel, id = wx.ID_ANY, label = _("Place arrow"))
+        gridSizer.Add(item = toggle, pos = (2, 0))
+        toggle.Bind(wx.EVT_TOGGLEBUTTON, self.OnDecorationPlacement)
+        self.win['decoration']['arrow']['place'] = toggle.GetId()
+        toggle.SetName('placeArrow')
+
+        delete = wx.Button(parent = panel, id = wx.ID_ANY, label = _("Delete"))
+        gridSizer.Add(item = delete, pos = (2, 1))
+        delete.Bind(wx.EVT_BUTTON, self.OnArrowDelete)
+        naboxSizer.Add(item = gridSizer, proportion = 0, flag = wx.EXPAND, border = 3)
+        pageSizer.Add(item = naboxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        
+        # north arrow
+        self.win['decoration']['scalebar'] = {}
+        nabox = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                             label = " %s " % (_("Scale bar")))
+        naboxSizer = wx.StaticBoxSizer(nabox, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        # size
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Scale bar length (in map units):")),
+                      pos = (0,0), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        sizeCtrl = NumTextCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1), style = wx.TE_PROCESS_ENTER)
+        gridSizer.Add(sizeCtrl, pos = (0, 2))
+        self.win['decoration']['scalebar']['size'] = sizeCtrl.GetId()
+        sizeCtrl.Bind(wx.EVT_TEXT_ENTER, self.OnDecorationProp)
+        sizeCtrl.Bind(wx.EVT_KILL_FOCUS, self.OnDecorationProp)
+        
+        # color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Scale bar color:")),
+                      pos = (1,0), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        color = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        gridSizer.Add(color, pos = (1, 2))
+        self.win['decoration']['scalebar']['color'] = color.GetId()
+        color.Bind(csel.EVT_COLOURSELECT, self.OnDecorationProp)
+        
+        # control
+        toggle = wx.ToggleButton(parent = panel, id = wx.ID_ANY, label = _("Place scalebar"))
+        gridSizer.Add(item = toggle, pos = (2, 0))
+        toggle.Bind(wx.EVT_TOGGLEBUTTON, self.OnDecorationPlacement)
+        self.win['decoration']['scalebar']['place'] = toggle.GetId()
+        toggle.SetName('placeScalebar')
+
+        delete = wx.Button(parent = panel, id = wx.ID_ANY, label = _("Delete last"))
+        gridSizer.Add(item = delete, pos = (2, 1))
+        delete.Bind(wx.EVT_BUTTON, self.OnScalebarDelete)
+        naboxSizer.Add(item = gridSizer, proportion = 0, flag = wx.EXPAND, border = 3)
+        pageSizer.Add(item = naboxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)      
+        panel.SetSizer(pageSizer)
+        panel.Layout()
+        panel.Fit()
+
+        return panel
+    
+    def GetLayerData(self, nvizType, nameOnly = False):
+        """!Get nviz data"""
+        name = self.FindWindowById(self.win[nvizType]['map']).GetValue()
+        if nameOnly:
+            return name
+        
+        if nvizType == 'surface' or nvizType == 'fringe':
+            return self.mapWindow.GetLayerByName(name, mapType = 'raster', dataType = 'nviz')
+        elif nvizType == 'vector':
+            return self.mapWindow.GetLayerByName(name, mapType = 'vector', dataType = 'nviz')
+        elif nvizType == 'volume':
+            return self.mapWindow.GetLayerByName(name, mapType = '3d-raster', dataType = 'nviz')
+        
+        return None
+        
+    def OnRecord(self, event):
+        """!Animation: start recording"""
+        anim = self.mapWindow.GetAnimation()
+        if not anim.IsPaused():
+            if anim.Exists() and not anim.IsSaved():
+                msg = _("Do you want to record new animation without saving the previous one?")
+                dlg = wx.MessageDialog(parent = self,
+                                       message = msg,
+                                       caption =_("Animation already axists"),
+                                       style = wx.YES_NO | wx.CENTRE)
+                if dlg.ShowModal() == wx.ID_NO:
+                    dlg.Destroy()
+                    return
+                
+        
+            anim.Clear()
+            self.UpdateFrameIndex(0)
+            self.UpdateFrameCount()
+            
+        anim.SetPause(False)
+        anim.SetMode(mode = 'record')
+        anim.Start()
+        
+        self.FindWindowById(self.win['anim']['play']).Disable()
+        self.FindWindowById(self.win['anim']['record']).Disable()
+        self.FindWindowById(self.win['anim']['pause']).Enable()
+        self.FindWindowById(self.win['anim']['stop']).Enable()
+        self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+        
+    def OnPlay(self, event):
+        """!Animation: replay"""
+        anim = self.mapWindow.GetAnimation()
+        anim.SetPause(False)
+        anim.SetMode(mode = 'play')
+        anim.Start()
+        
+        self.FindWindowById(self.win['anim']['play']).Disable()
+        self.FindWindowById(self.win['anim']['record']).Disable()
+        self.FindWindowById(self.win['anim']['pause']).Enable()
+        self.FindWindowById(self.win['anim']['stop']).Enable()
+        self.FindWindowById(self.win['anim']['frameIndex']['slider']).Enable()
+        self.FindWindowById(self.win['anim']['frameIndex']['text']).Enable()
+        
+    def OnStop(self, event):
+        """!Animation: stop recording/replaying"""
+        anim = self.mapWindow.GetAnimation()
+        anim.SetPause(False)
+        if anim.GetMode() == 'save':
+            anim.StopSaving()
+        if anim.IsRunning():
+            anim.Stop()
+        
+        self.UpdateFrameIndex(0)
+        
+        self.FindWindowById(self.win['anim']['play']).Enable()
+        self.FindWindowById(self.win['anim']['record']).Enable()
+        self.FindWindowById(self.win['anim']['pause']).Disable()
+        self.FindWindowById(self.win['anim']['stop']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+        
+    def OnPause(self, event):
+        """!Pause animation"""
+        anim = self.mapWindow.GetAnimation()
+        
+        anim.SetPause(True)
+        mode = anim.GetMode()
+        if anim.IsRunning():
+            anim.Pause()
+            
+        if mode == "record":
+            self.FindWindowById(self.win['anim']['play']).Disable()
+            self.FindWindowById(self.win['anim']['record']).Enable()
+            self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+            self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+        elif mode == 'play':
+            self.FindWindowById(self.win['anim']['record']).Disable()
+            self.FindWindowById(self.win['anim']['play']).Enable()
+            self.FindWindowById(self.win['anim']['frameIndex']['slider']).Enable()
+            self.FindWindowById(self.win['anim']['frameIndex']['text']).Enable()
+        
+        self.FindWindowById(self.win['anim']['pause']).Disable()
+        self.FindWindowById(self.win['anim']['stop']).Enable()
+
+        
+    def OnFrameIndex(self, event):
+        """!Frame index changed (by slider)"""
+        index = event.GetInt()
+        self.UpdateFrameIndex(index = index, sliderWidget = False)
+        
+    def OnFrameIndexText(self, event):
+        """!Frame index changed by (textCtrl)"""
+        index = event.GetValue()
+        self.UpdateFrameIndex(index = index, textWidget = False)
+        
+    def OnFPS(self, event):
+        """!Frames per second changed"""
+        anim = self.mapWindow.GetAnimation()
+        anim.SetFPS(event.GetInt())
+        
+    def UpdateFrameIndex(self, index, sliderWidget = True, textWidget = True, goToFrame = True):
+        """!Update frame index"""
+        anim = self.mapWindow.GetAnimation()
+        
+        # check index
+        frameCount = anim.GetFrameCount()
+        if index >= frameCount:
+            index = frameCount - 1
+        if index < 0:
+            index = 0
+            
+        if sliderWidget:
+            slider = self.FindWindowById(self.win['anim']['frameIndex']['slider'])
+            slider.SetValue(index)
+        if textWidget:
+            text = self.FindWindowById(self.win['anim']['frameIndex']['text'])
+            text.SetValue(int(index))
+        
+        # if called from tool window, update frame
+        if goToFrame:
+            anim.GoToFrame(int(index))
+            
+    def UpdateFrameCount(self):
+        """!Update frame count label"""
+        anim = self.mapWindow.GetAnimation()
+        count = anim.GetFrameCount()
+        self.FindWindowById(self.win['anim']['info']).SetLabel(str(count))
+        
+    def OnAnimationFinished(self, event):
+        """!Animation finished"""
+        anim = self.mapWindow.GetAnimation()
+        self.UpdateFrameIndex(index = 0)
+        
+        slider = self.FindWindowById(self.win['anim']['frameIndex']['slider'])
+        text = self.FindWindowById(self.win['anim']['frameIndex']['text'])
+        
+        if event.mode == 'record':
+            count = anim.GetFrameCount()
+            slider.SetMax(count)
+            self.UpdateFrameCount()
+            
+        self.FindWindowById(self.win['anim']['pause']).Disable()
+        self.FindWindowById(self.win['anim']['stop']).Disable()
+        self.FindWindowById(self.win['anim']['record']).Enable()
+        self.FindWindowById(self.win['anim']['play']).Enable()
+        self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+        self.FindWindowById(self.win['anim']['save']['image']['confirm']).Enable()
+        
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.Refresh(False)
+        
+    def OnAnimationUpdateIndex(self, event):
+        """!Animation: frame index changed"""
+        if event.mode == 'record':
+            self.UpdateFrameCount()
+        elif event.mode == 'play':
+            self.UpdateFrameIndex(index = event.index, goToFrame = False)
+        
+    def OnSaveAnimation(self, event):
+        """!Save animation as a sequence of images"""
+        anim = self.mapWindow.GetAnimation()
+        
+        prefix = self.FindWindowById(self.win['anim']['save']['image']['prefix']).GetValue()
+        format = self.FindWindowById(self.win['anim']['save']['image']['format']).GetSelection()
+        dir = self.FindWindowById(self.win['anim']['save']['image']['dir']).GetValue()
+        
+        if not prefix:
+            GMessage(parent = self,
+                     message = _("No file prefix given."))
+            return
+        elif not os.path.exists(dir):
+            GMessage(parent = self,
+                     message = _("Directory %s does not exist.") % dir)
+            return
+            
+        self.FindWindowById(self.win['anim']['pause']).Disable()
+        self.FindWindowById(self.win['anim']['stop']).Enable()
+        self.FindWindowById(self.win['anim']['record']).Disable()
+        self.FindWindowById(self.win['anim']['play']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+        
+        self.FindWindowById(self.win['anim']['save']['image']['confirm']).Disable()
+        
+        anim.SaveAnimationFile(path = dir, prefix = prefix, format = format)
+        
+    def OnNewConstant(self, event):
+        """!Create new surface with constant value"""
+        #TODO settings
+        name = self.mapWindow.NewConstant()
+        win = self.FindWindowById(self.win['constant']['surface'])
+        name = _("constant#") + str(name)
+        win.Append(name)
+        win.SetStringSelection(name)
+        self.OnConstantSelection(None)
+        self.EnablePage(name = 'constant', enabled = True)
+        
+        self.mapWindow.Refresh(eraseBackground = False)
+        
+        # need to update list of surfaces in vector page
+        for vtype in ('points', 'lines'):
+            checklist = self.FindWindowById(self.win['vector'][vtype]['surface'])
+            checklist.Append(name)
+        win = self.FindWindowById(self.win['vector']['map'])
+        win.SetValue(win.GetValue())
+                
+
+    def OnDeleteConstant(self, event):
+        """!Delete selected constant surface"""
+        layerIdx = self.FindWindowById(self.win['constant']['surface']).GetSelection()
+        if layerIdx == wx.NOT_FOUND:
+            return
+        name = self.FindWindowById(self.win['constant']['surface']).GetStringSelection()
+        self.mapWindow.DeleteConstant(layerIdx)
+        win = self.FindWindowById(self.win['constant']['surface'])
+        win.Delete(layerIdx)
+        if win.IsEmpty():
+            win.SetValue("")
+            self.EnablePage(name = 'constant', enabled = False)
+        else:
+            win.SetSelection(0)
+            self.OnConstantSelection(None)
+            
+        # need to update list of surfaces in vector page
+        for vtype in ('points', 'lines'):
+            checklist = self.FindWindowById(self.win['vector'][vtype]['surface'])
+            checklist.Delete(checklist.FindString(name))
+            
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+    
+    def OnConstantSelection(self, event):
+        """!Constant selected"""
+        layerIdx = self.FindWindowById(self.win['constant']['surface']).GetSelection()
+        if layerIdx == wx.NOT_FOUND:
+            return
+        name = _("constant#") + str(layerIdx + 1)
+        data = self.mapWindow.constants[layerIdx]
+        for attr, value in data['constant'].iteritems():
+            if attr == 'color':
+                value = self._getColorFromString(value)
+            if attr in ('color', 'value', 'resolution', 'transp'):
+                if attr == 'transp':
+                    self.FindWindowById(self.win['constant'][attr]).SetValue(self._getPercent(value))
+                self.FindWindowById(self.win['constant'][attr]).SetValue(value)
+        
+    def OnSetConstantProp(self, event):
+        """!Change properties (color, value, resolution)
+            of currently selected constant surface"""
+        layerIdx = self.FindWindowById(self.win['constant']['surface']).GetSelection()
+        if layerIdx == wx.NOT_FOUND:
+            return
+        data = self.mapWindow.constants[layerIdx]
+        for attr in ('resolution', 'value', 'transp'):
+            data['constant'][attr] = self.FindWindowById(self.win['constant'][attr]).GetValue()
+        data['constant']['color'] = self._getColorString(
+                self.FindWindowById(self.win['constant']['color']).GetValue())
+        data['constant']['transp'] = self._getPercent(data['constant']['transp'], toPercent = False)
+        
+       # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event) 
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def OnFringe(self, event):
+        """!Show/hide fringe"""
+        data = self.GetLayerData('fringe')['surface']
+        
+        sid = data['object']['id']
+        elev = self.FindWindowById(self.win['fringe']['elev']).GetValue()
+        color = self.FindWindowById(self.win['fringe']['color']).GetValue()
+        
+        self._display.SetFringe(sid, color, elev,
+                                self.FindWindowById(self.win['fringe']['nw']).IsChecked(),
+                                self.FindWindowById(self.win['fringe']['ne']).IsChecked(),
+                                self.FindWindowById(self.win['fringe']['sw']).IsChecked(),
+                                self.FindWindowById(self.win['fringe']['se']).IsChecked())
+        self.mapWindow.Refresh(False)
+        
+    def OnScroll(self, event, win, data):
+        """!Generic scrolling handler"""
+        winName = self.__GetWindowName(win, event.GetId())
+        if not winName:
+            return
+        data[winName] = self.FindWindowById(event.GetId()).GetValue()
+        for w in win[winName].itervalues():
+            self.FindWindowById(w).SetValue(data[winName])
+        
+        event.Skip()
+        
+    def AdjustSliderRange(self, slider, value):
+        minim, maxim = slider.GetRange()
+        if not (minim <= value <= maxim):
+            slider.SetRange(min(minim, value), max(maxim, value))
+    
+    def _createIsosurfacePanel(self, parent):
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        
+        vSizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Isosurface attributes")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
+        self.win['volume']['attr'] = {}
+        inout = wx.CheckBox(parent = panel, id = wx.ID_ANY, 
+                            label = _("toggle normal direction"))
+        gridSizer.Add(item = inout, pos = (0,0), span = (1,2), flag = wx.ALIGN_CENTER_VERTICAL)
+        inout.Bind(wx.EVT_CHECKBOX, self.OnInOutMode)
+        self.win['volume']['inout'] = inout.GetId()
+        
+        row = 1
+        for code, attrb in (('topo', _("Isosurface value")),
+                            ('color', _("Color")),
+                            ('mask', _("Mask")),
+                            ('transp', _("Transparency")),
+                            ('shine', _("Shininess"))):
+            self.win['volume'][code] = {} 
+            # label
+            colspan = 1
+            if code == 'topo':
+                colspan = 2
+            gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                             label = attrb + ':'),
+                          pos = (row, 0), span = (1, colspan),flag = wx.ALIGN_CENTER_VERTICAL)
+            if code != 'topo':
+                use = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
+                                 choices = [_("map")])
+            else:
+                use = None
+            # check for required properties
+            if code not in ('topo', 'color', 'shine'):
+                use.Insert(item = _("unset"), pos = 0)
+                self.win['volume'][code]['required'] = False
+            else:
+                self.win['volume'][code]['required'] = True
+            if use and code != 'mask':
+                use.Append(item = _('constant'))
+            if use:
+                self.win['volume'][code]['use'] = use.GetId()
+                use.Bind(wx.EVT_CHOICE, self.OnMapObjUse)
+                gridSizer.Add(item = use, flag = wx.ALIGN_CENTER_VERTICAL,
+                              pos = (row, 1))
+                    
+            if code != 'topo':
+                map = Select(parent = panel, id = wx.ID_ANY,
+                             # size = globalvar.DIALOG_GSELECT_SIZE,
+                             size = (200, -1),
+                             type = "grid3")
+                self.win['volume'][code]['map'] = map.GetId() - 1 # FIXME
+                map.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfMap)
+                gridSizer.Add(item = map, flag = wx.ALIGN_CENTER_VERTICAL,
+                              pos = (row, 2))
+            else:
+                map = None
+            
+            if code == 'color':
+                color = UserSettings.Get(group = 'nviz', key = 'volume', subkey = ['color', 'value'])
+                value = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                          colour = color,
+                                          size = globalvar.DIALOG_COLOR_SIZE)
+                value.Bind(csel.EVT_COLOURSELECT, self.OnVolumeIsosurfMap)
+                value.SetName('color')
+            elif code == 'mask':
+                value = None
+            elif code == 'topo':
+                value = NumTextCtrl(parent = panel, id = wx.ID_ANY, size = (200, -1),
+                            style = wx.TE_PROCESS_ENTER)
+                value.Bind(wx.EVT_TEXT_ENTER, self.OnVolumeIsosurfMap)
+                value.Bind(wx.EVT_KILL_FOCUS, self.OnVolumeIsosurfMap)
+##                value.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfMap)
+            else:
+                size = (65, -1)
+                value = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = size,
+                                    initial = 0)
+                if code == 'topo':
+                    value.SetRange(minVal = -1e9, maxVal = 1e9)
+                elif code in ('shine', 'transp'):
+                    value.SetRange(minVal = 0, maxVal = 100)
+                
+                value.Bind(wx.EVT_SPINCTRL, self.OnVolumeIsosurfMap)
+                value.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfMap)
+            
+            if value:
+                self.win['volume'][code]['const'] = value.GetId()
+                if code == 'topo':
+                    gridSizer.Add(item = value, flag = wx.ALIGN_CENTER_VERTICAL,
+                                  pos = (row, 2))
+                else:
+                    value.Enable(False)
+                    gridSizer.Add(item = value, flag = wx.ALIGN_CENTER_VERTICAL,
+                                  pos = (row, 3))
+            else:
+                self.win['volume'][code]['const'] = None
+            
+            if code != 'topo':
+                self.SetMapObjUseMap(nvizType = 'volume',
+                                     attrb = code) # -> enable map / disable constant
+            
+            row += 1
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        vSizer.Add(item = boxSizer, proportion = 1,
+                     flag = wx.EXPAND, border = 0)
+        panel.SetSizer(vSizer)
+        
+        return panel
+    
+    def _createSlicePanel(self, parent):
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        
+        vSizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Slice attributes")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        hSizer = wx.BoxSizer()
+        
+        self.win['volume']['slice'] = {}
+        hSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                      label = _("Slice parallel to axis:")), proportion = 0,
+                      flag = wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border = 3)
+        axes = wx.Choice(parent = panel, id = wx.ID_ANY, size = (65, -1), choices = ("X", "Y", "Z"))
+        hSizer.Add(axes, proportion = 0, flag = wx.ALIGN_LEFT|wx.LEFT, border = 3)
+        self.win['volume']['slice']['axes'] = axes.GetId()
+        axes.Bind(wx.EVT_CHOICE, self.OnVolumeSliceAxes)
+        boxSizer.Add(hSizer, proportion = 0, flag = wx.ALL|wx.EXPAND, border = 3)
+        
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(0,1)
+        gridSizer.AddGrowableCol(1,2)
+        gridSizer.AddGrowableCol(2,2)
+        
+        # text labels
+        for i in range(2):
+            label = wx.StaticText(parent = panel, id = wx.ID_ANY)
+            label.SetName('label_edge_' + str(i))
+            gridSizer.Add(item = label, pos = (0, i + 1),
+                          flag = wx.ALIGN_CENTER)
+        for i in range(2,4):
+            label = wx.StaticText(parent = panel, id = wx.ID_ANY)
+            label.SetName('label_edge_' + str(i))
+            gridSizer.Add(item = label, pos = (3, i -1),
+                          flag = wx.ALIGN_CENTER)
+        for i in range(2):
+            label = wx.StaticText(parent = panel, id = wx.ID_ANY)
+            label.SetName('label_coord_' + str(i))
+            gridSizer.Add(item = label, pos = (i + 1, 0),
+                          flag = wx.ALIGN_CENTER_VERTICAL)
+        label = wx.StaticText(parent = panel, id = wx.ID_ANY)
+        label.SetName('label_coord_2')
+        gridSizer.Add(item = label, pos = (4, 0), 
+                          flag = wx.ALIGN_CENTER_VERTICAL)
+        # sliders
+        for i, coord in enumerate(('x1', 'x2')):
+            slider = wx.Slider(parent = panel, id = wx.ID_ANY, minValue = 0, maxValue = 100, value = 0)
+            self.win['volume']['slice']['slider_' + coord] = slider.GetId()
+            slider.Bind(wx.EVT_SPIN, self.OnSlicePositionChange)
+            slider.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.OnSlicePositionChanged)
+            gridSizer.Add(item = slider, pos = (1, i + 1), 
+                          flag = wx.ALIGN_CENTER|wx.EXPAND)
+                        
+        for i, coord in enumerate(('y1', 'y2')):
+            slider = wx.Slider(parent = panel, id = wx.ID_ANY, minValue = 0, maxValue = 100, value = 0)
+            self.win['volume']['slice']['slider_' + coord] = slider.GetId()
+            slider.Bind(wx.EVT_SPIN, self.OnSlicePositionChange)
+            slider.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.OnSlicePositionChanged)
+            gridSizer.Add(item = slider, pos = (2, i + 1), 
+                          flag = wx.ALIGN_CENTER|wx.EXPAND)
+        
+        for i, coord in enumerate(('z1', 'z2')):
+            slider = wx.Slider(parent = panel, id = wx.ID_ANY, minValue = 0, maxValue = 100, value = 0)
+            self.win['volume']['slice']['slider_' + coord] = slider.GetId()
+            slider.Bind(wx.EVT_SPIN, self.OnSlicePositionChange)
+            slider.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.OnSlicePositionChanged)
+            gridSizer.Add(item = slider, pos = (4,i+1), 
+                          flag = wx.ALIGN_CENTER|wx.EXPAND)
+                        
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        
+        # transparency, reset
+        hSizer = wx.BoxSizer()
+        hSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                      label = _("Transparency:")), proportion = 0,
+                      flag = wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, border = 7)
+        spin = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           min = 0, max = 100, initial = 0)
+        spin.Bind(wx.EVT_SPINCTRL, self.OnSliceTransparency)
+        self.win['volume']['slice']['transp'] = spin.GetId()
+        hSizer.Add(item = spin, proportion = 0,
+                      flag = wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.TOP, border = 7)
+                    
+        hSizer.Add(item = wx.Size(-1, -1), proportion = 1,
+                      flag = wx.EXPAND)
+        reset = wx.Button(parent = panel, id = wx.ID_ANY, label = _("Reset"))
+        reset.Bind(wx.EVT_BUTTON, self.OnSliceReset)
+        self.win['volume']['slice']['reset'] = reset.GetId()
+        hSizer.Add(item = reset, proportion = 0,
+                      flag = wx.ALIGN_CENTER_VERTICAL|wx.TOP, border = 7)
+        
+        boxSizer.Add(hSizer, proportion = 0, flag = wx.ALL|wx.EXPAND, border = 3)
+        panel.SetSizer(boxSizer)
+        
+        return panel
+    
+    def _createControl(self, parent, data, name, range, bind = (None, None, None),
+                       sliderHor = True, size = 200, floatSlider = False):
+        """!Add control (Slider + TextCtrl)"""
+        data[name] = dict()
+        if sliderHor:
+            style = wx.SL_HORIZONTAL | wx.SL_AUTOTICKS | \
+                wx.SL_BOTTOM
+            sizeW = (size, -1)
+        else:
+            style = wx.SL_VERTICAL | wx.SL_AUTOTICKS | \
+                wx.SL_INVERSE
+            sizeW = (-1, size)
+        
+        kwargs = dict(parent = parent, id = wx.ID_ANY,
+                           minValue = range[0],
+                           maxValue = range[1],
+                           style = style,
+                           size = sizeW)
+        if floatSlider:
+            slider = FloatSlider(**kwargs)
+        else:
+            slider = wx.Slider(**kwargs)
+            
+        slider.SetName('slider')
+        if bind[0]:
+            #EVT_SCROLL emits event after slider is released, EVT_SPIN not
+            slider.Bind(wx.EVT_SPIN, bind[0])
+        
+        if bind[1]:
+            slider.Bind(wx.EVT_SCROLL_THUMBRELEASE, bind[1]) 
+        data[name]['slider'] = slider.GetId()
+        
+        text = NumTextCtrl(parent = parent, id = wx.ID_ANY, size = (65, -1),
+                            style = wx.TE_PROCESS_ENTER)
+        
+        text.SetName('text')
+        if bind[2]:
+            text.Bind(wx.EVT_TEXT_ENTER, bind[2])
+            text.Bind(wx.EVT_KILL_FOCUS, bind[2])
+        
+        data[name]['text'] = text.GetId()
+        
+    def _createCompass(self, panel, sizer, type):
+        """!Create 'compass' widget for light and view page"""
+        w = wx.Button(panel, id = wx.ID_ANY, label = _("W"))
+        n = wx.Button(panel, id = wx.ID_ANY, label = _("N"))
+        s = wx.Button(panel, id = wx.ID_ANY, label = _("S"))
+        e = wx.Button(panel, id = wx.ID_ANY, label = _("E"))
+        nw = wx.Button(panel, id = wx.ID_ANY, label = _("NW"))
+        ne = wx.Button(panel, id = wx.ID_ANY, label = _("NE"))
+        se = wx.Button(panel, id = wx.ID_ANY, label = _("SE"))
+        sw = wx.Button(panel, id = wx.ID_ANY, label = _("SW"))
+        minWidth = sw.GetTextExtent(sw.GetLabel())[0] + 15
+        for win, name in zip((w, n, s, e, nw, ne, se, sw),
+                        ('w', 'n', 's', 'e', 'nw', 'ne', 'se', 'sw')):
+            win.SetMinSize((minWidth, -1))
+            win.Bind(wx.EVT_BUTTON, self.OnLookFrom)
+            win.SetName(type + '_' + name)
+        sizer.Add(item = nw, pos = (0, 0), flag = wx.ALIGN_CENTER)
+        sizer.Add(item = n, pos = (0, 1), flag = wx.ALIGN_CENTER)
+        sizer.Add(item = ne, pos = (0, 2), flag = wx.ALIGN_CENTER)
+        sizer.Add(item = e, pos = (1, 2), flag = wx.ALIGN_CENTER)
+        sizer.Add(item = se, pos = (2, 2), flag = wx.ALIGN_CENTER)
+        sizer.Add(item = s, pos = (2, 1), flag = wx.ALIGN_CENTER)
+        sizer.Add(item = sw, pos = (2, 0), flag = wx.ALIGN_CENTER)
+        sizer.Add(item = w, pos = (1, 0), flag = wx.ALIGN_CENTER)
+        
+        
+        
+    def __GetWindowName(self, data, id):
+        for name in data.iterkeys():
+            if type(data[name]) is type({}):
+                for win in data[name].itervalues():
+                    if win == id:
+                        return name
+            else:
+                if data[name] == id:
+                    return name
+        
+        return None
+
+    def UpdateSettings(self):
+        """!Update view from settings values 
+        stored in self.mapWindow.view dictionary"""
+        for control in ('height',
+                        'persp',
+                        'twist',
+                        'z-exag'):
+            for win in self.win['view'][control].itervalues():             
+                try:
+                    if control == 'height':
+                        value = int(self.mapWindow.iview[control]['value'])
+                    else:
+                        value = self.mapWindow.view[control]['value']
+                except KeyError:
+                    value = -1
+                        
+                self.FindWindowById(win).SetValue(value)
+        
+        viewWin = self.FindWindowById(self.win['view']['position'])
+        x, y = viewWin.UpdatePos(self.mapWindow.view['position']['x'],
+                                 self.mapWindow.view['position']['y'])
+        viewWin.Draw(pos = (x, y), scale = True)
+        viewWin.Refresh(False)
+        
+        color = self._getColorString(self.mapWindow.view['background']['color'])
+        self._display.SetBgColor(str(color))
+        
+        self.Update()
+        
+        self.mapWindow.Refresh(eraseBackground = False)
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.Refresh(True)
+        
+    def OnShowLightModel(self, event):
+        """!Show light model"""
+        self._display.showLight = event.IsChecked()
+        self._display.DrawLightingModel()
+        
+    def OnLightChange(self, event):
+        """!Position of the light changing"""
+        winName = self.__GetWindowName(self.win['light'], event.GetId())
+        if not winName:
+            return
+        
+        value = self.FindWindowById(event.GetId()).GetValue()
+        
+        self.mapWindow.light['position']['z'] = value
+        for win in self.win['light'][winName].itervalues():
+            self.FindWindowById(win).SetValue(value)
+            
+        self.PostLightEvent()
+        
+        event.Skip()
+        
+    def OnLightChanged(self, event):
+        """!Light changed"""
+        self.PostLightEvent(refresh = True)
+        
+    def OnLightColor(self, event):
+        """!Color of the light changed"""
+        self.mapWindow.light['color'] = tuple(event.GetValue())
+        
+        self.PostLightEvent(refresh = True)
+        
+        event.Skip()
+        
+    def OnLightValue(self, event):
+        """!Light brightness/ambient changing"""
+        data = self.mapWindow.light
+        self.OnScroll(event, self.win['light'], data)
+        
+        self.PostLightEvent()
+        event.Skip()
+        
+    def OnBgColor(self, event):
+        """!Background color changed"""
+        color = event.GetValue()
+        self.mapWindow.view['background']['color'] = tuple(color)
+        color = str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
+        self._display.SetBgColor(str(color))
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def OnSetSurface(self, event):
+        """!Surface selected, currently used for fringes"""
+        name = event.GetString()
+        try:
+            data = self.mapWindow.GetLayerByName(name, mapType = 'raster', dataType = 'nviz')['surface']
+        except:
+            self.EnablePage('fringe', False)
+            return
+        
+        layer = self.mapWindow.GetLayerByName(name, mapType = 'raster')
+        self.EnablePage('fringe', True)
+        
+    def OnSetRaster(self, event):
+        """!Raster map selected, update surface page"""
+        name = event.GetString()
+        try:
+            data = self.mapWindow.GetLayerByName(name, mapType = 'raster', dataType = 'nviz')['surface']
+        except:
+            self.EnablePage('surface', False)
+            return
+
+        layer = self.mapWindow.GetLayerByName(name, mapType = 'raster')
+        self.EnablePage('surface', True)
+        self.UpdateSurfacePage(layer, data, updateName = False)
+        
+    def OnSetVector(self, event):
+        """!Vector map selected, update properties page"""
+        name = event.GetString()
+        try:
+            data = self.mapWindow.GetLayerByName(name, mapType = 'vector', dataType = 'nviz')['vector']
+        except:
+            self.EnablePage('vector', False)
+            return
+        layer = self.mapWindow.GetLayerByName(name, mapType = 'vector')
+        self.EnablePage('vector', True)
+        self.UpdateVectorPage(layer, data, updateName = False)
+
+    def OnSetRaster3D(self, event):
+        """!3D Raster map selected, update surface page"""
+        name = event.GetString()
+        try:
+            data = self.mapWindow.GetLayerByName(name, mapType = '3d-raster', dataType = 'nviz')['volume']
+        except:
+            self.EnablePage('volume', False)
+            return
+        
+        layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+        self.EnablePage('volume', True)
+        self.UpdateVolumePage(layer, data, updateName = False)
+        
+    def OnViewChange(self, event):
+        """!Change view, render in quick mode"""
+        # find control
+        winName = self.__GetWindowName(self.win['view'], event.GetId())
+        if not winName:
+            return
+        
+        value = self.FindWindowById(event.GetId()).GetValue()
+        slider = self.FindWindowById(self.win['view'][winName]['slider'])
+        self.AdjustSliderRange(slider = slider, value = value)
+        
+        if winName == 'height':
+            view = self.mapWindow.iview # internal
+        else:
+            view = self.mapWindow.view
+        
+        if winName == 'z-exag' and value >= 0:
+            self.PostViewEvent(zExag = True)
+        else:
+            self.PostViewEvent(zExag = False)
+        
+        if winName in ('persp', 'twist'):
+            convert = int
+        else:
+            convert = float
+        
+        view[winName]['value'] = convert(value)
+            
+        for win in self.win['view'][winName].itervalues():
+            self.FindWindowById(win).SetValue(value)
+
+        self.mapWindow.iview['dir']['use'] = False
+        self.mapWindow.render['quick'] = True
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+        event.Skip()
+        
+    def OnViewChanged(self, event):
+        """!View changed, render in full resolution"""
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.Refresh(False)
+        self.UpdateSettings()
+        try:# when calling event = None
+            event.Skip()
+        except AttributeError:
+            pass
+            
+    def OnViewChangedText(self, event):
+        """!View changed, render in full resolution""" 
+        self.mapWindow.render['quick'] = False
+        self.OnViewChange(event)
+        self.OnViewChanged(None)
+        self.Update()
+        
+        event.Skip()
+    
+    def OnLookAt(self, event):
+        """!Look here/center"""
+        name = self.FindWindowById(event.GetId()).GetName()
+        if name == 'center':
+            self._display.LookAtCenter()
+            focus = self.mapWindow.iview['focus']
+            focus['x'], focus['y'], focus['z'] = self._display.GetFocus()
+            self.mapWindow.saveHistory = True
+            self.mapWindow.Refresh(False)
+        elif name == 'top':
+            self.mapWindow.view['position']['x'] = 0.5
+            self.mapWindow.view['position']['y'] = 0.5
+            self.PostViewEvent(zExag = True)
+            self.UpdateSettings()
+            self.mapWindow.Refresh(False)
+        else: # here
+            if self.FindWindowById(event.GetId()).GetValue():
+                self.mapDisplay.Raise()
+                self.mapWindow.mouse['use'] = 'lookHere'
+                self.mapWindow.SetCursor(self.mapWindow.cursors["cross"])
+            else:
+                self.mapWindow.mouse['use'] = 'default'
+                self.mapWindow.SetCursor(self.mapWindow.cursors['default'])
+            
+    def OnResetView(self, event):
+        """!Reset to default view (view page)"""
+        self.mapWindow.ResetView()
+        self.UpdateSettings()
+        self.mapWindow.Refresh(False)
+        
+    def OnResetSurfacePosition(self, event):
+        """!Reset position of surface"""
+        
+        for win in self.win['surface']['position'].itervalues():
+            if win == self.win['surface']['position']['axis']:
+                self.FindWindowById(win).SetSelection(0)
+            elif win == self.win['surface']['position']['reset']:
+                continue
+            else:
+                self.FindWindowById(win).SetValue(0)
+                
+        data = self.GetLayerData('surface')
+        data['surface']['position']['x'] = 0
+        data['surface']['position']['y'] = 0
+        data['surface']['position']['z'] = 0
+        data['surface']['position']['update'] = None
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+            
+    def OnLookFrom(self, event):
+        """!Position of view/light changed by buttons"""
+        name = self.FindWindowById(event.GetId()).GetName()
+        buttonName = name.split('_')[1]
+        if name.split('_')[0] == 'view':
+            type = 'view'
+            data = self.mapWindow.view
+        else:
+            type = 'light'
+            data = self.mapWindow.light
+        if buttonName == 'n': # north
+            data['position']['x'] = 0.5
+            data['position']['y'] = 0.0
+        elif buttonName == 's': # south
+            data['position']['x'] = 0.5
+            data['position']['y'] = 1.0
+        elif buttonName == 'e': # east
+            data['position']['x'] = 1.0
+            data['position']['y'] = 0.5
+        elif buttonName =='w': # west
+            data['position']['x'] = 0.0
+            data['position']['y'] = 0.5
+        elif buttonName == 'nw': # north-west
+            data['position']['x'] = 0.0
+            data['position']['y'] = 0.0
+        elif buttonName == 'ne': # north-east
+            data['position']['x'] = 1.0
+            data['position']['y'] = 0.0
+        elif buttonName == 'se': # south-east
+            data['position']['x'] = 1.0
+            data['position']['y'] = 1.0
+        elif buttonName == 'sw': # south-west
+            data['position']['x'] = 0.0
+            data['position']['y'] = 1.0
+        if type == 'view':    
+            self.PostViewEvent(zExag = True)
+            
+            self.UpdateSettings()
+        else:
+            self.PostLightEvent()
+            lightWin = self.FindWindowById(self.win['light']['position'])
+            x, y = lightWin.UpdatePos(self.mapWindow.light['position']['x'],
+                                     self.mapWindow.light['position']['y'])
+            lightWin.Draw(pos = (x, y), scale = True)
+            lightWin.Refresh(False)
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.Refresh(False)
+
+    def OnMapObjUse(self, event):
+        """!Set surface attribute -- use -- map/constant"""
+        if not self.mapWindow.init:
+            return
+        
+        wx.Yield()
+        
+        # find attribute row
+        attrb = self.__GetWindowName(self.win['surface'], event.GetId())
+        if not attrb:
+            attrb = self.__GetWindowName(self.win['volume'], event.GetId())
+            nvizType = 'volume'
+        else:
+            nvizType = 'surface'
+        
+        selection = event.GetSelection()
+        if self.win[nvizType][attrb]['required']: # no 'unset'
+            selection += 1
+        if selection == 0: # unset
+            useMap = None
+            value = ''
+        elif selection == 1: # map
+            useMap = True
+            value = self.FindWindowById(self.win[nvizType][attrb]['map']).GetValue()
+        elif selection == 2: # constant
+            useMap = False
+            if attrb == 'color':
+                value = self.FindWindowById(self.win[nvizType][attrb]['const']).GetColour()
+                value = self._getColorString(value)
+            else:
+                value = self._getPercent(self.FindWindowById(self.win[nvizType][attrb]['const']).GetValue(), toPercent = False)
+        
+        self.SetMapObjUseMap(nvizType = nvizType,
+                             attrb = attrb, map = useMap)
+        
+        name = self.FindWindowById(self.win[nvizType]['map']).GetValue()
+        if nvizType == 'surface':
+            data = self.mapWindow.GetLayerByName(name, mapType = 'raster', dataType = 'nviz')
+            data[nvizType]['attribute'][attrb] = { 'map' : useMap,
+                                                   'value' : str(value),
+                                                   'update' : None }
+        else: # volume / isosurface
+            data = self.mapWindow.GetLayerByName(name, mapType = '3d-raster', dataType = 'nviz')
+            list = self.FindWindowById(self.win['volume']['isosurfs'])
+            id = list.GetSelection()
+            if id != -1:
+                data[nvizType]['isosurface'][id][attrb] = { 'map' : useMap,
+                                                            'value' : str(value),
+                                                            'update' : None }
+        
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+ 
+    def EnablePage(self, name, enabled = True):
+        """!Enable/disable all widgets on page"""
+        for key, item in self.win[name].iteritems():
+            if key in ('map', 'surface', 'new','planes'):
+                continue
+            if type(item) == types.DictType:
+                for skey, sitem in self.win[name][key].iteritems():
+                    if type(sitem) == types.DictType:
+                        for ssitem in self.win[name][key][skey].itervalues():
+                            if type(ssitem) == types.IntType:
+                                self.FindWindowById(ssitem).Enable(enabled)
+                    else:
+                        if type(sitem) == types.IntType:
+                            self.FindWindowById(sitem).Enable(enabled)
+            else:
+                if type(item) == types.IntType:
+                    self.FindWindowById(item).Enable(enabled)
+        
+    def SetMapObjUseMap(self, nvizType, attrb, map = None):
+        """!Update dialog widgets when attribute type changed"""
+        if attrb in ('topo', 'color', 'shine'):
+            incSel = -1 # decrement selection (no 'unset')
+        else:
+            incSel = 0
+        if nvizType == 'volume' and attrb == 'topo':
+            return
+        if map is True: # map
+            if attrb != 'topo': # changing map topography not allowed
+                # not sure why, but here must be disabled both ids, should be fixed!
+                self.FindWindowById(self.win[nvizType][attrb]['map'] + 1).Enable(True)
+            if self.win[nvizType][attrb]['const']:
+                self.FindWindowById(self.win[nvizType][attrb]['const']).Enable(False)
+            self.FindWindowById(self.win[nvizType][attrb]['use']).SetSelection(1 + incSel)
+        elif map is False: # const
+            self.FindWindowById(self.win[nvizType][attrb]['map'] + 1).Enable(False)
+            if self.win[nvizType][attrb]['const']:
+                self.FindWindowById(self.win[nvizType][attrb]['const']).Enable(True)
+            self.FindWindowById(self.win[nvizType][attrb]['use']).SetSelection(2 + incSel)
+        else: # unset
+            self.FindWindowById(self.win[nvizType][attrb]['use']).SetSelection(0)
+            self.FindWindowById(self.win[nvizType][attrb]['map'] + 1).Enable(False)
+            if self.win[nvizType][attrb]['const']:
+                self.FindWindowById(self.win[nvizType][attrb]['const']).Enable(False)
+            
+        
+    def OnSurfaceMap(self, event):
+        """!Set surface attribute"""
+        if self.vetoGSelectEvt:
+            self.vetoGSelectEvt = False
+            return
+        self.SetMapObjAttrb(nvizType = 'surface', winId = event.GetId())
+        
+    def SetMapObjAttrb(self, nvizType, winId):
+        """!Set map object (surface/isosurface) attribute (map/constant)"""
+        if not self.mapWindow.init:
+            return
+        
+        attrb = self.__GetWindowName(self.win[nvizType], winId)
+        if not attrb:
+            return
+        
+        if not (nvizType == 'volume' and attrb == 'topo'):
+            selection = self.FindWindowById(self.win[nvizType][attrb]['use']).GetSelection()
+            if self.win[nvizType][attrb]['required']:
+                selection += 1
+
+            if selection == 0: # unset
+                useMap = None
+                value = ''
+            elif selection == 1: # map
+                value = self.FindWindowById(self.win[nvizType][attrb]['map']).GetValue()
+                useMap = True
+            else: # constant
+                if attrb == 'color':
+                    value = self.FindWindowById(self.win[nvizType][attrb]['const']).GetColour()
+                    # tuple to string
+                    value = self._getColorString(value)
+                else:
+                    value = self._getPercent(
+                        self.FindWindowById(self.win[nvizType][attrb]['const']).GetValue(), toPercent = False)
+                    
+                useMap = False
+        else:
+            useMap = None
+            value = self.FindWindowById(self.win[nvizType][attrb]['const']).GetValue()
+        if not self.pageChanging:
+            name = self.FindWindowById(self.win[nvizType]['map']).GetValue()
+            if nvizType == 'surface':
+                data = self.mapWindow.GetLayerByName(name, mapType = 'raster', dataType = 'nviz')
+                data[nvizType]['attribute'][attrb] = { 'map' : useMap,
+                                                       'value' : str(value),
+                                                       'update' : None }
+            else:
+                data = self.mapWindow.GetLayerByName(name, mapType = '3d-raster', dataType = 'nviz')
+                list = self.FindWindowById(self.win['volume']['isosurfs'])
+                id = list.GetSelection()
+                if id > -1:
+                    data[nvizType]['isosurface'][id][attrb] = { 'map' : useMap,
+                                                                'value' : str(value),
+                                                                'update' : None }
+                    if attrb == 'topo':
+                        list = self.FindWindowById(self.win['volume']['isosurfs'])
+                        sel = list.GetSelection()
+                        list.SetString(sel, "%s %s" % (_("Level"), str(value)))
+                        list.Check(sel)
+            
+            # update properties
+            event = wxUpdateProperties(data = data)
+            wx.PostEvent(self.mapWindow, event)
+            
+            if self.mapDisplay.IsAutoRendered():
+                self.mapWindow.Refresh(False)
+        
+    def OnSurfaceResolution(self, event):
+        """!Draw resolution changed"""
+        self.SetSurfaceResolution()
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+    
+
+    def SetSurfaceResolution(self):
+        """!Set draw resolution"""
+        coarse = self.FindWindowById(self.win['surface']['draw']['res-coarse']).GetValue()
+        fine = self.FindWindowById(self.win['surface']['draw']['res-fine']).GetValue()
+        
+        data = self.GetLayerData('surface')
+        data['surface']['draw']['resolution'] = { 'coarse' : coarse,
+                                                  'fine' : fine,
+                                                  'update' : None }
+        
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+    def SetSurfaceMode(self):
+        """!Set draw mode"""
+        mode = self.FindWindowById(self.win['surface']['draw']['mode']).GetSelection()
+        style = self.FindWindowById(self.win['surface']['draw']['style']).GetSelection()
+        if style == 0: # wire
+            self.FindWindowById(self.win['surface']['draw']['wire-color']).Enable(True)
+        elif style == 1: # surface
+            self.FindWindowById(self.win['surface']['draw']['wire-color']).Enable(False)
+        
+        shade = self.FindWindowById(self.win['surface']['draw']['shading']).GetSelection()
+        
+        value, desc = self.mapWindow.nvizDefault.GetDrawMode(mode, style, shade)
+        
+        return value, desc
+
+    def OnSurfaceMode(self, event):
+        """!Set draw mode"""
+        value, desc = self.SetSurfaceMode()
+        
+        data = self.GetLayerData('surface')
+        data['surface']['draw']['mode'] = { 'value' : value,
+                                            'desc' : desc,
+                                            'update' : None }
+        
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+
+    def OnSurfaceModeAll(self, event):
+        """!Set draw mode (including wire color) for all loaded surfaces"""
+        value, desc = self.SetSurfaceMode()
+        coarse = self.FindWindowById(self.win['surface']['draw']['res-coarse']).GetValue()
+        fine = self.FindWindowById(self.win['surface']['draw']['res-fine']).GetValue()
+        color = self.FindWindowById(self.win['surface']['draw']['wire-color']).GetColour()
+        cvalue = self._getColorString(color)
+        
+        for name in self.mapWindow.GetLayerNames(type = 'raster'):
+            
+            data = self.mapWindow.GetLayerByName(name, mapType = 'raster', dataType = 'nviz')
+            if not data:
+                continue # shouldy no happen
+            
+            data['surface']['draw']['all'] = True
+            data['surface']['draw']['mode'] = { 'value' : value,
+                                                'desc' : desc,
+                                                'update' : None }
+            data['surface']['draw']['resolution'] = { 'coarse' : coarse,
+                                                      'fine' : fine,
+                                                      'update' : None }
+            data['surface']['draw']['wire-color'] = { 'value' : cvalue,
+                                                      'update' : None }
+            
+            # update properties
+            event = wxUpdateProperties(data = data)
+            wx.PostEvent(self.mapWindow, event)
+            
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def _getColorString(self, color):
+        """!Convert color tuple to R:G:B format
+
+        @param color tuple
+        
+        @return string R:G:B
+        """
+        return str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
+    
+    def _getColorFromString(self, color, delim = ':'):
+        """!Convert color string (R:G:B) to wx.Color
+
+        @param color string
+        @param delim delimiter
+
+        @return wx.Color instance
+        """
+        return wx.Color(*map(int, color.split(delim)))
+    
+    def _get3dRange(self, name):
+        """!Gelper func for getting range of 3d map"""
+        ret = RunCommand('r3.info', read = True, flags = 'r', map = name)
+        if ret:
+            range = []
+            for value in ret.strip('\n').split('\n'):
+                range.append(float(value.split('=')[1]))
+            return range
+        
+        return -1e6, 1e6
+    
+    def _getPercent(self, value, toPercent = True):
+        """!Convert values 0 - 255 to percents and vice versa"""
+        value = int(value)
+        if toPercent:
+            value = int(value/255. * 100)
+        else:
+            value = int(value/100. * 255)
+        return value
+    
+    def OnSurfaceWireColor(self, event):
+        """!Set wire color"""
+        data = self.GetLayerData('surface')
+        value = self._getColorString(event.GetValue())
+        data['surface']['draw']['wire-color'] = { 'value' : value,
+                                                  'update' : None }
+        
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def OnSurfaceAxis(self, event):
+        """!Surface position, axis changed"""
+        data = self.GetLayerData('surface')
+        id = data['surface']['object']['id']
+        
+        axis = self.FindWindowById(self.win['surface']['position']['axis']).GetSelection()
+        slider = self.FindWindowById(self.win['surface']['position']['slider'])
+        text = self.FindWindowById(self.win['surface']['position']['text'])
+        xydim = self._display.GetLongDim()
+        zdim = self._display.GetZRange()
+        zdim = zdim[1] - zdim[0]
+        
+        x, y, z = self._display.GetSurfacePosition(id)
+        
+        if axis == 0: # x
+            slider.SetRange(-3 * xydim, 3 * xydim)
+            slider.SetValue(x)
+            text.SetValue(x)
+        elif axis == 1: # y
+            slider.SetRange(-3 * xydim, 3 * xydim)
+            slider.SetValue(y)
+            text.SetValue(y)
+        else: # z
+            slider.SetRange(-3 * zdim, 3 * zdim)
+            slider.SetValue(z)
+            text.SetValue(z)
+        
+    def OnSurfacePosition(self, event):
+        """!Surface position"""
+        winName = self.__GetWindowName(self.win['surface'], event.GetId())
+        if not winName:
+            return
+        axis = self.FindWindowById(self.win['surface']['position']['axis']).GetSelection()
+        
+        value = self.FindWindowById(event.GetId()).GetValue()
+        slider = self.FindWindowById(self.win['surface'][winName]['slider'])
+        self.AdjustSliderRange(slider = slider, value = value)
+        
+        for win in self.win['surface']['position'].itervalues():
+            if win in (self.win['surface']['position']['axis'],
+                       self.win['surface']['position']['reset']):
+                continue
+            else:
+                self.FindWindowById(win).SetValue(value)
+        
+        data = self.GetLayerData('surface')
+        id = data['surface']['object']['id']
+        x, y, z = self._display.GetSurfacePosition(id)
+        
+        if axis == 0: # x
+            x = value
+        elif axis == 1: # y
+            y = value
+        else: # z
+            z = value
+        
+        data['surface']['position']['x'] = x
+        data['surface']['position']['y'] = y
+        data['surface']['position']['z'] = z
+        data['surface']['position']['update'] = None
+        # update properties
+        
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        self.mapWindow.render['quick'] = True
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        #        self.UpdatePage('surface')
+        
+    def OnSurfacePositionChanged(self, event):
+        """!Surface position changed"""
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.Refresh(False)
+
+    def OnSurfacePositionText(self, event):
+        """!Surface position changed by textctrl"""
+        self.OnSurfacePosition(event)
+        self.OnSurfacePositionChanged(None)
+        
+    def UpdateVectorShow(self, vecType, enabled):
+        """!Enable/disable lines/points widgets
+        
+        @param vecType vector type (lines, points)
+        """
+        if vecType != 'lines' and vecType != 'points':
+            return False
+        
+        for win in self.win['vector'][vecType].keys():
+            if win == 'show':
+                continue
+            if type(self.win['vector'][vecType][win]) == type({}):
+                for swin in self.win['vector'][vecType][win].keys():
+                    if enabled:
+                        self.FindWindowById(self.win['vector'][vecType][win][swin]).Enable(True)
+                    else:
+                        self.FindWindowById(self.win['vector'][vecType][win][swin]).Enable(False)
+            else:
+                if enabled:
+                    self.FindWindowById(self.win['vector'][vecType][win]).Enable(True)
+                else:
+                    self.FindWindowById(self.win['vector'][vecType][win]).Enable(False)
+        
+        return True
+    
+    def OnVectorShow(self, event):
+        """!Show vector lines/points"""
+        winId = event.GetId()
+        if winId == self.win['vector']['lines']['show']:
+            vecType = 'lines'
+            points = False
+        else: # points
+            vecType = 'points' 
+            points = True
+       
+        checked = event.IsChecked()
+        name = self.FindWindowById(self.win['vector']['map']).GetValue()
+        item = self.mapWindow.GetLayerByName(name, mapType = 'vector', dataType = 'item')
+        data = self.GetLayerData('vector')['vector']
+        
+        if checked:
+            self.mapWindow.LoadVector(item, points = points, append = False)
+        else:
+            self.mapWindow.UnloadVector(item, points = points, remove = False)
+        
+        self.UpdateVectorShow(vecType, checked)
+        
+        if checked:
+            try:
+                id = data[vecType]['object']['id']
+            except KeyError:
+                id = -1
+            
+            if id > 0:
+                self.mapWindow.SetMapObjProperties(item, id, vecType)
+                
+                # update properties
+                event = wxUpdateProperties(data = data)
+                wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+        event.Skip()
+    
+    def OnVectorDisplay(self, event):
+        """!Display vector lines on surface/flat"""
+        rasters = self.mapWindow.GetLayerNames('raster')
+        if event.GetSelection() == 0: # surface
+            if len(rasters) < 1:
+                self.FindWindowById(self.win['vector']['lines']['surface']).Enable(False)
+                self.FindWindowById(self.win['vector']['lines']['flat']).SetSelection(1)
+                return
+            
+            self.FindWindowById(self.win['vector']['lines']['surface']).Enable(True)
+            # set first found surface
+            data = self.GetLayerData('vector')
+            data['vector']['lines']['mode']['surface'] = rasters[0]
+            self.FindWindowById(self.win['vector']['lines']['surface']).SetStringSelection( \
+                rasters[0])
+        else: # flat
+            self.FindWindowById(self.win['vector']['lines']['surface']).Enable(False)
+        
+        self.OnVectorLines(event)
+        
+        event.Skip()
+
+    def OnVectorLines(self, event):
+        """!Set vector lines mode, apply changes if auto-rendering is enabled"""
+        data = self.GetLayerData('vector')
+        width = self.FindWindowById(self.win['vector']['lines']['width']).GetValue()
+        
+        mode = {}
+        if self.FindWindowById(self.win['vector']['lines']['flat']).GetSelection() == 0:
+            mode['type'] = 'surface'
+            mode['surface'] = {}
+            checklist = self.FindWindowById(self.win['vector']['lines']['surface'])
+            value = list()
+            checked = list()
+            for surface in range(checklist.GetCount()):
+                value.append(checklist.GetString(surface))
+                checked.append(checklist.IsChecked(surface))
+                    
+            mode['surface']['value'] = value
+            mode['surface']['show'] = checked
+        else:
+            mode['type'] = 'flat'
+        
+        for attrb in ('width', 'mode'):
+            data['vector']['lines'][attrb]['update'] = None
+        data['vector']['lines']['width']['value'] = width
+        data['vector']['lines']['mode'] = mode
+        
+        color = self.FindWindowById(self.win['vector']['lines']['color']).GetColour()
+        
+        if isinstance(color, csel.ColourSelect):
+            pass #color picker not yet instantiated
+        else:
+            color = str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
+            data['vector']['lines']['color']['update'] = None
+            data['vector']['lines']['color']['value'] = color
+        
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+                        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def OnVectorHeight(self, event):
+        id = event.GetId()
+        if id in self.win['vector']['lines']['height'].values():
+            vtype = 'lines'
+        else:
+            vtype = 'points'
+        
+        value = self.FindWindowById(id).GetValue()
+        slider = self.FindWindowById(self.win['vector'][vtype]['height']['slider'])
+        self.AdjustSliderRange(slider = slider, value = value)
+        
+        for win in self.win['vector'][vtype]['height'].itervalues():
+            self.FindWindowById(win).SetValue(value)
+        
+        data = self.GetLayerData('vector')
+        data['vector'][vtype]['height'] = { 'value' : value,
+                                            'update' : None }
+        
+        # update properties
+        
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        self.mapWindow.render['quick'] = True
+        self.mapWindow.render['v' + vtype] = True
+        self.mapWindow.Refresh(False)
+        
+        event.Skip()
+    
+    def OnVectorHeightFull(self, event):
+        """!Vector height changed, render in full resolution"""
+        self.OnVectorHeight(event)
+##        self.OnVectorSurface(event)
+        id = event.GetId()
+        if id in self.win['vector']['lines']['height'].values():
+            vtype = 'lines'
+        else:
+            vtype = 'points'
+        
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.render['v' + vtype] = False
+        self.mapWindow.Refresh(False)
+
+    def OnVectorHeightText(self, event):
+        """!Vector height changed, render in full resolution"""
+        
+        #        self.OnVectorHeight(event)
+        self.OnVectorHeightFull(event)
+    
+    def OnVectorSurface(self, event):
+        """!Reference surface for vector map (lines/points)"""   
+        id = event.GetId()
+        if id == self.win['vector']['lines']['surface']:
+            vtype = 'lines'
+        else:
+            vtype = 'points'
+        checkList = self.FindWindowById(self.win['vector'][vtype]['surface'])
+        checked = []
+        surfaces = []
+        for items in range(checkList.GetCount()):
+            checked.append(checkList.IsChecked(items))
+            surfaces.append(checkList.GetString(items))
+        
+        data = self.GetLayerData('vector')
+        data['vector'][vtype]['mode']['surface'] = { 'value' : surfaces,
+                                                     'show'  : checked}
+        data['vector'][vtype]['mode']['update'] = None 
+        
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+            
+        
+    def OnVectorPoints(self, event):
+        """!Set vector points mode, apply changes if auto-rendering is enabled"""
+        data = self.GetLayerData('vector')
+        
+        size  = self.FindWindowById(self.win['vector']['points']['size']).GetValue()
+        marker = self.FindWindowById(self.win['vector']['points']['marker']).GetSelection()
+        #        width = self.FindWindowById(self.win['vector']['points']['width']).GetValue()
+        
+        for attrb in ('size', 'marker'):
+            data['vector']['points'][attrb]['update'] = None
+        data['vector']['points']['size']['value'] = size
+        #        data['vector']['points']['width']['value'] = width
+        data['vector']['points']['marker']['value'] = marker
+        
+        color = self.FindWindowById(self.win['vector']['points']['color']).GetColour()
+        if isinstance(color, csel.ColourSelect):
+            pass #color picker not yet instantiated
+        else:
+            color = str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
+            data['vector']['points']['color']['update'] = None
+            data['vector']['points']['color']['value'] = color
+        
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+        
+    def UpdateIsosurfButtons(self, list):
+        """!Enable/disable buttons 'add', 'delete',
+        'move up', 'move down'"""
+        nitems = list.GetCount()
+        add = self.parent.FindWindowById(self.win['volume']['btnAdd'])
+        delete = self.parent.FindWindowById(self.win['volume']['btnDelete'])
+        moveDown = self.parent.FindWindowById(self.win['volume']['btnMoveDown'])
+        moveUp = self.parent.FindWindowById(self.win['volume']['btnMoveUp'])
+        if nitems >= wxnviz.MAX_ISOSURFS:
+            # disable add button on max
+            add.Enable(False)
+        else:
+            add.Enable(True)
+        
+        if nitems < 1:
+            # disable 'delete' if only one item in the lis
+            delete.Enable(False)
+        else:
+            delete.Enable(True)
+        
+        if list.GetSelection() >= nitems - 1:
+            # disable 'move-down' if last
+            moveDown.Enable(False)
+        else:
+            moveDown.Enable(True)
+        
+        if list.GetSelection() < 1:
+            # disable 'move-up' if first
+            moveUp.Enable(False)
+        else:
+            moveUp.Enable(True)
+            
+    def OnVolumeMode(self, event):
+        """!Change mode isosurfaces/slices"""
+        mode = self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection()
+        data = self.GetLayerData('volume')['volume']
+        
+        sizer = self.isoPanel.GetContainingSizer()
+        sizer = self.slicePanel.GetContainingSizer()
+        listBox = self.FindWindowByName('listStaticBox')
+        if mode == 0:
+            sizer.Show(self.isoPanel)
+            sizer.Hide(self.slicePanel)
+            listBox.SetLabel(" %s " % _("List of isosurfaces"))
+            data['draw']['mode']['value'] = 0
+            data['draw']['mode']['desc'] = 'isosurface'
+        else:
+            sizer.Hide(self.isoPanel)
+            sizer.Show(self.slicePanel)
+            listBox.SetLabel(" %s " % _("List of slices"))
+            data['draw']['mode']['value'] = 1
+            data['draw']['mode']['desc'] = 'slice'
+        
+        if event:
+            name = self.FindWindowById(self.win['volume']['map']).GetValue()
+            layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+            self.UpdateVolumePage(layer, data, updateName = False)
+            
+        sizer.Layout()
+        listBox.GetParent().Fit()
+            
+    def OnVolumeDrawMode(self, event):
+        """!Set isosurface/slice draw mode"""
+        self.SetVolumeDrawMode(event.GetSelection())
+        
+    def SetVolumeDrawMode(self, selection):
+        """!Set isosurface draw mode"""
+        data = self.GetLayerData('volume')['volume']
+        id = data['object']['id']
+        
+        mode = 0
+        if selection == 0:
+            mode |= wxnviz.DM_FLAT
+        else:
+            mode |= wxnviz.DM_GOURAUD
+            
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            self._display.SetIsosurfaceMode(id, mode)
+            data['draw']['shading']['isosurface']['desc'] = 'gouraud'
+            data['draw']['shading']['isosurface']['value'] = mode
+        else:
+            self._display.SetSliceMode(id, mode)
+            data['draw']['shading']['slice']['desc'] = 'flat'
+            data['draw']['shading']['slice']['value'] = mode
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def OnVolumeResolution(self, event):
+        """!Set isosurface/slice draw resolution"""
+        self.SetVolumeResolution(event.GetInt())
+        
+    def SetVolumeResolution(self, res):
+        """!Set isosurface draw resolution"""
+        data = self.GetLayerData('volume')['volume']
+        id = data['object']['id']
+        
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            self._display.SetIsosurfaceRes(id, res)
+            data['draw']['resolution']['isosurface']['value'] = res
+        else:
+            self._display.SetSliceRes(id, res)
+            data['draw']['resolution']['slice']['value'] = res
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+    
+    def OnInOutMode(self, event):
+        """!Change isosurfaces mode inout"""
+        data = self.GetLayerData('volume')['volume']
+        id = data['object']['id']
+        isosurfId = self.FindWindowById(self.win['volume']['isosurfs']).GetSelection()
+        
+        ret = self._display.SetIsosurfaceInOut(id, isosurfId, event.GetInt())
+        if ret == 1:
+            data['isosurface'][isosurfId]['inout'] = event.GetInt()
+            
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+    
+        
+    def OnVolumeIsosurfMap(self, event):
+        """!Set surface attribute"""
+        if self.vetoGSelectEvt:
+            self.vetoGSelectEvt = False
+            return
+        self.SetMapObjAttrb(nvizType = 'volume', winId = event.GetId())
+        
+    def OnVolumeCheck(self, event):
+        """!Isosurface/slice checked (->load) or unchecked (->unload)"""
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            mode = 'isosurf'
+        else:
+            mode = 'slice'
+        index = event.GetSelection()
+        list = self.FindWindowById(self.win['volume'][mode + 's'])
+        
+        data = self.GetLayerData('volume')['volume']
+        vid = data['object']['id']
+        
+        id = event.GetSelection()
+        
+        if mode == 'isosurf':
+            if list.IsChecked(index):
+                if 'transp' in data['isosurface'][id] and\
+                    data['isosurface'][id]['transp']['map'] is not None:
+                    if data['isosurface'][id]['transp']['map']:
+                        map = True
+                        value = data['isosurface'][id]['transp']['value']
+                    elif data['isosurface'][id]['transp']['map'] is not None:
+                        map = False
+                        value = data['isosurface'][id]['transp']['value']
+                    self._display.SetIsosurfaceTransp(vid, id, map, value)
+                else:
+                    self._display.SetIsosurfaceTransp(vid, id, False, "0")
+            else:
+                # disable -> make transparent
+                self._display.SetIsosurfaceTransp(vid, id, False, "255")
+        else:
+            if list.IsChecked(index):
+                value = data['slice'][id]['transp']['value']
+                self._display.SetSliceTransp(vid, id, value)
+            else:
+                # disable -> make transparent
+                self._display.SetSliceTransp(vid, id, 255)
+                
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def OnVolumeSelect(self, event):
+        """!Isosurface/Slice item selected"""
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            mode = 'isosurf'
+        else:
+            mode = 'slice'
+            
+        winUp = self.FindWindowById(self.win['volume']['btnMoveUp'])
+        winDown = self.FindWindowById(self.win['volume']['btnMoveDown'])
+        selection = event.GetSelection()
+        if selection == -1:
+            return
+        elif selection == 0:
+            winUp.Enable(False)
+            if not winDown.IsEnabled():
+                winDown.Enable()
+        elif selection == self.FindWindowById(event.GetId()).GetCount() - 1:
+            winDown.Enable(False)
+            if not winUp.IsEnabled():
+                winUp.Enable()
+        else:
+            if not winDown.IsEnabled():
+                winDown.Enable()
+            if not winUp.IsEnabled():
+                winUp.Enable()
+        
+        # update dialog
+        name = self.FindWindowById(self.win['volume']['map']).GetValue()
+        layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+        
+        if mode == 'isosurf':
+            data = self.GetLayerData('volume')['volume']['isosurface'][selection]
+            self.UpdateVolumeIsosurfPage(data)
+        else:
+            data = self.GetLayerData('volume')['volume']['slice'][selection]
+            self.UpdateVolumeSlicePage(data)
+        
+        
+        
+    def OnVolumeAdd(self, event):
+        """!Add new isosurface/slice to the list"""
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            mode = 'isosurf'
+        else:
+            mode = 'slice'
+        list = self.FindWindowById(self.win['volume'][mode + 's'])
+        
+        name = self.FindWindowById(self.win['volume']['map']).GetValue()
+        layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+        data = self.GetLayerData('volume')['volume']
+        id = data['object']['id']
+        
+        sel = list.GetSelection()
+        if mode == 'isosurf':
+            isosurfData = self.mapWindow.nvizDefault.SetIsosurfaceDefaultProp()
+            if isosurfData['color']['map']:
+                isosurfData['color']['value'] = layer.name
+
+            level = isosurfData['topo']['value'] = round(self._get3dRange(name = layer.name)[0], 2)
+        
+            if sel < 0 or sel >= list.GetCount() - 1:
+                item = list.Append(item = "%s %s" % (_("Level"), str(level)))
+            else:
+                list.Insert(item = "%s %s" % (_("Level"), str(level)),
+                            pos = sel+1) # append
+                item = sel + 1
+        else:
+            sliceData = self.mapWindow.nvizDefault.SetSliceDefaultProp()
+            axis = ("X", "Y", "Z")[sliceData['position']['axis']]
+            if sel < 0 or sel >= list.GetCount() - 1:
+                item = list.Append(item = "%s %s" % (_("Slice parallel to"), axis))
+            else:
+                list.Insert(item = "%s" % (_("Slice parallel to"), axis),
+                            pos = sel+1) # append
+                item = sel + 1
+        
+        list.Check(item)
+        list.SetSelection(item)
+        
+        if mode == 'isosurf':
+            data['isosurface'].insert(item, isosurfData)
+            # add isosurface        
+            self._display.AddIsosurface(id, float(level))
+        else:
+            data['slice'].insert(item, sliceData)
+            # add isosurface        
+            nslice = self._display.AddSlice(id)
+            self._display.SetSlicePosition(id, nslice -1, sliceData['position']['x1'], sliceData['position']['x2'],
+                                               sliceData['position']['y1'], sliceData['position']['y2'],
+                                               sliceData['position']['z1'], sliceData['position']['z2'],
+                                               sliceData['position']['axis'])
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        # update buttons
+        self.UpdateIsosurfButtons(list)
+        if mode == 'isosurf':
+            self.UpdateVolumeIsosurfPage(isosurfData)
+        else:
+            self.UpdateVolumeSlicePage(sliceData)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+        event.Skip()
+        
+    def OnVolumeDelete(self, event):
+        """!Remove isosurface/slice from list"""
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            mode = 'isosurf'
+        else:
+            mode = 'slice'
+        list = self.FindWindowById(self.win['volume'][mode + 's'])
+        
+        # remove item from list
+        id = list.GetSelection()
+        list.Delete(id)
+        # select last item
+        if list.GetCount() > 0:
+            list.SetSelection(list.GetCount()-1)
+        
+        name = self.FindWindowById(self.win['volume']['map']).GetValue()
+        layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+        data = self.GetLayerData('volume')['volume']
+
+        vid = data['object']['id']
+        
+        # delete isosurface
+        if mode == 'isosurf':
+            del data['isosurface'][id]
+            self._display.DeleteIsosurface(vid, id)
+        else:
+            del data['slice'][id]
+            self._display.DeleteSlice(vid, id)
+        
+        # update buttons
+        if list.GetCount() > 0:
+            if mode == 'isosurf':
+                self.UpdateVolumeIsosurfPage(data['isosurface'][list.GetSelection()])
+            else:
+                self.UpdateVolumeSlicePage(data['slice'][list.GetSelection()])
+        else:
+            if mode == 'isosurf':
+                self.UpdateVolumeIsosurfPage(data['attribute'])
+            else:
+                self.UpdateVolumeSlicePage(None)
+        self.UpdateIsosurfButtons(list)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+        event.Skip()
+        
+    def OnVolumeMoveUp(self, event):
+        """!Move isosurface/slice up in the list"""
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            mode = 'isosurf'
+        else:
+            mode = 'slice'
+        list = self.FindWindowById(self.win['volume'][mode + 's'])
+        sel = list.GetSelection()
+        
+        if sel < 1:
+            return # this should not happen
+        
+        name = self.FindWindowById(self.win['volume']['map']).GetValue()
+        layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+        data = self.GetLayerData('volume')['volume']
+        
+        id = data['object']['id']
+        
+        # move item up
+        text = list.GetStringSelection()
+        list.Insert(item = text, pos = sel-1)
+        list.Check(sel-1)
+        list.SetSelection(sel-1)
+        list.Delete(sel+1)
+        if mode == 'isosurf':
+            data['isosurface'].insert(sel-1, data['isosurface'][sel])
+            del data['isosurface'][sel+1]
+            self._display.MoveIsosurface(id, sel, True)
+        else:
+            data['slice'].insert(sel-1, data['slice'][sel])
+            del data['slice'][sel+1]
+            self._display.MoveSlice(id, sel, True)
+        
+        # update buttons
+        self.UpdateIsosurfButtons(list)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+        event.Skip()
+        
+    def OnVolumeMoveDown(self, event):
+        """!Move isosurface/slice down in the list"""
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            mode = 'isosurf'
+        else:
+            mode = 'slice'
+        list = self.FindWindowById(self.win['volume'][mode + 's'])
+        sel = list.GetSelection()
+        
+        if sel >= list.GetCount() - 1:
+            return # this should not happen
+        
+        name = self.FindWindowById(self.win['volume']['map']).GetValue()
+        layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+        data = self.GetLayerData('volume')['volume']
+        
+        id = data['object']['id']
+        
+        # move item up
+        text = list.GetStringSelection()
+        list.Insert(item = text, pos = sel+2)
+        list.Check(sel+2)
+        list.SetSelection(sel+2)
+        list.Delete(sel)
+        if mode == 'isosurf':
+            data['isosurface'].insert(sel+2, data['isosurface'][sel])
+            del data['isosurface'][sel]
+            self._display.MoveIsosurface(id, sel, False)
+        else:
+            data['slice'].insert(sel+2, data['slice'][sel])
+            del data['slice'][sel]
+            self._display.MoveSlice(id, sel, False)
+        
+        # update buttons
+        self.UpdateIsosurfButtons(list)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+        event.Skip()
+    
+    def OnVolumePositionChanged(self, event):
+        """!Volume position changed"""
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.Refresh(False)
+        
+    def OnVolumePosition(self, event):
+        """!Volume position"""
+        winName = self.__GetWindowName(self.win['volume'], event.GetId())
+        if not winName:
+            return
+        axis = self.FindWindowById(self.win['volume']['position']['axis']).GetSelection()
+        
+        value = self.FindWindowById(event.GetId()).GetValue()
+        slider = self.FindWindowById(self.win['volume'][winName]['slider'])
+        self.AdjustSliderRange(slider = slider, value = value)
+        
+        for win in self.win['volume']['position'].itervalues():
+            if win in (self.win['volume']['position']['axis'],
+                       self.win['volume']['position']['reset']):
+                continue
+            else:
+                self.FindWindowById(win).SetValue(value)
+        
+        data = self.GetLayerData('volume')
+        id = data['volume']['object']['id']
+        x, y, z = self._display.GetVolumePosition(id)
+        
+        if axis == 0: # x
+            x = value
+        elif axis == 1: # y
+            y = value
+        else: # z
+            z = value
+        
+        data['volume']['position']['x'] = x
+        data['volume']['position']['y'] = y
+        data['volume']['position']['z'] = z
+        data['volume']['position']['update'] = None
+        # update properties
+        
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        self.mapWindow.render['quick'] = True
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def OnVolumeAxis(self, event):
+        """!Volume position, axis changed"""
+        data = self.GetLayerData('volume')
+        id = data['volume']['object']['id']
+        
+        axis = self.FindWindowById(self.win['volume']['position']['axis']).GetSelection()
+        slider = self.FindWindowById(self.win['volume']['position']['slider'])
+        text = self.FindWindowById(self.win['volume']['position']['text'])
+        xydim = self._display.GetLongDim()
+        zdim = self._display.GetZRange()
+        zdim = zdim[1] - zdim[0]
+        x, y, z = self._display.GetVolumePosition(id)
+        
+        if axis == 0: # x
+            slider.SetRange(-3 * xydim, 3 * xydim)
+            slider.SetValue(x)
+            text.SetValue(x)
+        elif axis == 1: # y
+            slider.SetRange(-3 * xydim, 3 * xydim)
+            slider.SetValue(y)
+            text.SetValue(y)
+        else: # z
+            slider.SetRange(-3 * zdim, 3 * zdim)
+            slider.SetValue(z)
+            text.SetValue(z)
+            
+    def OnVolumePositionText(self, event):
+        """!Volume position changed by textctrl"""
+        self.OnVolumePosition(event)
+        self.OnVolumePositionChanged(None)
+        
+    def OnResetVolumePosition(self, event):
+        """!Reset position of volume"""
+        for win in self.win['volume']['position'].itervalues():
+            if win == self.win['volume']['position']['axis']:
+                self.FindWindowById(win).SetSelection(0)
+            elif win == self.win['volume']['position']['reset']:
+                continue
+            else:
+                self.FindWindowById(win).SetValue(0)
+                
+        data = self.GetLayerData('volume')
+        data['volume']['position']['x'] = 0
+        data['volume']['position']['y'] = 0
+        data['volume']['position']['z'] = 0
+        data['volume']['position']['update'] = None
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def OnVolumeSliceAxes(self, event):
+        """!Slice axis changed"""
+        self.UpdateSliceLabels()
+        data = self.GetLayerData('volume')
+        list = self.FindWindowById(self.win['volume']['slices'])
+        sel = list.GetSelection()
+        if sel < 0:
+            return
+        axis = self.FindWindowById(self.win['volume']['slice']['axes']).GetSelection()
+        data['volume']['slice'][sel]['position']['axis'] = axis
+        data['volume']['slice'][sel]['position']['update'] = None
+        
+        axis = ("X", "Y", "Z")[axis]
+        list.SetString(sel, "%s %s" % (_("Slice parallel to"), axis))
+        list.Check(sel)
+        
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event) 
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+    
+    def OnSliceTransparency(self, event):
+        """!Slice transparency changed"""
+        data = self.GetLayerData('volume')
+        
+        list = self.FindWindowById(self.win['volume']['slices'])
+        sel = list.GetSelection()
+        if sel < 0:
+            return
+        
+        val = self.FindWindowById(self.win['volume']['slice']['transp']).GetValue()
+        data['volume']['slice'][sel]['transp']['value'] = self._getPercent(val, toPercent = False)
+        data['volume']['slice'][sel]['transp']['update'] = None
+        
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def OnSliceReset(self, event):
+        """!Slice position reset"""
+        data = self.GetLayerData('volume')
+        
+        list = self.FindWindowById(self.win['volume']['slices'])
+        sel = list.GetSelection()
+        if sel < 0:
+            return
+        
+        for coord, val in zip(('x1', 'x2', 'y1', 'y2', 'z1', 'z2'),(0, 1, 0, 1, 0, 1, 0)):
+            data['volume']['slice'][sel]['position'][coord] = val
+        data['volume']['slice'][sel]['position']['update'] = None
+        
+        self.UpdateVolumeSlicePage(data['volume']['slice'][sel])
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def OnSlicePositionChange(self, event):
+        """!Slice position is changing"""
+        data = self.GetLayerData('volume')
+        list = self.FindWindowById(self.win['volume']['slices'])
+        sel = list.GetSelection()
+        if sel < 0:
+            return
+        win = self.win['volume']['slice']
+        winId = event.GetId()
+        value = event.GetInt()/100.
+        
+        for coord in ('x1', 'x2', 'y1', 'y2', 'z1', 'z2'):
+            if win['slider_' + coord] == winId:
+                data['volume']['slice'][sel]['position'][coord] = value
+                data['volume']['slice'][sel]['position']['update'] = None
+                break
+        self.mapWindow.render['quick'] = True
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event) 
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+               
+    def OnSlicePositionChanged(self, event):
+        """!Slice position is changed"""
+        self.mapWindow.render['quick'] = False
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+                
+    def OnCPlaneSelection(self, event):
+        """!Cutting plane selected"""
+        plane = self.FindWindowById(self.win['cplane']['planes']).GetStringSelection()
+        try:
+            planeIndex = int(plane.split()[-1]) - 1
+            self.EnablePage("cplane", enabled = True)
+        except:
+            planeIndex = -1
+            self.EnablePage("cplane", enabled = False)
+        self.mapWindow.SelectCPlane(planeIndex)
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        self.UpdateCPlanePage(planeIndex)
+        
+    def OnCPlaneChanging(self, event):
+        """!Cutting plane is changing"""
+        plane = self.FindWindowById(self.win['cplane']['planes']).GetStringSelection()
+        try:
+            planeIndex = int(plane.split()[-1]) - 1
+        except:#TODO disabled page
+            planeIndex = -1
+    
+        if event.GetId() in (self.win['cplane']['rotation']['rot'].values() +
+                            self.win['cplane']['rotation']['tilt'].values()):
+            action = 'rotation'
+        else:
+            action = 'position'
+        data = self.mapWindow.cplanes[planeIndex][action]
+        self.OnScroll(event, self.win['cplane'][action], data)
+        
+        self.mapWindow.render['quick'] = True
+        event = wxUpdateCPlane(update = (action,), current = planeIndex)
+        wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+
+    def OnCPlaneChangeDone(self, event):
+        """!Cutting plane change done"""
+        self.mapWindow.render['quick'] = False
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+            
+    def OnCPlaneChangeText(self, event):
+        """!Cutting plane changed by textctrl"""
+        for axis in ('x', 'y', 'z'):
+            if event.GetId() == self.win['cplane']['position'][axis]['text']:
+                value = self.FindWindowById(event.GetId()).GetValue()
+                slider = self.FindWindowById(self.win['cplane']['position'][axis]['slider'])
+                self.AdjustSliderRange(slider = slider, value = value)
+        self.OnCPlaneChanging(event = event)
+        self.OnCPlaneChangeDone(None)   
+        
+    def OnCPlaneShading(self, event):
+        """!Cutting plane shading changed"""
+        shading = self.FindWindowById(self.win['cplane']['shading']).GetSelection()
+        plane = self.FindWindowById(self.win['cplane']['planes']).GetStringSelection()
+        try:
+            planeIndex = int(plane.split()[-1]) - 1
+        except:#TODO disabled page
+            planeIndex = -1
+            
+        self.mapWindow.cplanes[planeIndex]['shading'] = shading
+        
+        event = wxUpdateCPlane(update = ('shading',), current = planeIndex)
+        wx.PostEvent(self.mapWindow, event)
+        
+        self.OnCPlaneChangeDone(None)
+        
+    def OnCPlaneReset(self, event):
+        """!Reset current cutting plane"""
+        plane = self.FindWindowById(self.win['cplane']['planes']).GetStringSelection()
+        try:
+            planeIndex = int(plane.split()[-1]) - 1
+        except:#TODO disabled page
+            planeIndex = -1
+        self.mapWindow.cplanes[planeIndex] = copy.deepcopy(UserSettings.Get(group = 'nviz',
+                                                                            key = 'cplane'))
+        event = wxUpdateCPlane(update = ('position','rotation','shading'), current = planeIndex)
+        wx.PostEvent(self.mapWindow, event)
+        self.OnCPlaneChangeDone(None)
+        self.UpdateCPlanePage(planeIndex)
+    
+    def OnDecorationPlacement(self, event):
+        """!Place an arrow/scalebar by clicking on display"""
+        if event.GetId() == self.win['decoration']['arrow']['place']:
+            type = 'arrow'
+        elif event.GetId() == self.win['decoration']['scalebar']['place']:
+            type = 'scalebar'
+        else: return
+        
+        if event.GetInt():
+            self.mapDisplay.Raise()
+            self.mapWindow.mouse['use'] = type
+            self.mapWindow.SetCursor(self.mapWindow.cursors["cross"])
+        else:
+            self.mapWindow.mouse['use'] = 'default'
+            self.mapWindow.SetCursor(self.mapWindow.cursors["default"])
+    
+    def OnArrowDelete(self, event):
+        """!Delete arrow"""
+        self._display.DeleteArrow()
+        self.mapWindow.decoration['arrow']['show'] = False
+        self.mapWindow.Refresh(False)
+    
+    def OnScalebarDelete(self, event):
+        """!Delete scalebar"""
+        try:
+            id = self.mapWindow.decoration['scalebar'][-1]['id']
+        except IndexError:
+            return
+        self._display.DeleteScalebar(id = id)
+        del self.mapWindow.decoration['scalebar'][-1]
+        
+        self.mapWindow.Refresh(False)   
+         
+    def OnDecorationProp(self, event):
+        """!Set arrow/scalebar properties"""
+        if event.GetId() in self.win['decoration']['arrow'].values():
+            type = 'arrow'
+        elif event.GetId() in self.win['decoration']['scalebar'].values():
+            type = 'scalebar'
+        else: return
+        
+        color = self.FindWindowById(self.win['decoration'][type]['color']).GetValue()
+        size = self.FindWindowById(self.win['decoration'][type]['size']).GetValue()
+        if type == 'arrow':
+            self.mapWindow.decoration[type]['color'] = self._getColorString(color)
+            self.mapWindow.decoration[type]['size'] = size
+        elif type == 'scalebar'and self.mapWindow.decoration['scalebar']:
+            self.mapWindow.decoration[type][-1]['color'] = self._getColorString(color)
+            self.mapWindow.decoration[type][-1]['size'] = size
+        
+        if type == 'arrow' and self.mapWindow.decoration['arrow']['show']:
+            self._display.SetArrow(self.mapWindow.decoration['arrow']['position']['x'],
+                                   self.mapWindow.decoration['arrow']['position']['y'],
+                                   self.mapWindow.decoration['arrow']['size'],
+                                   self.mapWindow.decoration['arrow']['color'])
+            self._display.DrawArrow()
+        elif type == 'scalebar' and self.mapWindow.decoration['scalebar']:
+            self._display.SetScalebar(self.mapWindow.decoration['scalebar'][-1]['id'],
+                                      self.mapWindow.decoration['scalebar'][-1]['position']['x'],
+                                      self.mapWindow.decoration['scalebar'][-1]['position']['y'],
+                                      self.mapWindow.decoration['scalebar'][-1]['size'],
+                                      self.mapWindow.decoration['scalebar'][-1]['color'])
+            self._display.DrawScalebar()
+            self.mapWindow.Refresh(False)
+        
+    def UpdatePage(self, pageId):
+        """!Update dialog (selected page)"""
+        self.pageChanging = True
+        Debug.msg(1, "NvizToolWindow.UpdatePage(): %s", pageId)
+        
+        if pageId == 'view':
+            self.SetPage('view')
+            hmin = self.mapWindow.iview['height']['min']
+            hmax = self.mapWindow.iview['height']['max']
+            hval = self.mapWindow.iview['height']['value']
+            zmin = self.mapWindow.view['z-exag']['min']
+            zmax = self.mapWindow.view['z-exag']['max']
+            zval = self.mapWindow.view['z-exag']['value']
+            
+            for control in ('slider','text'):
+                self.FindWindowById(self.win['view']['height'][control]).SetRange(
+                                                                        hmin,hmax)
+                self.FindWindowById(self.win['view']['z-exag'][control]).SetRange(
+                                                                        zmin, zmax) 
+                self.FindWindowById(self.win['view']['height'][control]).SetValue(hval)                                      
+                
+                self.FindWindowById(self.win['view']['z-exag'][control]).SetValue(zval)                                      
+        
+            self.FindWindowById(self.win['view']['background']['color']).SetColour(\
+                            self.mapWindow.view['background']['color'])
+                            
+            tval = self.mapWindow.view['twist']['value']
+            pval = self.mapWindow.view['persp']['value']
+            for control in ('slider','text'):
+                self.FindWindowById(self.win['view']['twist'][control]).SetValue(tval)                                      
+                
+                self.FindWindowById(self.win['view']['persp'][control]).SetValue(pval)
+            
+            
+        elif pageId in ('surface', 'vector', 'volume'):
+            name = self.FindWindowById(self.win[pageId]['map']).GetValue()
+            data = self.GetLayerData(pageId)
+            if data:
+                if pageId == 'surface':
+                    layer = self.mapWindow.GetLayerByName(name, mapType = 'raster')
+                    self.UpdateSurfacePage(layer, data['surface'])
+                elif pageId == 'vector':
+                    layer = self.mapWindow.GetLayerByName(name, mapType = 'vector')
+                    self.UpdateVectorPage(layer, data['vector'])
+                elif pageId == 'volume':
+                    layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+                    self.UpdateVolumePage(layer, data['volume'])
+        elif pageId == 'light':
+            zval = self.mapWindow.light['position']['z']
+            bval = self.mapWindow.light['bright']
+            aval = self.mapWindow.light['ambient']
+            for control in ('slider','text'):
+                self.FindWindowById(self.win['light']['z'][control]).SetValue(zval)
+                self.FindWindowById(self.win['light']['bright'][control]).SetValue(bval)
+                self.FindWindowById(self.win['light']['ambient'][control]).SetValue(aval)
+            self.FindWindowById(self.win['light']['color']).SetColour(self.mapWindow.light['color'])
+            self.FindWindowById(self.win['light']['position']).PostDraw()
+        elif pageId == 'fringe':
+            win = self.FindWindowById(self.win['fringe']['map'])
+            win.SetValue(self.FindWindowById(self.win['surface']['map']).GetValue())
+        elif pageId == 'decoration':
+            win = self.FindWindowById(self.win['decoration']['arrow']['size'])
+            win.SetValue(self.mapWindow.decoration['arrow']['size'])
+            win = self.FindWindowById(self.win['decoration']['scalebar']['size'])
+            win.SetValue(self.mapWindow._getDecorationSize())
+        elif pageId == 'constant':
+            if self.mapWindow.constants:
+                surface = self.FindWindowById(self.win['constant']['surface'])
+                for item in self.mapWindow.constants:
+                    surface.Append(_("constant#") + str(item['constant']['object']['name']))
+                surface.SetSelection(0)
+                self.OnConstantSelection(None)
+                self.EnablePage('constant', True)
+        elif pageId == 'cplane':
+            count = self._display.GetCPlanesCount()
+            choices = [_("None"),]
+            for plane in range(count):
+                choices.append("%s %i" % (_("Plane"), plane+1))
+            self.FindWindowById(self.win['cplane']['planes']).SetItems(choices)
+            current = 0
+            for i, cplane in enumerate(self.mapWindow.cplanes):
+                if cplane['on']:
+                    current = i + 1
+            self.FindWindowById(self.win['cplane']['planes']).SetSelection(current)
+            
+            xyRange, zRange = self._display.GetXYRange(), self._display.GetZRange()
+            if xyRange > 0: # GTK warning
+                self.FindWindowById(self.win['cplane']['position']['x']['slider']).SetRange(
+                                                                    -xyRange/2., xyRange/2.)
+                self.FindWindowById(self.win['cplane']['position']['y']['slider']).SetRange(
+                                                                    -xyRange/2., xyRange/2.)
+            if zRange[0] - zRange[1] > 0:
+                self.FindWindowById(self.win['cplane']['position']['z']['slider']).SetRange(zRange[0], zRange[1])
+            self.FindWindowById(self.win['cplane']['position']['z']['slider']).SetValue(zRange[0])
+            self.FindWindowById(self.win['cplane']['position']['z']['text']).SetValue(zRange[0])
+            self.OnCPlaneSelection(None)
+            
+        elif pageId == 'animation':
+            self.UpdateAnimationPage()
+            
+        self.Update()
+        self.pageChanging = False
+        
+    def UpdateAnimationPage(self):
+        """!Update animation page"""
+        # wrap help text according to tool window
+        help = self.FindWindowById(self.win['anim']['help'])
+        width = help.GetGrandParent().GetSizeTuple()[0]
+        help.Wrap(width - 15)
+        anim = self.mapWindow.GetAnimation()
+        if anim.Exists():
+            self.FindWindowById(self.win['anim']['play']).Enable()
+        else:
+            self.UpdateFrameIndex(index = 0)
+            
+        self.UpdateFrameCount()
+        
+        self.FindWindowById(self.win['anim']['play']).Disable()
+        self.FindWindowById(self.win['anim']['record']).Enable()
+        self.FindWindowById(self.win['anim']['pause']).Disable()
+        self.FindWindowById(self.win['anim']['stop']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+        
+    def UpdateCPlanePage(self, index):
+        """!Update widgets according to selected clip plane"""
+        if index == -1:   
+            return
+        data = self.mapWindow.cplanes[index]
+        for widget in ('text', 'slider'):
+            for axes in ('x', 'y', 'z'):
+                self.FindWindowById(self.win['cplane']['position'][axes][widget]).SetValue(data['position'][axes])
+            for each in ('tilt', 'rot'):
+                self.FindWindowById(self.win['cplane']['rotation'][each][widget]).SetValue(data['rotation'][each])
+        self.FindWindowById(self.win['cplane']['shading']).SetSelection(data['shading'])
+                
+    def UpdateSurfacePage(self, layer, data, updateName = True):
+        """!Update surface page"""
+        desc = grass.raster_info(layer.name)['title']
+        if updateName:
+            self.FindWindowById(self.win['surface']['map']).SetValue(layer.name)
+        self.FindWindowById(self.win['surface']['desc']).SetLabel(desc)
+        
+        # attributes
+        if layer and layer.type == 'raster':
+            self.vetoGSelectEvt = True
+            self.FindWindowById(self.win['surface']['color']['map']).SetValue(layer.name)
+        else:
+            self.FindWindowById(self.win['surface']['color']['map']).SetValue('')
+
+        self.SetMapObjUseMap(nvizType = 'surface',
+                             attrb = 'color', map = True) # -> map
+                                
+        if 'color' in data['attribute']:
+            value = data['attribute']['color']['value']
+
+            if data['attribute']['color']['map']:
+                self.FindWindowById(self.win['surface']['color']['map']).SetValue(value)
+            else: # constant
+                color = map(int, value.split(':'))
+                self.FindWindowById(self.win['surface']['color']['const']).SetColour(color)
+            self.SetMapObjUseMap(nvizType = 'surface',
+                                 attrb = 'color', map = data['attribute']['color']['map'])
+
+        self.SetMapObjUseMap(nvizType = 'surface',
+                             attrb = 'shine', map = data['attribute']['shine']['map'])
+        value = data['attribute']['shine']['value']
+        if data['attribute']['shine']['map']:
+            self.FindWindowById(self.win['surface']['shine']['map']).SetValue(value)
+        else:
+            self.FindWindowById(self.win['surface']['shine']['const']).SetValue(self._getPercent(value))
+        if 'transp' in data['attribute']:  
+            value = data['attribute']['transp']['value']  
+            if data['attribute']['transp']['map']:
+                self.FindWindowById(self.win['surface']['color']['map']).SetValue(value)
+            else:
+                self.FindWindowById(self.win['surface']['transp']['const']).SetValue(self._getPercent(value))
+            self.SetMapObjUseMap(nvizType = 'surface', attrb = 'transp', map = data['attribute']['transp']['map'])
+        else:
+            self.SetMapObjUseMap(nvizType = 'surface', attrb = 'transp', map = None)
+        #
+        # draw
+        #
+        for control, drawData in data['draw'].iteritems():
+            if control == 'all': # skip 'all' property
+                continue
+            if control == 'resolution':
+                self.FindWindowById(self.win['surface']['draw']['res-coarse']).SetValue(drawData['coarse'])
+                self.FindWindowById(self.win['surface']['draw']['res-fine']).SetValue(drawData['fine'])
+                continue
+            
+            if control == 'mode':
+                if drawData['desc']['mode'] == 'coarse':
+                    self.FindWindowById(self.win['surface']['draw']['mode']).SetSelection(0)
+                elif drawData['desc']['mode'] == 'fine':
+                    self.FindWindowById(self.win['surface']['draw']['mode']).SetSelection(1)
+                else: # both
+                    self.FindWindowById(self.win['surface']['draw']['mode']).SetSelection(2)
+                
+                if drawData['desc']['style'] == 'wire':
+                    self.FindWindowById(self.win['surface']['draw']['style']).SetSelection(0)
+                else: # surface
+                    self.FindWindowById(self.win['surface']['draw']['style']).SetSelection(1)
+                
+                if drawData['desc']['shading'] == 'flat':
+                    self.FindWindowById(self.win['surface']['draw']['shading']).SetSelection(0)
+                else: # gouraud
+                    self.FindWindowById(self.win['surface']['draw']['shading']).SetSelection(1)
+                
+                continue
+            
+            value = drawData['value']
+            win = self.FindWindowById(self.win['surface']['draw'][control])
+            
+            name = win.GetName()
+            
+            if name == "selection":
+                win.SetSelection(value)
+            elif name == "colour":
+                color = map(int, value.split(':'))
+                win.SetColour(color)
+            else:
+                win.SetValue(value)
+        #
+        # position
+        #
+        dim = self._display.GetLongDim()
+        self.FindWindowById(self.win['surface']['position']['slider']).SetRange(-2 * dim, 2 * dim)
+        if 'x' in data['position']:
+            xval = data['position']['x']
+            self.FindWindowById(self.win['surface']['position']['axis']).SetSelection(0)
+            for control in ('slider','text'):
+                    self.FindWindowById(self.win['surface']['position'][control]).SetValue(xval)
+        # enable/disable res widget + set draw mode
+        self.OnSurfaceMode(event = None)
+
+    def VectorInfo(self, layer):
+        """!Get number of points/lines
+        
+        @param layer MapLayer instance
+        
+        @return num of points/features (expect of points)
+        @return None
+        """
+        vInfo = grass.vector_info_topo(layer.GetName())
+        
+        if not vInfo:
+            return None
+        
+        nprimitives = 0
+        for key, value in vInfo.iteritems():
+            if key in ('points',
+                       'lines',
+                       'boundaries',
+                       'centroids',
+                       'faces',
+                       'kernels'):
+                nprimitives += value
+        
+        return (vInfo['points'], vInfo['lines'], nprimitives, vInfo['map3d'])
+        
+    def UpdateVectorPage(self, layer, data, updateName = True):
+        """!Update vector page"""
+        npoints, nlines, nfeatures, mapIs3D = self.VectorInfo(layer)
+        if mapIs3D:
+            desc = _("Vector map is 3D")
+            enable = False
+        else:
+            desc = _("Vector map is 2D")
+            enable = True
+        desc += " - " + _("%(features)d features (%(points)d points)") % \
+            { 'features' : nfeatures, 'points' : npoints }
+        
+        if updateName:
+            self.FindWindowById(self.win['vector']['map']).SetValue(layer.name)
+        self.FindWindowById(self.win['vector']['desc']).SetLabel(desc)
+        
+        self.FindWindowById(self.win['vector']['lines']['flat']).Enable(enable)
+        for v in ('lines', 'points'):
+            self.FindWindowById(self.win['vector'][v]['surface']).Enable(enable)
+            self.FindWindowById(self.win['vector'][v]['height']['slider']).Enable(enable)
+            self.FindWindowById(self.win['vector'][v]['height']['text']).Enable(enable)
+            
+        #
+        # lines
+        #
+        showLines = self.FindWindowById(self.win['vector']['lines']['show'])
+        if 'object' in data['lines']:
+            showLines.SetValue(True)
+        else:
+            showLines.SetValue(False)
+            if nlines > 0:
+                showLines.Enable(True)
+            else:
+                showLines.Enable(False)
+        
+        self.UpdateVectorShow('lines',
+                              showLines.IsChecked())
+        
+        width = self.FindWindowById(self.win['vector']['lines']['width'])
+        width.SetValue(data['lines']['width']['value'])
+        
+        color = self.FindWindowById(self.win['vector']['lines']['color'])
+        color.SetValue(map(int, data['lines']['color']['value'].split(':')))
+        
+        for vtype in ('lines', 'points'):
+            if vtype == 'lines':
+                display = self.FindWindowById(self.win['vector']['lines']['flat'])
+                if data[vtype]['mode']['type'] == 'flat':
+                    display.SetSelection(1)
+                else:
+                    display.SetSelection(0)
+            if data[vtype]['mode']['type'] == 'surface':
+                rasters = self.mapWindow.GetLayerNames('raster')
+                constants = self.mapWindow.GetLayerNames('constant')
+                surfaces = rasters + constants
+                surfaceWin = self.FindWindowById(self.win['vector'][vtype]['surface'])
+                surfaceWin.SetItems(surfaces)
+                for idx, surface in enumerate(surfaces):
+                    try:# TODO fix this mess
+                        selected = data[vtype]['mode']['surface']['show'][idx]
+                    except (TypeError, IndexError, KeyError):
+                        selected = False
+                    surfaceWin.Check(idx, selected)
+
+        for type in ('slider', 'text'):
+            win = self.FindWindowById(self.win['vector']['lines']['height'][type])
+            win.SetValue(data['lines']['height']['value'])
+        
+        #
+        # points
+        #
+        showPoints = self.FindWindowById(self.win['vector']['points']['show'])
+        
+        if 'object' in data['points']:
+            showPoints.SetValue(True)
+        else:
+            showPoints.SetValue(False)
+            if npoints > 0:
+                showPoints.Enable(True)
+            else:
+                showPoints.Enable(False)
+        
+        self.UpdateVectorShow('points',
+                              showPoints.IsChecked())
+        # size, width, marker, color
+        for prop in ('size', 'marker', 'color'):
+            win = self.FindWindowById(self.win['vector']['points'][prop])
+            name = win.GetName()
+            if name == 'selection':
+                win.SetSelection(data['points'][prop]['value'])
+            elif name == 'color':
+                color = map(int, data['points'][prop]['value'].split(':'))
+                win.SetValue(color)
+            else:
+                win.SetValue(data['points'][prop]['value'])
+            
+        # height
+        for type in ('slider', 'text'):
+            win = self.FindWindowById(self.win['vector']['points']['height'][type])
+            win.SetValue(data['points']['height']['value'])
+        
+    def UpdateVolumePage(self, layer, data, updateName = True):
+        """!Update volume page"""
+        if updateName:
+            self.FindWindowById(self.win['volume']['map']).SetValue(layer.name)
+        
+        # draw
+        for control, idata in data['draw'].iteritems():
+            if control == 'all': # skip 'all' property
+                continue
+                
+            win = self.FindWindowById(self.win['volume']['draw'][control])
+            if control == 'mode':
+                value = data['draw']['mode']['value']
+            if control == 'shading':
+                if data['draw']['shading'][data['draw']['mode']['desc']]['desc'] == 'flat':
+                    value = 0
+                else:
+                    value = 1
+            if control == 'resolution':
+                value = idata[data['draw']['mode']['desc']]['value']
+            
+            if win.GetName() == "selection":
+                win.SetSelection(value)
+            else:
+                win.SetValue(value)
+                
+        self.OnVolumeMode(None)
+        id = data['object']['id']
+        if data['draw']['mode']['desc'] == 'isosurface':
+            self._display.SetIsosurfaceMode(id, data['draw']['shading']['isosurface']['value'])
+            self._display.SetIsosurfaceRes(id, data['draw']['resolution']['isosurface']['value'])
+        else:
+            self._display.SetSliceMode(id, data['draw']['shading']['slice']['value'])
+            self._display.SetSliceRes(id, data['draw']['resolution']['slice']['value'])
+        box = self.FindWindowById(self.win['volume']['isosurfs'])
+        
+        if data['draw']['mode']['desc'] == 'isosurface':
+            isosurfaces = []
+            for iso in data['isosurface']:
+                level = iso['topo']['value']
+                isosurfaces.append("%s %s" % (_("Level"), level))
+            box.Set(isosurfaces)
+            for i in range(len(isosurfaces)):
+                box.Check(i)
+            if data['isosurface']:
+                box.SetSelection(0)
+                self.UpdateVolumeIsosurfPage(data['isosurface'][0])
+            else:
+                self.UpdateVolumeIsosurfPage(data['attribute'])
+        else:
+            slices = []
+            for slice in data['slice']:
+                axis = ("X", "Y", "Z")[slice['position']['axis']]
+                slices.append("%s %s" % (_("Slice parallel to"), axis))
+            box.Set(slices)
+            for i in range(len(slices)):
+                box.Check(i)
+            if data['slice']:
+                box.SetSelection(0)
+                self.UpdateVolumeSlicePage(data['slice'][0])
+            else:
+                self.UpdateVolumeSlicePage(None)
+        #
+        # position
+        #
+        if 'x' in data['position']:
+            xval = data['position']['x']
+            self.FindWindowById(self.win['volume']['position']['axis']).SetSelection(0)
+            for control in ('slider','text'):
+                    self.FindWindowById(self.win['volume']['position'][control]).SetValue(xval)    
+        # set topo range
+        mapRange = self._get3dRange(name = layer.name)
+        desc = self.FindWindowById(self.win['volume']['desc'])
+        desc.SetLabel("%s %.2f - %.2f" % (_("range:"), mapRange[0], mapRange[1]))
+        
+    def UpdateVolumeIsosurfPage(self, data):
+        """!Update dialog -- isosurface attributes"""
+        #
+        # isosurface attributes
+        #
+        for attrb in ('topo', 'color', 'mask',
+                     'transp', 'shine'):
+            # skip empty attributes
+            if attrb not in data:
+                self.SetMapObjUseMap(nvizType = 'volume', attrb = attrb, map = None)
+                continue     
+            
+            value = data[attrb]['value']
+            if attrb == 'color':
+                if data[attrb]['map']:
+                    self.FindWindowById(self.win['volume'][attrb]['map']).SetValue(value)
+                else: # constant
+                    color = map(int, value.split(':'))
+                    self.FindWindowById(self.win['volume'][attrb]['const']).SetColour(color)
+            else:
+                if data[attrb]['map']:
+                    self.vetoGSelectEvt = True
+                    win = self.FindWindowById(self.win['volume'][attrb]['map'])
+                    win.SetValue(value)
+                else:
+                    if value:
+                        win = self.FindWindowById(self.win['volume'][attrb]['const'])
+                        if attrb == 'topo':
+                            win.SetValue(float(value))
+                        else:
+                            win.SetValue(self._getPercent(value))
+                    
+            self.SetMapObjUseMap(nvizType = 'volume',
+                                 attrb = attrb, map = data[attrb]['map'])
+        # set inout
+        if 'inout' in data:
+            self.FindWindowById(self.win['volume']['inout']).SetValue(data['inout'])
+            
+    def UpdateVolumeSlicePage(self, data):
+        """!Update dialog -- slice attributes"""
+        if data:
+            for coord in ('x1', 'x2', 'y1', 'y2', 'z1', 'z2'):
+                win = self.FindWindowById(self.win['volume']['slice']['slider_' + coord])
+                win.Enable()
+                win.SetValue(data['position'][coord] * 100)
+            win = self.FindWindowById(self.win['volume']['slice']['axes'])
+            win.SetSelection(data['position']['axis'])
+            win.Enable()
+            
+            win = self.FindWindowById(self.win['volume']['slice']['transp'])
+            win.SetValue(self._getPercent(data['transp']['value']))
+            win.Enable()
+            self.FindWindowById(self.win['volume']['slice']['reset']).Enable()
+        else:
+            for coord in ('x1', 'x2', 'y1', 'y2', 'z1', 'z2'):
+                self.FindWindowById(self.win['volume']['slice']['slider_' + coord]).Disable()
+            self.FindWindowById(self.win['volume']['slice']['axes']).Disable()
+            self.FindWindowById(self.win['volume']['slice']['transp']).Disable()
+            self.FindWindowById(self.win['volume']['slice']['reset']).Disable()
+        
+        self.UpdateSliceLabels()
+        
+    def UpdateSliceLabels(self):
+        """!Update text labels of slice controls according to axis"""
+        sel = self.FindWindowById(self.win['volume']['slice']['axes']).GetSelection()
+        if sel == 0:
+            self.FindWindowByName('label_edge_0').SetLabel(_("North edge:"))
+            self.FindWindowByName('label_edge_1').SetLabel(_("South edge:"))
+            self.FindWindowByName('label_edge_2').SetLabel(_("West edge:"))
+            self.FindWindowByName('label_edge_3').SetLabel(_("East edge:"))
+            
+            self.FindWindowByName('label_coord_0').SetLabel(_("Northing (Y):"))
+            self.FindWindowByName('label_coord_1').SetLabel(_("Height (Z):"))
+            self.FindWindowByName('label_coord_2').SetLabel(_("Easting (X):"))
+        elif sel == 1:
+            self.FindWindowByName('label_edge_0').SetLabel(_("West edge:"))
+            self.FindWindowByName('label_edge_1').SetLabel(_("East edge:"))
+            self.FindWindowByName('label_edge_2').SetLabel(_("North edge:"))
+            self.FindWindowByName('label_edge_3').SetLabel(_("South edge:"))
+            
+            self.FindWindowByName('label_coord_0').SetLabel(_("Easting (X):"))
+            self.FindWindowByName('label_coord_1').SetLabel(_("Height (Z):"))
+            self.FindWindowByName('label_coord_2').SetLabel(_("Northing (Y):"))
+        else:
+            self.FindWindowByName('label_edge_0').SetLabel(_("West edge:"))
+            self.FindWindowByName('label_edge_1').SetLabel(_("East edge:"))
+            self.FindWindowByName('label_edge_2').SetLabel(_("Bottom edge:"))
+            self.FindWindowByName('label_edge_3').SetLabel(_("Top edge:"))  
+            
+            self.FindWindowByName('label_coord_0').SetLabel(_("Easting (X):"))
+            self.FindWindowByName('label_coord_1').SetLabel(_("Northing (Y):"))
+            self.FindWindowByName('label_coord_2').SetLabel(_("Height (Z):")) 
+        
+    def SetPage(self, name):
+        """!Get named page"""
+        if name == 'view':
+            self.SetSelection(0)
+        elif name in ('surface', 'vector', 'volume'):
+            self.SetSelection(1)
+        else:
+            self.SetSelection(2)
+
+        win = self.FindWindowById(self.page[name]['notebook'])
+        try:
+            win.Expand(win.GetFoldPanel(self.page[name]['id']))
+            self.UpdateScrolling((win.GetFoldPanel(self.page[name]['id']).GetGrandParent(),))
+        except AttributeError:
+            win.SetSelection(self.page[name]['id'])
+
+class PositionWindow(wx.Window):
+    """!Abstract position control window, see subclasses
+    ViewPostionWindow and LightPositionWindow"""
+    def __init__(self, parent, mapwindow, id = wx.ID_ANY,
+                 **kwargs):
+        self.mapWindow = mapwindow
+        self.quick = True
+        
+        wx.Window.__init__(self, parent, id, **kwargs)
+        
+        self.SetBackgroundColour("WHITE")
+        
+        self.pdc = wx.PseudoDC()
+        
+        self.pdc.SetBrush(wx.Brush(colour = 'dark green', style = wx.SOLID))
+        self.pdc.SetPen(wx.Pen(colour = 'dark green', width = 2, style = wx.SOLID))
+
+        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+        # self.Bind(wx.EVT_MOTION,       self.OnMouse)
+        self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
+        
+    def Draw(self, pos, scale = False):
+        w, h = self.GetClientSize()
+        x, y = pos
+        if scale:
+            x = x * w
+            y = y * h
+        self.pdc.Clear()
+        self.pdc.BeginDrawing()
+        self.pdc.DrawLine(w / 2, h / 2, x, y)
+        self.pdc.DrawCircle(x, y, 5)
+        self.pdc.EndDrawing()
+        
+    def OnPaint(self, event):
+        dc = wx.BufferedPaintDC(self)
+        dc.SetBackground(wx.Brush("White"))
+        dc.Clear()
+        
+        self.PrepareDC(dc)
+        self.pdc.DrawToDC(dc)
+        
+    def UpdatePos(self, xcoord, ycoord):
+        """!Update position coordinates (origin: UL)"""
+        if xcoord < 0.0:
+            xcoord = 0.0
+        elif xcoord > 1.0:
+            xcoord = 1.0
+        if ycoord < 0.0:
+            ycoord = 0.0
+        elif ycoord > 1.0:
+            ycoord = 1.0
+        
+        x, y = self.TransformCoordinates(xcoord, ycoord)
+        self.data['position']['x'] = x        
+        self.data['position']['y'] = y
+        
+        return xcoord, ycoord
+    
+    def OnMouse(self, event):
+        if event.LeftIsDown():
+            x, y = event.GetPosition()
+            self.Draw(pos = (x, y))
+            w, h = self.GetClientSize()
+            x = float(x) / w
+            y = float(y) / h
+            self.UpdatePos(x, y)
+            self.Refresh(False)
+        
+        event.Skip()
+        
+    def PostDraw(self):
+        x, y = self.UpdatePos(self.data['position']['x'],
+                              self.data['position']['y'])
+        
+        self.Draw(pos = (x,y), scale = True)
+
+class ViewPositionWindow(PositionWindow):
+    """!View position control widget"""
+    def __init__(self, parent, mapwindow, id = wx.ID_ANY,
+                 **kwargs):
+        PositionWindow.__init__(self, parent, mapwindow, id, **kwargs)
+
+        self.data = self.mapWindow.view
+        self.PostDraw()
+        
+    def UpdatePos(self, xcoord, ycoord):
+        x, y = PositionWindow.UpdatePos(self, xcoord, ycoord)
+        
+        event = wxUpdateView(zExag = True)
+        wx.PostEvent(self.mapWindow, event)
+
+        return x, y
+    
+    def TransformCoordinates(self, x, y, toLight = True):
+        return x, y
+        
+    def OnMouse(self, event):
+        self.mapWindow.iview['dir']['use'] = False # use focus instead of viewdir
+        PositionWindow.OnMouse(self, event)
+        if event.LeftIsDown():
+            self.mapWindow.render['quick'] = self.quick
+            self.mapWindow.Refresh(eraseBackground = False)
+        elif event.LeftUp():
+            self.mapWindow.render['quick'] = False
+            self.mapWindow.Refresh(eraseBackground = False)
+        
+        event.Skip()
+
+class LightPositionWindow(PositionWindow):
+    """!Light position control widget"""
+    def __init__(self, parent, mapwindow, id = wx.ID_ANY,
+                 **kwargs):
+        PositionWindow.__init__(self, parent, mapwindow, id, **kwargs)
+        
+        self.data = self.mapWindow.light
+        self.quick = False
+        self.PostDraw()
+
+    def UpdatePos(self, xcoord, ycoord):
+        x, y = PositionWindow.UpdatePos(self, xcoord, ycoord)
+        
+        event = wxUpdateLight(refresh = False)
+        wx.PostEvent(self.mapWindow, event)
+        
+        return x, y
+    
+    def TransformCoordinates(self, x, y, toLight = True):
+        if toLight:
+            x = 2 * x - 1
+            y = -2 * y + 1
+        else:
+            x = (x + 1)/2
+            y = (1 - y)/2
+        return x, y
+    
+    def PostDraw(self):
+        event = wxUpdateLight(refresh = True)
+        wx.PostEvent(self.mapWindow, event)
+        x, y = self.data['position']['x'], self.data['position']['y']
+        x, y = self.TransformCoordinates(x, y, toLight = False)
+        
+        self.Draw(pos = (x,y), scale = True)
+        
+    def OnMouse(self, event):
+        PositionWindow.OnMouse(self, event)
+        if event.LeftUp():
+            self.mapWindow.render['quick'] = False
+            self.mapWindow.Refresh(eraseBackground = False)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/nviz/workspace.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/nviz/workspace.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/nviz/workspace.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,325 @@
+"""!
+ at package nviz.workspace
+
+ at brief wxNviz workspace settings
+
+Classes:
+ - workspace::NvizSettings
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Kratochvilova <kratochanna gmail.com> (wxNviz / Google SoC 2011)
+"""
+
+import copy
+
+from core.settings import UserSettings
+
+try:
+    from nviz import wxnviz
+except ImportError:
+    wxnviz = None
+
+class NvizSettings(object):
+    def __init__(self):
+        pass
+        
+    def SetConstantDefaultProp(self):
+        """Set default constant data properties"""
+        data = dict()
+        for key, value in UserSettings.Get(group='nviz', key='constant').iteritems():
+            data[key] = value
+        color = str(data['color'][0]) + ':' + str(data['color'][1]) + ':' + str(data['color'][2])
+        data['color'] = color
+
+        return data
+    
+    def SetSurfaceDefaultProp(self, data = None):
+        """Set default surface data properties"""
+        if not data:
+            data = dict()
+        for sec in ('attribute', 'draw', 'mask', 'position'):
+            data[sec] = {}
+        
+        #
+        # attributes
+        #
+        for attrb in ('shine', ):
+            data['attribute'][attrb] = {}
+            for key, value in UserSettings.Get(group='nviz', key='surface',
+                                               subkey=attrb).iteritems():
+                data['attribute'][attrb][key] = value
+            data['attribute'][attrb]['update'] = None
+        
+        #
+        # draw
+        #
+        data['draw']['all'] = False # apply only for current surface
+        for control, value in UserSettings.Get(group='nviz', key='surface', subkey='draw').iteritems():
+            if control[:3] == 'res':
+                if 'resolution' not in data['draw']:
+                    data['draw']['resolution'] = {}
+                if 'update' not in data['draw']['resolution']:
+                    data['draw']['resolution']['update'] = None
+                data['draw']['resolution'][control[4:]] = value
+                continue
+            
+            if control == 'wire-color':
+                value = str(value[0]) + ':' + str(value[1]) + ':' + str(value[2])
+            elif control in ('mode', 'style', 'shading'):
+                if 'mode' not in data['draw']:
+                    data['draw']['mode'] = {}
+                continue
+
+            data['draw'][control] = { 'value' : value }
+            data['draw'][control]['update'] = None
+        
+        value, desc = self.GetDrawMode(UserSettings.Get(group='nviz', key='surface', subkey=['draw', 'mode']),
+                                       UserSettings.Get(group='nviz', key='surface', subkey=['draw', 'style']),
+                                       UserSettings.Get(group='nviz', key='surface', subkey=['draw', 'shading']))
+    
+        data['draw']['mode'] = { 'value' : value,
+                                 'desc' : desc, 
+                                 'update': None }
+        # position
+        for coord in ('x', 'y', 'z'):
+            data['position'][coord] = UserSettings.Get(group='nviz', key='surface', subkey=['position', coord])
+        data['position']['update'] = None
+            
+        return data
+    
+    def SetVolumeDefaultProp(self):
+        """Set default volume data properties"""
+        data = dict()
+        for sec in ('attribute', 'draw', 'position'):
+            data[sec] = dict()
+            for sec in ('isosurface', 'slice'):
+                    data[sec] = list()
+        
+        #
+        # draw
+        #
+        for control, value in UserSettings.Get(group='nviz', key='volume', subkey='draw').iteritems():
+            if control == 'shading':
+                sel = UserSettings.Get(group='nviz', key='volume', subkey=['draw', 'shading'])
+                value, desc = self.GetDrawMode(shade=sel, string=False)
+                
+                data['draw']['shading'] = {}
+                data['draw']['shading']['isosurface'] = { 'value' : value,
+                                                          'desc' : desc['shading'] }
+                data['draw']['shading']['slice'] = { 'value' : value,
+                                                     'desc' : desc['shading'] }
+            elif control == 'mode':
+                sel = UserSettings.Get(group='nviz', key='volume', subkey=['draw', 'mode'])
+                if sel == 0:
+                    desc = 'isosurface'
+                else:
+                    desc = 'slice'
+                data['draw']['mode'] = { 'value' : sel,
+                                         'desc' : desc, }
+            else:
+                data['draw'][control] = {}
+                data['draw'][control]['isosurface'] = { 'value' : value }
+                data['draw'][control]['slice'] = { 'value' : value }
+
+            if 'update' not in data['draw'][control]:
+                data['draw'][control]['update'] = None
+        
+        #
+        # isosurface attributes
+        #
+        for attrb in ('shine', ):
+            data['attribute'][attrb] = {}
+            for key, value in UserSettings.Get(group='nviz', key='volume',
+                                               subkey=attrb).iteritems():
+                data['attribute'][attrb][key] = value
+        
+        return data
+    
+    def SetIsosurfaceDefaultProp(self):
+        """!Set default isosurface properties"""
+        data = dict()
+        for attr in ('shine', 'topo', 'transp', 'color'):
+            data[attr] = {}
+            for key, value in UserSettings.Get(group = 'nviz', key = 'volume',
+                                               subkey = attr).iteritems():
+                data[attr][key] = value
+            data[attr]['update'] = None
+        return data
+    
+    def SetSliceDefaultProp(self):
+        """!Set default slice properties"""
+        data = dict()
+        data['position'] = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'volume',
+                                               subkey = 'slice_position'))
+        data['position']['update'] = None
+        
+        data['transp'] = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'volume',
+                                               subkey = 'transp'))
+        return data
+    
+    def SetVectorDefaultProp(self, data = None):
+        """Set default vector data properties"""
+        if not data:
+            data = dict()
+        for sec in ('lines', 'points'):
+            data[sec] = {}
+        
+        self.SetVectorLinesDefaultProp(data['lines'])
+        self.SetVectorPointsDefaultProp(data['points'])
+
+        return data
+    
+    def SetVectorLinesDefaultProp(self, data):
+        """Set default vector properties -- lines"""
+        # width
+        data['width'] = {'value' : UserSettings.Get(group='nviz', key='vector',
+                                                    subkey=['lines', 'width']) }
+        
+        # color
+        value = UserSettings.Get(group='nviz', key='vector',
+                                 subkey=['lines', 'color'])
+        color = str(value[0]) + ':' + str(value[1]) + ':' + str(value[2])
+        data['color'] = { 'value' : color }
+
+        # mode
+        if UserSettings.Get(group='nviz', key='vector',
+                            subkey=['lines', 'flat']):
+            type = 'flat'
+            
+        else:
+            type = 'surface'
+            
+        data['mode'] = {}
+        data['mode']['type'] = type
+        data['mode']['update'] = None
+    
+        # height
+        data['height'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
+                                                      subkey=['lines', 'height']) }
+        if 'object' in data:
+            for attrb in ('color', 'width', 'mode', 'height'):
+                data[attrb]['update'] = None
+        
+    def SetVectorPointsDefaultProp(self, data):
+        """Set default vector properties -- points"""
+        # size
+        data['size'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
+                                                    subkey=['points', 'size']) }
+
+        # width
+        data['width'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
+                                                     subkey=['points', 'width']) }
+
+        # marker
+        data['marker'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
+                                                      subkey=['points', 'marker']) }
+
+        # color
+        value = UserSettings.Get(group='nviz', key='vector',
+                                 subkey=['points', 'color'])
+        color = str(value[0]) + ':' + str(value[1]) + ':' + str(value[2])
+        data['color'] = { 'value' : color }
+
+        # mode
+        data['mode'] = { 'type' : 'surface'}
+##                         'surface' : '', }
+        
+        # height
+        data['height'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
+                                                      subkey=['points', 'height']) }
+        
+        if 'object' in data:
+            for attrb in ('size', 'width', 'marker', 'color', 'height'):
+                data[attrb]['update'] = None
+        
+    def GetDrawMode(self, mode=None, style=None, shade=None, string=False):
+        """Get surface draw mode (value) from description/selection
+
+        @param mode,style,shade modes
+        @param string if True input parameters are strings otherwise
+        selections
+        """
+        if not wxnviz:
+            return None
+        
+        value = 0
+        desc = {}
+
+        if string:
+            if mode is not None:
+                if mode == 'coarse':
+                    value |= wxnviz.DM_WIRE
+                elif mode == 'fine':
+                    value |= wxnviz.DM_POLY
+                else: # both
+                    value |= wxnviz.DM_WIRE_POLY
+
+            if style is not None:
+                if style == 'wire':
+                    value |= wxnviz.DM_GRID_WIRE
+                else: # surface
+                    value |= wxnviz.DM_GRID_SURF
+                    
+            if shade is not None:
+                if shade == 'flat':
+                    value |= wxnviz.DM_FLAT
+                else: # surface
+                    value |= wxnviz.DM_GOURAUD
+
+            return value
+
+        # -> string is False
+        if mode is not None:
+            if mode == 0: # coarse
+                value |= wxnviz.DM_WIRE
+                desc['mode'] = 'coarse'
+            elif mode == 1: # fine
+                value |= wxnviz.DM_POLY
+                desc['mode'] = 'fine'
+            else: # both
+                value |= wxnviz.DM_WIRE_POLY
+                desc['mode'] = 'both'
+
+        if style is not None:
+            if style == 0: # wire
+                value |= wxnviz.DM_GRID_WIRE
+                desc['style'] = 'wire'
+            else: # surface
+                value |= wxnviz.DM_GRID_SURF
+                desc['style'] = 'surface'
+
+        if shade is not None:
+            if shade == 0:
+                value |= wxnviz.DM_FLAT
+                desc['shading'] = 'flat'
+            else: # surface
+                value |= wxnviz.DM_GOURAUD
+                desc['shading'] = 'gouraud'
+        
+        return (value, desc)
+    
+    def SetDecorDefaultProp(self, type):
+        """!Set default arrow properties
+        """
+        data = {}
+        
+        # arrow
+        if type == 'arrow':
+            data['arrow'] = UserSettings.Get(group = 'nviz', key = 'arrow')
+            data['arrow']['color'] = "%d:%d:%d" % (
+                UserSettings.Get(group = 'nviz', key = 'arrow', subkey = 'color')[:3])
+            data['arrow'].update(UserSettings.Get(group = 'nviz', key = 'arrow', internal = True))
+            data['arrow']['show'] = False
+        
+        # arrow
+        if type == 'scalebar':
+            data['scalebar'] = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'scalebar'))
+            data['scalebar']['color'] = "%d:%d:%d" % (
+                UserSettings.Get(group = 'nviz', key = 'scalebar', subkey = 'color')[:3])
+            data['scalebar'].update(copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'scalebar', internal = True)))
+            data['scalebar']['id'] = 0
+        return data


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/nviz/wxnviz.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/nviz/wxnviz.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/nviz/wxnviz.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,2042 @@
+"""!
+ at package nviz.wxnviz
+
+ at brief wxGUI 3D view mode (ctypes-based classes)
+
+This module implements 3D visualization mode for map display (ctypes
+required).
+
+List of classes:
+ - wxnviz::Nviz
+ - wxnviz::Texture
+ - wxnviz::ImageTexture
+ - wxnviz::TextTexture
+
+(C) 2008-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
+ at author Pythonized by Glynn Clements
+ at author Anna Kratochvilova <KratochAnna seznam.cz> (Google SoC 2011)
+"""
+
+import sys
+import locale
+import struct
+from math  import sqrt
+try:
+    from numpy import matrix
+except ImportError:
+    msg = _("This module requires the NumPy module, which could not be "
+            "imported. It probably is not installed (it's not part of the "
+            "standard Python distribution). See the Numeric Python site "
+            "(http://numpy.scipy.org) for information on downloading source or "
+            "binaries.")
+    print >> sys.stderr, "wxnviz.py: " + msg
+
+import wx
+
+from ctypes import *
+try:
+    from grass.lib.gis      import *
+    from grass.lib.g3d      import *
+    from grass.lib.vector   import *
+    from grass.lib.ogsf     import *
+    from grass.lib.nviz     import *
+except ImportError, e:
+    sys.stderr.write(_("3D view mode: %s\n") % e)
+    
+from core.debug import Debug
+import grass.script as grass
+
+log      = None
+progress = None
+
+def print_error(msg, type):
+    """!Redirect stderr"""
+    global log
+    if log:
+        log.write(msg)
+    else:
+        print msg
+    
+    return 0
+
+def print_progress(value):
+    """!Redirect progress info"""
+    global progress
+    if progress:
+        progress.SetValue(value)
+    else:
+        print value
+    
+    return 0
+
+errtype = CFUNCTYPE(UNCHECKED(c_int), String, c_int)
+errfunc = errtype(print_error)
+pertype = CFUNCTYPE(UNCHECKED(c_int), c_int)
+perfunc = pertype(print_progress)
+
+class Nviz(object):
+    def __init__(self, glog, gprogress):
+        """!Initialize Nviz class instance
+        
+        @param log logging area
+        @param gprogress progressbar
+        """
+        global errfunc, perfunc, log, progress
+        log = glog
+        progress = gprogress
+        
+        G_gisinit("wxnviz")
+        # gislib is already initialized (where?)
+        G_set_error_routine(errfunc)
+        G_set_percent_routine(perfunc)
+        
+        self.Init()
+        
+        self.data_obj = nv_data()
+        self.data = pointer(self.data_obj)
+        self.color_obj = Colors()
+        self.color = pointer(self.color_obj)
+        
+        self.width = self.height = -1
+        self.showLight = False
+        
+        Debug.msg(1, "Nviz::Nviz()")
+        
+    def __del__(self):
+        """!Destroy Nviz class instance"""
+        G_unset_error_routine()
+        G_unset_percent_routine()
+        del self.data
+        del self.data_obj
+        self.log = None
+
+    def Init(self):
+        """!Initialize window"""
+        locale.setlocale(locale.LC_NUMERIC, 'C')
+        #G_unset_window()
+        #Rast_unset_window()
+        #Rast__init_window()
+        GS_libinit()
+        GVL_libinit()
+    
+    def ResizeWindow(self, width, height):
+        """!GL canvas resized
+        
+        @param width window width
+        @param height window height
+        
+        @return 1 on success
+        @return 0 on failure (window resized by default to 20x20 px)
+        """
+        self.width  = width
+        self.height = height
+        Debug.msg(3, "Nviz::ResizeWindow(): width=%d height=%d",
+                  width, height)
+        return Nviz_resize_window(width, height)
+    
+    def GetLongDim(self):
+        """!Get longest dimension, used for initial size of north arrow"""
+        return Nviz_get_longdim(self.data)
+    
+    def SetViewDefault(self):
+        """!Set default view (based on loaded data)
+        
+        @return z-exag value, default, min and max height
+        """
+        # determine z-exag
+        z_exag = Nviz_get_exag()
+        Nviz_change_exag(self.data, z_exag)
+        
+        # determine height
+        hdef = c_double()
+        hmin = c_double()
+        hmax = c_double()
+        Nviz_get_exag_height(byref(hdef), byref(hmin), byref(hmax))
+        
+        Debug.msg(1, "Nviz::SetViewDefault(): hdef=%f, hmin=%f, hmax=%f",
+                  hdef.value, hmin.value, hmax.value)
+        
+        return (z_exag, hdef.value, hmin.value, hmax.value)
+    
+    def SetView(self, x, y, height, persp, twist):
+        """!Change view settings
+        @param x,y position
+        @param height
+        @param persp perpective
+        @param twist
+        """
+        Nviz_set_viewpoint_height(height)
+        Nviz_set_viewpoint_position(x, y)
+        Nviz_set_viewpoint_twist(twist)
+        Nviz_set_viewpoint_persp(persp)
+        
+        Debug.msg(3, "Nviz::SetView(): x=%f, y=%f, height=%f, persp=%f, twist=%f",
+                  x, y, height, persp, twist)
+                
+    def GetViewpointPosition(self):
+        x = c_double()
+        y = c_double()
+        h = c_double()
+        Nviz_get_viewpoint_height(byref(h))
+        Nviz_get_viewpoint_position(byref(x), byref(y))
+        
+        return (x.value, y.value, h.value)
+        
+    def LookHere(self, x, y):
+        """!Look here feature 
+        @param x,y screen coordinates
+        """
+        
+        Nviz_look_here(x, y)
+        Debug.msg(3, "Nviz::LookHere(): x=%f, y=%f", x, y)
+    
+    def LookAtCenter(self):
+        """!Center view at center of displayed surface"""
+        Nviz_set_focus_map(MAP_OBJ_UNDEFINED, -1)
+        Debug.msg(3, "Nviz::LookAtCenter()")
+    
+    def GetFocus(self):
+        """!Get focus"""
+        Debug.msg(3, "Nviz::GetFocus()")
+        if Nviz_has_focus(self.data):
+            x = c_float()
+            y = c_float()
+            z = c_float()
+            Nviz_get_focus(self.data, byref(x), byref(y), byref(z))
+            return x.value, y.value, z.value
+        else:
+            return -1, -1, -1
+        
+    def SetFocus(self, x, y, z):
+        """!Set focus"""
+        Debug.msg(3, "Nviz::SetFocus()")
+        Nviz_set_focus(self.data, x, y, z)
+        
+    def GetViewdir(self):
+        """!Get viewdir"""
+        Debug.msg(3, "Nviz::GetViewdir()")
+        dir = (c_float * 3)()
+        GS_get_viewdir(byref(dir))
+        
+        return dir[0], dir[1], dir[2]
+        
+    def SetViewdir(self, x, y, z):
+        """!Set viewdir"""
+        Debug.msg(3, "Nviz::SetViewdir(): x=%f, y=%f, z=%f" % (x, y, z))
+        dir = (c_float * 3)()
+        for i, coord in enumerate((x, y, z)):
+            dir[i] = coord
+        GS_set_viewdir(byref(dir))
+                
+    def SetZExag(self, z_exag):
+        """!Set z-exag value
+        
+        @param z_exag value
+        
+        @return 1
+        """
+        Debug.msg(3, "Nviz::SetZExag(): z_exag=%f", z_exag)
+        return Nviz_change_exag(self.data, z_exag)
+    
+    def Draw(self, quick, quick_mode):
+        """!Draw canvas
+        
+        Draw quick mode:
+         - DRAW_QUICK_SURFACE
+         - DRAW_QUICK_VLINES
+         - DRAW_QUICK_VPOINTS
+         - DRAW_QUICK_VOLUME
+        
+        @param quick if true draw in wiremode
+        @param quick_mode quick mode
+        """
+        Debug.msg(3, "Nviz::Draw(): quick=%d", quick)
+        
+        Nviz_draw_cplane(self.data, -1, -1) # ?
+        
+        if quick:
+            Nviz_draw_quick(self.data, quick_mode)
+        else:
+            Nviz_draw_all(self.data)
+        
+    def EraseMap(self):
+        """!Erase map display (with background color)
+        """
+        Debug.msg(1, "Nviz::EraseMap()")
+        GS_clear(Nviz_get_bgcolor(self.data))
+        
+    def InitView(self):
+        """!Initialize view"""
+        # initialize nviz data
+        Nviz_init_data(self.data)
+        
+        # define default attributes for map objects
+        Nviz_set_surface_attr_default()
+        # set background color
+        Nviz_set_bgcolor(self.data, Nviz_color_from_str("white"))
+        
+        GS_clear(Nviz_get_bgcolor(self.data))        
+        # initialize view, lights
+        Nviz_init_view(self.data)
+        
+        Debug.msg(1, "Nviz::InitView()")
+        
+    def SetBgColor(self, color_str):
+        """!Set background color
+        
+        @param color_str color string
+        """
+        Nviz_set_bgcolor(self.data, Nviz_color_from_str(color_str))
+        
+    def SetLight(self, x, y, z, color, bright, ambient, w = 0, lid = 1):
+        """!Change lighting settings
+        @param x,y,z position
+        @param color light color (as string)
+        @param bright light brightness
+        @param ambient light ambient
+        @param w local coordinate (default to 0)
+        """
+        Nviz_set_light_position(self.data, lid, x, y, z, w)
+        Nviz_set_light_bright(self.data, lid, bright)
+        Nviz_set_light_color(self.data, lid, int(color[0]), int(color[1]), int(color[2]))
+        Nviz_set_light_ambient(self.data, lid, ambient)
+                             
+    def LoadSurface(self, name, color_name, color_value):
+        """!Load raster map (surface)
+        
+        @param name raster map name
+        @param color_name raster map for color (None for color_value)
+        @param color_value color string (named color or RGB triptet)
+        
+        @return object id
+        @return -1 on failure
+        """
+        mapset = G_find_cell2(name, "")
+        if mapset is None:
+            G_warning(_("Raster map <%s> not found"), name)
+            return -1
+        
+        # topography
+        id = Nviz_new_map_obj(MAP_OBJ_SURF,
+                              G_fully_qualified_name(name, mapset), 0.0,
+                              self.data)
+        
+        if color_name:      # check for color map
+            mapset = G_find_cell2(color_name, "")
+            if mapset is None:
+                G_warning(_("Raster map <%s> not found"), color_name)
+                GS_delete_surface(id)
+                return -1
+            
+            Nviz_set_attr(id, MAP_OBJ_SURF, ATT_COLOR, MAP_ATT,
+                          G_fully_qualified_name(color_name, mapset), -1.0,
+                          self.data)
+        
+        elif color_value:   # check for color value
+            Nviz_set_attr(id, MAP_OBJ_SURF, ATT_COLOR, CONST_ATT,
+                          None, Nviz_color_from_str(color_value),
+                          self.data)
+        
+        else:               # use by default elevation map for coloring
+            Nviz_set_attr(id, MAP_OBJ_SURF, ATT_COLOR, MAP_ATT,
+                          G_fully_qualified_name(name, mapset), -1.0,
+                          self.data)
+        
+        # if (i > 1)
+        #     set_default_wirecolors(self.data, i)
+        
+        # focus on loaded self.data
+        Nviz_set_focus_map(MAP_OBJ_UNDEFINED, -1)
+        
+        Debug.msg(1, "Nviz::LoadRaster(): name=%s -> id=%d", name, id)
+        
+        return id
+    
+    def AddConstant(self, value, color):
+        """!Add new constant surface"""
+        id = Nviz_new_map_obj(MAP_OBJ_SURF, None, value, self.data)
+        
+        Nviz_set_attr(id, MAP_OBJ_SURF, ATT_COLOR, CONST_ATT,
+                        None, Nviz_color_from_str(color),
+                        self.data)
+        Nviz_set_focus_map(MAP_OBJ_UNDEFINED, -1)
+        
+        Debug.msg(1, "Nviz::AddConstant(): id=%d", id)
+        return id
+        
+    def UnloadSurface(self, id):
+        """!Unload surface
+        
+        @param id surface id
+        
+        @return 1 on success
+        @return 0 on failure
+        """
+        if not GS_surf_exists(id):
+            return 0
+        
+        Debug.msg(1, "Nviz::UnloadSurface(): id=%d", id)
+        
+        if GS_delete_surface(id) < 0:
+            return 0
+        
+        return 1
+    
+    def LoadVector(self, name, points):
+        """!Load vector map overlay
+        
+        @param name vector map name
+        @param points if true load 2d points rather then 2d lines
+        
+        @return object id, id of base surface (or -1 if it is not loaded)
+        @return -1 on failure
+        """
+        baseId = -1
+        if GS_num_surfs() == 0:     # load base surface if no loaded
+            baseId = Nviz_new_map_obj(MAP_OBJ_SURF, None, 0.0, self.data)
+            
+            nsurf = c_int()
+            surf_list = GS_get_surf_list(byref(nsurf))
+            GS_set_att_const(surf_list[0], ATT_TRANSP, 255)
+            
+        mapset = G_find_vector2 (name, "")
+        if mapset is None:
+            G_warning(_("Vector map <%s> not found"),
+                      name)
+        
+        if points:
+            id = Nviz_new_map_obj(MAP_OBJ_SITE,
+                                  G_fully_qualified_name(name, mapset), 0.0,
+                                  self.data)
+        else:
+            id = Nviz_new_map_obj(MAP_OBJ_VECT,
+                                  G_fully_qualified_name(name, mapset), 0.0,
+                                  self.data)
+        
+        Debug.msg(1, "Nviz::LoadVector(): name=%s -> id=%d", name, id)
+        
+        return id, baseId
+    
+    def UnloadVector(self, id, points):
+        """!Unload vector set
+        
+        @param id vector set id
+        @param points vector points or lines set
+        
+        @return 1 on success
+        @return 0 on failure
+        """
+        Debug.msg(1, "Nviz::UnloadVector(): id=%d", id)
+        
+        if points:
+            if not GP_site_exists(id):
+                return 0
+            if GP_delete_site(id) < 0:
+                return 0
+        else:
+            if not GV_vect_exists(id):
+                return 0
+            if GV_delete_vector(id) < 0:
+                return 0
+        
+        return 1
+
+    def VectorSurfaceSelected(self, vid, sid):
+        """!Check if surface is selected (currently unused)
+        
+        @param vid vector id
+        @param sid surface id
+        
+        @return True if selected
+        @return False if not selected
+        """
+        selected = GV_surf_is_selected(vid, sid)
+        Debug.msg(1, "Nviz::VectorSurfaceSelected(): vid=%s, sid=%d -> selected=%d", vid, sid, selected)
+        return selected
+    
+    def LoadVolume(self, name, color_name, color_value):
+        """!Load 3d raster map (volume)
+        
+        @param name 3d raster map name
+        @param color_name 3d raster map for color (None for color_value)
+        @param color_value color string (named color or RGB triptet)
+        
+        @return object id
+        @return -1 on failure
+        """
+        mapset = G_find_grid3(name, "")
+        if mapset is None:
+            G_warning(_("3d raster map <%s> not found"),
+                      name)
+            return -1
+        
+        # topography
+        id = Nviz_new_map_obj(MAP_OBJ_VOL,
+                              G_fully_qualified_name(name, mapset), 0.0,
+                              self.data)
+        
+        if color_name:      # check for color map
+            mapset = G_find_grid3(color_name, "")
+            if mapset is None:
+                G_warning(_("3d raster map <%s> not found"),
+                          color_name)
+                GVL_delete_vol(id)
+                return -1
+            
+            Nviz_set_attr(id, MAP_OBJ_VOL, ATT_COLOR, MAP_ATT,
+                          G_fully_qualified_name(color_name, mapset), -1.0,
+                          self.data)
+        elif color_value:   # check for color value
+            Nviz_set_attr(id, MAP_OBJ_VOL, ATT_COLOR, CONST_ATT,
+                          None, Nviz_color_from_str(color_value),
+                          self.data)
+        else:               # use by default elevation map for coloring
+            Nviz_set_attr(id, MAP_OBJ_VOL, ATT_COLOR, MAP_ATT,
+                          G_fully_qualified_name(name, mapset), -1.0,
+                          self.data)
+        
+        Debug.msg(1, "Nviz::LoadVolume(): name=%s -> id=%d", name, id)
+        
+        return id
+
+    def UnloadVolume(self, id):
+        """!Unload volume
+        
+        @param id volume id
+        
+        @return 1 on success
+        @return 0 on failure
+        """
+        if not GVL_vol_exists(id):
+            return 0
+        
+        Debug.msg(1, "Nviz::UnloadVolume(): id=%d", id)
+        
+        if GVL_delete_vol(id) < 0:
+          return 0
+        
+        return 1
+    
+    def SetSurfaceTopo(self, id, map, value):
+        """!Set surface topography
+        
+        @param id surface id
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.SetSurfaceAttr(id, ATT_TOPO, map, value)
+    
+    def SetSurfaceColor(self, id, map, value):
+        """!Set surface color
+        
+        @param id surface id
+        @param map if true use map otherwise constant
+        @param value map name or value
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.SetSurfaceAttr(id, ATT_COLOR, map, value)
+    
+    def SetSurfaceMask(self, id, invert, value):
+        """!Set surface mask
+        
+        @todo invert
+        
+        @param id surface id
+        @param invert if true invert mask 
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.SetSurfaceAttr(id, ATT_MASK, True, value)
+    
+    def SetSurfaceTransp(self, id, map, value):
+        """!Set surface mask
+        
+        @todo invert
+        
+        @param id surface id
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.SetSurfaceAttr(id, ATT_TRANSP, map, value)
+    
+    def SetSurfaceShine(self, id, map, value):
+        """!Set surface shininess
+        
+        @param id surface id
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.SetSurfaceAttr(id, ATT_SHINE, map, value)
+    
+    def SetSurfaceEmit(self, id, map, value):
+        """!Set surface emission (currently unused)
+        
+        @param id surface id
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.SetSurfaceAttr(id, ATT_EMIT, map, value)
+    
+    def SetSurfaceAttr(self, id, attr, map, value):
+        """!Set surface attribute
+        
+        @param id surface id
+        @param attr attribute desc
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        if not GS_surf_exists(id):
+            return -1
+        
+        if map:
+            ret = Nviz_set_attr(id, MAP_OBJ_SURF, attr, MAP_ATT,
+                                value, -1.0, self.data)
+        else:
+            if attr == ATT_COLOR:
+                val = Nviz_color_from_str(value)
+            else:
+                val = float(value)
+            
+            ret = Nviz_set_attr(id, MAP_OBJ_SURF, attr, CONST_ATT,
+                                None, val, self.data)
+        
+        Debug.msg(3, "Nviz::SetSurfaceAttr(): id=%d, attr=%d, map=%d, value=%s",
+                  id, attr, map, value)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
+    def UnsetSurfaceMask(self, id):
+        """!Unset surface mask
+        
+        @param id surface id
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        @return -1 on failure
+        """
+        return self.UnsetSurfaceAttr(id, ATT_MASK)
+    
+    def UnsetSurfaceTransp(self, id):
+        """!Unset surface transparency
+        
+        @param id surface id
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.UnsetSurfaceAttr(id, ATT_TRANSP)
+    
+    def UnsetSurfaceEmit(self, id):
+        """!Unset surface emission (currently unused)
+        
+        @param id surface id
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        return self.UnsetSurfaceAttr(id, ATT_EMIT)
+    
+    def UnsetSurfaceAttr(self, id, attr):
+        """!Unset surface attribute
+        
+        @param id surface id
+        @param attr attribute descriptor
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        if not GS_surf_exists(id):
+            return -1
+        
+        Debug.msg(3, "Nviz::UnsetSurfaceAttr(): id=%d, attr=%d",
+                  id, attr)
+        
+        ret = Nviz_unset_attr(id, MAP_OBJ_SURF, attr)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+
+    def SetSurfaceRes(self, id, fine, coarse):
+        """!Set surface resolution
+        
+        @param id surface id
+        @param fine x/y fine resolution
+        @param coarse x/y coarse resolution
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        Debug.msg(3, "Nviz::SetSurfaceRes(): id=%d, fine=%d, coarse=%d",
+                  id, fine, coarse)
+        
+        if id > 0:
+            if not GS_surf_exists(id):
+                return -1
+            
+            if GS_set_drawres(id, fine, fine, coarse, coarse) < 0:
+                return -2
+        else:
+            GS_setall_drawres(fine, fine, coarse, coarse)
+        
+        return 1
+
+    def SetSurfaceStyle(self, id, style):
+        """!Set draw style
+        
+        Draw styles:
+         - DM_GOURAUD
+         - DM_FLAT
+         - DM_FRINGE
+         - DM_WIRE
+         - DM_COL_WIRE
+         - DM_POLY
+         - DM_WIRE_POLY
+         - DM_GRID_WIRE
+         - DM_GRID_SURF
+         
+        @param id surface id (<= 0 for all)
+        @param style draw style
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        """
+        Debug.msg(3, "Nviz::SetSurfaceStyle(): id=%d, style=%d",
+                  id, style)
+        
+        if id > 0:
+            if not GS_surf_exists(id):
+                return -1
+            
+            if GS_set_drawmode(id, style) < 0:
+                return -2
+            
+            return 1
+        
+        if GS_setall_drawmode(style) < 0:
+            return -2
+        
+        return 1
+    
+    def SetWireColor(self, id, color_str):
+        """!Set color of wire
+        
+        @todo all
+         
+        @param surface id (< 0 for all)
+        @param color color string (R:G:B)
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting attributes failed
+        @return 1 on success
+        @return 0 on failure
+        """
+        Debug.msg(3, "Nviz::SetWireColor(): id=%d, color=%s",
+                  id, color_str)
+        
+        color = Nviz_color_from_str(color_str)
+        
+        if id > 0:
+            if not GS_surf_exists(id):
+                return -1
+            
+            GS_set_wire_color(id, color)
+        else:
+            nsurfs = c_int()
+            surf_list = GS_get_surf_list(byref(nsurfs))
+            for i in xrange(nsurfs.value):
+                id = surf_list[i]
+                GS_set_wire_color(id, color)
+            
+            G_free(surf_list)
+            surf_list = None
+        
+        return 1
+    
+    def GetSurfacePosition(self, id):
+        """!Get surface position
+        
+        @param id surface id
+        
+        @return x,y,z
+        @return zero-length vector on error
+        """
+        if not GS_surf_exists(id):
+            return []
+        
+        x, y, z = c_float(), c_float(), c_float()
+        GS_get_trans(id, byref(x), byref(y), byref(z))
+        
+        Debug.msg(3, "Nviz::GetSurfacePosition(): id=%d, x=%f, y=%f, z=%f",
+                  id, x.value, y.value, z.value)
+        
+        return [x.value, y.value, z.value]
+
+    def SetSurfacePosition(self, id, x, y, z):
+        """!Set surface position
+        
+        @param id surface id
+        @param x,y,z translation values
+        
+        @return 1 on success
+        @return -1 surface not found
+        @return -2 setting position failed
+        """
+        if not GS_surf_exists(id):
+            return -1
+        
+        Debug.msg(3, "Nviz::SetSurfacePosition(): id=%d, x=%f, y=%f, z=%f",
+                  id, x, y, z)
+        
+        GS_set_trans(id, x, y, z)
+        
+        return 1
+
+    def SetVectorLineMode(self, id, color_str, width, flat):
+        """!Set mode of vector line overlay
+        
+        @param id vector id
+        @param color_str color string
+        @param width line width
+        @param flat display flat or on surface
+        
+        @return -1 vector set not found
+        @return -2 on failure
+        @return 1 on success
+        """
+        if not GV_vect_exists(id):
+            return -1
+        
+        Debug.msg(3, "Nviz::SetVectorMode(): id=%d, color=%s, width=%d, flat=%d",
+                  id, color_str, width, flat)
+        
+        color = Nviz_color_from_str(color_str)
+        
+        # use memory by default
+        if GV_set_vectmode(id, 1, color, width, flat) < 0:
+            return -2
+        
+        return 1
+
+    def SetVectorLineHeight(self, id, height):
+        """!Set vector height above surface (lines)
+        
+        @param id vector set id
+        @param height
+        
+        @return -1 vector set not found
+        @return 1 on success
+        """
+        if not GV_vect_exists(id):
+            return -1
+        
+        Debug.msg(3, "Nviz::SetVectorLineHeight(): id=%d, height=%f",
+                  id, height)
+        
+        GV_set_trans(id, 0.0, 0.0, height)
+        
+        return 1
+
+    def SetVectorLineSurface(self, id, surf_id):
+        """!Set reference surface of vector set (lines)
+        
+        @param id vector set id
+        @param surf_id surface id
+        
+        @return 1 on success
+        @return -1 vector set not found
+        @return -2 surface not found
+        @return -3 on failure
+        """
+        if not GV_vect_exists(id):
+            return -1
+        
+        if not GS_surf_exists(surf_id):
+            return -2
+        
+        if GV_select_surf(id, surf_id) < 0:
+            return -3
+        
+        return 1
+
+    def UnsetVectorLineSurface(self, id, surf_id):
+        """!Unset reference surface of vector set (lines)
+        
+        @param id vector set id
+        @param surf_id surface id
+        
+        @return 1 on success
+        @return -1 vector set not found
+        @return -2 surface not found
+        @return -3 on failure
+        """
+        if not GV_vect_exists(id):
+            return -1
+        
+        if not GS_surf_exists(surf_id):
+            return -2
+        
+        if GV_unselect_surf(id, surf_id) < 0:
+            return -3
+        
+        return 1
+        
+    def SetVectorPointMode(self, id, color_str, width, size, marker):
+        """!Set mode of vector point overlay
+        
+        @param id vector id
+        @param color_str color string
+        @param width line width
+        @param flat
+        
+        @return -1 vector set not found
+        """
+        if not GP_site_exists(id):
+            return -1
+        
+        Debug.msg(3, "Nviz::SetVectorPointMode(): id=%d, color=%s, "
+                  "width=%d, size=%f, marker=%d",
+                  id, color_str, width, size, marker)
+        
+        color = Nviz_color_from_str(color_str)
+        
+        if GP_set_sitemode(id, ST_ATT_NONE, color, width, size, marker) < 0:
+            return -2
+        
+        return 1
+
+    def SetVectorPointHeight(self, id, height):
+        """!Set vector height above surface (points)
+        
+        @param id vector set id
+        @param height
+        
+        @return -1 vector set not found
+        @return 1 on success
+        """
+        if not GP_site_exists(id):
+            return -1
+        
+        Debug.msg(3, "Nviz::SetVectorPointHeight(): id=%d, height=%f",
+                  id, height)
+        
+        GP_set_trans(id, 0.0, 0.0, height)
+        
+        return 1
+
+    def SetVectorPointSurface(self, id, surf_id):
+        """!Set reference surface of vector set (points)
+        
+        @param id vector set id
+        @param surf_id surface id
+        
+        @return 1 on success
+        @return -1 vector set not found
+        @return -2 surface not found
+        @return -3 on failure
+        """
+        if not GP_site_exists(id):
+            return -1
+        
+        if not GS_surf_exists(surf_id):
+            return -2
+        
+        if GP_select_surf(id, surf_id) < 0:
+            return -3
+        
+        return 1
+
+    def ReadVectorColors(self, name, mapset):
+        """!Read vector colors
+        
+        @param name vector map name
+        @mapset mapset name ("" for search path)
+        
+        @return -1 on error 
+        @return 0 if color table missing 
+        @return 1 on success (color table found) 
+        """
+        return Vect_read_colors(name, mapset, self.color)
+        
+    def CheckColorTable(self, id, type):
+        """!Check if color table exists.
+        
+        @param id vector set id
+        @param type vector set type (lines/points)
+        
+        @return 1 color table exists
+        @return 0 no color table found
+        @return -1 on error
+        @return -2 vector set not found
+        """
+        file = c_char_p()
+        
+        if type == 'points':
+            ret = GP_get_sitename(id, byref(file))
+        elif type == 'lines':
+            ret = GV_get_vectname(id, byref(file))
+            
+        if ret < 0:
+            return -2
+        
+        return self.ReadVectorColors(file, "")
+        
+    def UnsetVectorPointSurface(self, id, surf_id):
+        """!Unset reference surface of vector set (points)
+        
+        @param id vector set id
+        @param surf_id surface id
+        
+        @return 1 on success
+        @return -1 vector set not found
+        @return -2 surface not found
+        @return -3 on failure
+        """
+        if not GP_site_exists(id):
+            return -1
+        
+        if not GS_surf_exists(surf_id):
+            return -2
+        
+        if GP_unselect_surf(id, surf_id) < 0:
+            return -3
+        
+        return 1
+        
+    def AddIsosurface(self, id, level, isosurf_id = None):
+        """!Add new isosurface
+        
+        @param id volume id
+        @param level isosurface level (topography)
+        
+        @return -1 on failure
+        @return 1 on success
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if isosurf_id is not None:
+            num = GVL_isosurf_num_isosurfs(id)
+            if num < 0 or isosurf_id != num:
+                return -1
+                
+        if GVL_isosurf_add(id) < 0:
+            return -1
+        
+        # set topography level
+        nisosurfs = GVL_isosurf_num_isosurfs(id)
+        
+        return GVL_isosurf_set_att_const(id, nisosurfs - 1, ATT_TOPO, level)
+    
+    def AddSlice(self, id, slice_id = None):
+        """!Add new slice
+        
+        @param id volume id
+        
+        @return -1 on failure
+        @return number of slices
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if slice_id is not None:
+            num = GVL_slice_num_slices(id)
+            if num < 0 or slice_id != num:
+                return -1
+                
+        if GVL_slice_add(id) < 0:
+            return -1
+        
+        return GVL_slice_num_slices(id)
+    
+    def DeleteIsosurface(self, id, isosurf_id):
+        """!Delete isosurface
+        
+        @param id volume id
+        @param isosurf_id isosurface id
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if isosurf_id > GVL_isosurf_num_isosurfs(id):
+            return -2
+        
+        ret = GVL_isosurf_del(id, isosurf_id)
+        
+        if ret < 0:
+            return -3
+
+        return 1
+    
+    def DeleteSlice(self, id, slice_id):
+        """!Delete slice
+        
+        @param id volume id
+        @param slice_id slice id
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 slice not found
+        @return -3 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if slice_id > GVL_slice_num_slices(id):
+            return -2
+        
+        ret = GVL_slice_del(id, slice_id)
+        
+        if ret < 0:
+            return -3
+
+        return 1
+    
+    def MoveIsosurface(self, id, isosurf_id, up):
+        """!Move isosurface up/down in the list
+        
+        @param id volume id
+        @param isosurf_id isosurface id
+        @param up if true move up otherwise down
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if isosurf_id > GVL_isosurf_num_isosurfs(id):
+            return -2
+        
+        if up:
+            ret = GVL_isosurf_move_up(id, isosurf_id)
+        else:
+            ret = GVL_isosurf_move_down(id, isosurf_id)
+        
+        if ret < 0:
+            return -3
+
+        return 1
+
+    def MoveSlice(self, id, slice_id, up):
+        """!Move slice up/down in the list
+        
+        @param id volume id
+        @param slice_id slice id
+        @param up if true move up otherwise down
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 slice not found
+        @return -3 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if slice_id > GVL_slice_num_slices(id):
+            return -2
+        
+        if up:
+            ret = GVL_slice_move_up(id, slice_id)
+        else:
+            ret = GVL_slice_move_down(id, slice_id)
+        
+        if ret < 0:
+            return -3
+
+        return 1
+    
+    def SetIsosurfaceTopo(self, id, isosurf_id, map, value):
+        """!Set isosurface level
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        return self.SetIsosurfaceAttr(id, isosurf_id, ATT_TOPO, map, value)
+    
+    def SetIsosurfaceColor(self, id, isosurf_id, map, value):
+        """!Set isosurface color
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        return self.SetIsosurfaceAttr(id, isosurf_id, ATT_COLOR, map, value)
+    
+    def SetIsosurfaceMask(self, id, isosurf_id, invert, value):
+        """!Set isosurface mask
+        
+        @todo invert
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param invert true for invert mask
+        @param value map name to be used for mask
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        return self.SetIsosurfaceAttr(id, isosurf_id, ATT_MASK, True, value)
+    
+    def SetIsosurfaceTransp(self, id, isosurf_id, map, value):
+        """!Set isosurface transparency
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        return self.SetIsosurfaceAttr(id, isosurf_id, ATT_TRANSP, map, value)
+    
+    def SetIsosurfaceShine(self, id, isosurf_id, map, value):
+        """!Set isosurface shininess
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        return self.SetIsosurfaceAttr(id, isosurf_id, ATT_SHINE, map, value)
+    
+    def SetIsosurfaceEmit(self, id, isosurf_id, map, value):
+        """!Set isosurface emission (currently unused)
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        return self.SetIsosurfaceAttr(id, isosurf_id, ATT_EMIT, map, value)
+    
+    def SetIsosurfaceAttr(self, id, isosurf_id, attr, map, value):
+        """!Set isosurface attribute
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param attr attribute desc
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 setting attributes failed
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if isosurf_id > GVL_isosurf_num_isosurfs(id) - 1:
+            return -2
+        
+        if map:
+            ret = GVL_isosurf_set_att_map(id, isosurf_id, attr, value)
+        else:
+            if attr == ATT_COLOR:
+                val = Nviz_color_from_str(value)
+            else:
+                val = float(value)
+            
+            ret = GVL_isosurf_set_att_const(id, isosurf_id, attr, val)
+        
+        Debug.msg(3, "Nviz::SetIsosurfaceAttr(): id=%d, isosurf=%d, "
+                  "attr=%d, map=%s, value=%s",
+                  id, isosurf_id, attr, map, value)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
+    def UnsetIsosurfaceMask(self, id, isosurf_id):
+        """!Unset isosurface mask
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 setting attributes failed
+        """
+        return self.UnsetIsosurfaceAttr(id, isosurf_id, ATT_MASK)
+    
+    def UnsetIsosurfaceTransp(self, id, isosurf_id):
+        """!Unset isosurface transparency
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 setting attributes failed
+        """
+        return self.UnsetIsosurfaceAttr(id, isosurf_id, ATT_TRANSP)
+    
+    def UnsetIsosurfaceEmit(self, id, isosurf_id):
+        """!Unset isosurface emission (currently unused)
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 setting attributes failed
+        """
+        return self.UnsetIsosurfaceAttr(id, isosurf_id, ATT_EMIT)
+    
+    def UnsetIsosurfaceAttr(self, id, isosurf_id, attr):
+        """!Unset surface attribute
+        
+        @param id surface id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param attr attribute descriptor
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -2 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if isosurf_id > GVL_isosurf_num_isosurfs(id) - 1:
+            return -2
+        
+        Debug.msg(3, "Nviz::UnsetSurfaceAttr(): id=%d, isosurf_id=%d, attr=%d",
+                  id, isosurf_id, attr)
+        
+        ret = GVL_isosurf_unset_att(id, isosurf_id, attr)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+
+    def SetIsosurfaceMode(self, id, mode):
+        """!Set draw mode for isosurfaces
+        
+        @param mode
+        
+        @return 1 on success
+        @return -1 volume set not found
+        @return -2 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        ret = GVL_isosurf_set_drawmode(id, mode)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
+    def SetSliceMode(self, id, mode):
+        """!Set draw mode for slices
+        
+        @param mode
+        
+        @return 1 on success
+        @return -1 volume set not found
+        @return -2 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        ret = GVL_slice_set_drawmode(id, mode)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
+    def SetIsosurfaceRes(self, id, res):
+        """!Set draw resolution for isosurfaces
+        
+        @param res resolution value
+        
+        @return 1 on success
+        @return -1 volume set not found
+        @return -2 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        ret = GVL_isosurf_set_drawres(id, res, res, res)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
+    def SetSliceRes(self, id, res):
+        """!Set draw resolution for slices
+        
+        @param res resolution value
+        
+        @return 1 on success
+        @return -1 volume set not found
+        @return -2 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        ret = GVL_slice_set_drawres(id, res, res, res)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
+    def SetSlicePosition(self, id, slice_id, x1, x2, y1, y2, z1, z2, dir):
+        """!Set slice position
+        
+        @param id volume id
+        @param slice_id slice id
+        @param x1,x2,y1,y2,z1,z2 slice coordinates
+        @param dir axis
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 slice not found
+        @return -3 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if slice_id > GVL_slice_num_slices(id):
+            return -2
+        
+        ret = GVL_slice_set_pos(id, slice_id, x1, x2, y1, y2, z1, z2, dir)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
+    def SetSliceTransp(self, id, slice_id, value):
+        """!Set slice transparency
+        
+        @param id volume id
+        @param slice_id slice id
+        @param x1,x2,y1,y2,z1,z2 slice coordinates
+        @param value transparency value (0 - 255)
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 slice not found
+        @return -3 on failure
+        """
+        
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if slice_id > GVL_slice_num_slices(id):
+            return -2
+        
+        ret = GVL_slice_set_transp(id, slice_id, value)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
+    def SetIsosurfaceInOut(self, id, isosurf_id, inout):
+        """!Set inout mode
+        
+        @param inout mode true/false
+        
+        @return 1 on success
+        @return -1 volume set not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if isosurf_id > GVL_isosurf_num_isosurfs(id) - 1:
+            return -2
+        
+        ret = GVL_isosurf_set_flags(id, isosurf_id, inout)
+        
+        if ret < 0:
+            return -3
+        
+        return 1
+    
+    def GetVolumePosition(self, id):
+        """!Get volume position
+        
+        @param id volume id
+        
+        @return x,y,z
+        @return zero-length vector on error
+        """
+        if not GVL_vol_exists(id):
+            return []
+        
+        x, y, z = c_float(), c_float(), c_float()
+        GVL_get_trans(id, byref(x), byref(y), byref(z))
+        
+        Debug.msg(3, "Nviz::GetVolumePosition(): id=%d, x=%f, y=%f, z=%f",
+                  id, x.value, y.value, z.value)
+        
+        return [x.value, y.value, z.value]
+    
+    def SetVolumePosition(self, id, x, y, z):
+        """!Set volume position
+        
+        @param id volume id
+        @param x,y,z translation values
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 setting position failed
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        Debug.msg(3, "Nviz::SetVolumePosition(): id=%d, x=%f, y=%f, z=%f",
+                  id, x, y, z)
+        
+        GVL_set_trans(id, x, y, z)
+        
+        return 1
+    
+    def GetCPlaneCurrent(self):
+        return Nviz_get_current_cplane(self.data)
+    
+    def GetCPlanesCount(self):
+        """!Returns number of cutting planes"""
+        return Nviz_num_cplanes(self.data) 
+    
+    def GetCPlaneRotation(self):
+        """!Returns rotation parameters of current cutting plane"""
+        x, y, z = c_float(), c_float(), c_float()
+        
+        current = Nviz_get_current_cplane(self.data)
+        Nviz_get_cplane_rotation(self.data, current, byref(x), byref(y), byref(z))
+        
+        return x.value, y.value, z.value
+    
+    def GetCPlaneTranslation(self):
+        """!Returns translation parameters of current cutting plane"""
+        x, y, z = c_float(), c_float(), c_float()
+        
+        current = Nviz_get_current_cplane(self.data)
+        Nviz_get_cplane_translation(self.data, current, byref(x), byref(y), byref(z))
+        
+        return x.value, y.value, z.value
+    
+    def SetCPlaneRotation(self, x, y, z):
+        """!Set current clip plane rotation
+        
+        @param x,y,z rotation parameters
+        """
+        current = Nviz_get_current_cplane(self.data)
+        Nviz_set_cplane_rotation(self.data, current, x, y, z)
+        Nviz_draw_cplane(self.data, -1, -1)
+    
+    def SetCPlaneTranslation(self, x, y, z):
+        """!Set current clip plane translation
+        
+        @param x,y,z translation parameters
+        """
+        current = Nviz_get_current_cplane(self.data)
+        Nviz_set_cplane_translation(self.data, current, x, y, z)
+        Nviz_draw_cplane(self.data, -1, -1) 
+        Debug.msg(3, "Nviz::SetCPlaneTranslation(): id=%d, x=%f, y=%f, z=%f",
+                  current, x, y, z)
+                
+    def SetCPlaneInteractively(self, x, y):
+        current = Nviz_get_current_cplane(self.data)
+        ret = Nviz_set_cplane_here(self.data, current, x, y)
+        if ret:
+            Nviz_draw_cplane(self.data, -1, -1)
+            x, y, z = self.GetCPlaneTranslation()
+            return x, y, z
+        else:
+            return None, None, None
+        
+        
+    def SelectCPlane(self, index):
+        """!Select cutting plane
+        
+        @param index index of cutting plane
+        """
+        Nviz_on_cplane(self.data, index)
+    
+    def UnselectCPlane(self, index):
+        """!Unselect cutting plane
+        
+        @param index index of cutting plane
+        """
+        Nviz_off_cplane(self.data, index)
+        
+    def SetFenceColor(self, index):
+        """!Select current cutting plane
+        
+        @param index type of fence - from 0 (off) to 4
+        """    
+        Nviz_set_fence_color(self.data, index)
+            
+    def GetXYRange(self):
+        """!Get xy range"""
+        return Nviz_get_xyrange(self.data)
+    
+    def GetZRange(self):
+        """!Get z range"""
+        min, max = c_float(), c_float()
+        Nviz_get_zrange(self.data, byref(min), byref(max))
+        return min.value, max.value
+    
+    def SaveToFile(self, filename, width = 20, height = 20, itype = 'ppm'):
+        """!Save current GL screen to ppm/tif file
+
+        @param filename file name
+        @param width image width
+        @param height image height
+        @param itype image type ('ppm' or 'tif')
+        """
+        widthOrig  = self.width
+        heightOrig = self.height
+        
+        self.ResizeWindow(width, height)
+        GS_clear(Nviz_get_bgcolor(self.data))
+        self.Draw(False, -1)
+        if itype == 'ppm':
+            GS_write_ppm(filename)
+        else:
+            GS_write_tif(filename)
+        
+        self.ResizeWindow(widthOrig, heightOrig)
+
+    def DrawLightingModel(self):
+        """!Draw lighting model"""
+        if self.showLight:
+            Nviz_draw_model(self.data)
+
+    def DrawFringe(self):
+        """!Draw fringe"""
+        Nviz_draw_fringe(self.data)
+        
+    def SetFringe(self, sid, color, elev, nw = False, ne = False, sw = False, se = False):
+        """!Set fringe
+
+        @param sid surface id
+        @param color color
+        @param elev elevation (height)
+        @param nw,ne,sw,se fringe edges (turn on/off)
+        """
+        scolor = str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
+        Nviz_set_fringe(self.data,
+                        sid, Nviz_color_from_str(scolor),
+                        elev, int(nw), int(ne), int(sw), int(se))
+    
+    def DrawArrow(self):
+        """!Draw north arrow
+        """
+        return Nviz_draw_arrow(self.data)
+        
+    def SetArrow(self, sx, sy, size, color):
+        """!Set north arrow from canvas coordinates
+        
+        @param sx,sy canvas coordinates
+        @param size arrow length
+        @param color arrow color
+        """
+        return Nviz_set_arrow(self.data, sx, sy, size, Nviz_color_from_str(color))       
+        
+    def DeleteArrow(self):
+        """!Delete north arrow
+        """
+        Nviz_delete_arrow(self.data)
+    
+    def SetScalebar(self, id, sx, sy, size, color):
+        """!Set scale bar from canvas coordinates
+        
+        @param sx,sy canvas coordinates
+        @param id scale bar id
+        @param size scale bar length
+        @param color scale bar color
+        """
+        return Nviz_set_scalebar(self.data, id, sx, sy, size, Nviz_color_from_str(color))
+    
+    def DrawScalebar(self):
+        """!Draw scale bar
+        """
+        return Nviz_draw_scalebar(self.data)
+    
+    def DeleteScalebar(self, id):
+        """!Delete scalebar
+        """
+        Nviz_delete_scalebar(self.data, id)
+        
+    def GetPointOnSurface(self, sx, sy):
+        """!Get point on surface
+
+        @param sx,sy canvas coordinates (LL)
+        """
+        sid = c_int()
+        x   = c_float()
+        y   = c_float()
+        z   = c_float()
+        Debug.msg(5, "Nviz::GetPointOnSurface(): sx=%d sy=%d" % (sx, sy))
+        num = GS_get_selected_point_on_surface(sx, sy, byref(sid), byref(x), byref(y), byref(z))
+        if num == 0:
+            return (None, None, None, None)
+        
+        return (sid.value, x.value, y.value, z.value)
+
+    def QueryMap(self, sx, sy):
+        """!Query surface map
+
+        @param sx,sy canvas coordinates (LL)
+        """
+        sid, x, y, z = self.GetPointOnSurface(sx, sy)
+        if not sid:
+            return None
+        
+        catstr = create_string_buffer(256)
+        valstr = create_string_buffer(256)
+        GS_get_cat_at_xy(sid, ATT_TOPO, catstr, x, y)
+        GS_get_val_at_xy(sid, ATT_COLOR, valstr, x, y)
+        
+        return { 'id' : sid,
+                 'x'  : x,
+                 'y'  : y,
+                 'z'  : z,
+                 'elevation' : catstr.value.replace('(', '').replace(')', ''),
+                 'color'     : valstr.value }
+    
+    def GetDistanceAlongSurface(self, sid, p1, p2, useExag = True):
+        """!Get distance measured along surface"""
+        d = c_float()
+        
+        GS_get_distance_alongsurf(sid, p1[0], p1[1], p2[0], p2[1],
+                                  byref(d), int(useExag))
+        
+        return d.value
+
+    def GetRotationParameters(self, dx, dy):
+        """!Get rotation parameters (angle, x, y, z axes)
+        
+        @param dx,dy difference from previous mouse drag event
+        """
+        modelview = (c_double * 16)()
+        Nviz_get_modelview(byref(modelview))
+        
+        angle = sqrt(dx*dx+dy*dy)/float(self.width+1)*180.0
+        m = []
+        row = []
+        for i, item in enumerate(modelview):
+            row.append(item)
+            if (i+1) % 4 == 0:
+                m.append(row)
+                row = []
+        inv = matrix(m).I
+        ax, ay, az = dy, dx, 0.
+        x = inv[0,0]*ax + inv[1,0]*ay + inv[2,0]*az
+        y = inv[0,1]*ax + inv[1,1]*ay + inv[2,1]*az
+        z = inv[0,2]*ax + inv[1,2]*ay + inv[2,2]*az
+        
+        return angle, x, y, z 
+       
+    def Rotate(self, angle, x, y, z):
+        """!Set rotation parameters
+        Rotate scene (difference from current state).
+
+        @param angle angle
+        @param x,y,z axis coordinate
+        """
+        Nviz_set_rotation(angle, x, y, z)
+        
+    def UnsetRotation(self):
+        """!Stop rotating the scene"""
+        Nviz_unset_rotation()
+        
+    def ResetRotation(self):
+        """!Reset scene rotation"""
+        Nviz_init_rotation()
+        
+    def GetRotationMatrix(self):
+        """!Get rotation matrix"""
+        matrix = (c_double * 16)()
+        GS_get_rotation_matrix(byref(matrix))
+        returnMatrix = []
+        for item in matrix:
+            returnMatrix.append(item)
+        return returnMatrix
+        
+    def SetRotationMatrix(self, matrix):
+        """!Set rotation matrix"""
+        mtrx = (c_double * 16)()
+        for i in range(len(matrix)):
+            mtrx[i] = matrix[i]
+        GS_set_rotation_matrix(byref(mtrx))
+    
+    def Start2D(self):
+        Nviz_set_2D(self.width, self.height)
+        
+    def FlyThrough(self, flyInfo, mode, exagInfo):
+        """!Fly through the scene
+        
+        @param flyInfo fly parameters
+        @param mode 0 or 1 for different fly behaviour
+        @param exagInfo parameters changing fly speed
+        """
+        fly = (c_float * 3)()
+        for i, item in enumerate(flyInfo):
+            fly[i] = item
+        exag = (c_int * 2)()
+        exag[0] = int(exagInfo['move'])
+        exag[1] = int(exagInfo['turn'])
+        Nviz_flythrough(self.data, fly, exag, mode)
+        
+class Texture(object):
+    """!Class representing OpenGL texture"""
+    def __init__(self, filepath, overlayId, coords):
+        """!Load image to texture
+
+        @param filepath path to image file
+        @param overlayId id of overlay (1 for legend, 101 and more for text)
+        @param coords image coordinates
+        """
+        self.path = filepath
+        self.image = wx.Image(filepath, wx.BITMAP_TYPE_ANY)
+        self.width = self.image.GetWidth()
+        self.height = self.image.GetHeight()
+        self.id = overlayId
+        self.coords = list(coords)
+        self.bounds = wx.Rect()
+        self.active = True
+        
+        # alpha needs to be initialized
+        if not self.image.HasAlpha():
+            self.image.InitAlpha()
+    
+        # resize image to match 2^n
+        self.Resize()
+        
+        # check max texture size
+        maxSize = c_int()
+        Nviz_get_max_texture(byref(maxSize))
+        self.maxSize = maxSize.value
+        if self.maxSize < self.width or self.maxSize < self.height:
+            # TODO: split up image 
+            self.textureId = None
+        else:
+            self.textureId = self.Load()
+            
+    def __del__(self):
+        """!Delete texture"""
+        if self.textureId:
+            Nviz_del_texture(self.textureId)
+        grass.try_remove(self.path)
+            
+    def Resize(self):    
+        """!Resize image to match 2^n"""
+        n = m = 1
+        while self.width > pow(2,n):
+            n += 1
+        while self.height > pow(2,m):
+            m += 1
+        self.image.Resize(size = (pow(2,n), pow(2,m)), pos = (0, 0))
+        self.width = self.image.GetWidth()
+        self.height = self.image.GetHeight()
+        
+    def Load(self):
+        """!Load image to texture"""  
+        if self.image.HasAlpha():
+            bytesPerPixel = 4
+        else:
+            bytesPerPixel = 3
+        bytes = bytesPerPixel * self.width * self.height
+        rev_val = self.height - 1
+        im = (c_ubyte * bytes)()
+        bytes3 = 3 * self.width * self.height
+        bytes1 = self.width * self.height
+        imageData = struct.unpack(str(bytes3) + 'B', self.image.GetData())
+        if self.image.HasAlpha():
+            alphaData = struct.unpack(str(bytes1) + 'B', self.image.GetAlphaData())
+        
+        # this takes too much time
+        wx.BeginBusyCursor()
+        for i in range(self.height):
+            for j in range(self.width):
+                im[(j + i * self.width) * bytesPerPixel + 0] = imageData[( j + (rev_val - i) * self.width) * 3 + 0]
+                im[(j + i * self.width) * bytesPerPixel + 1] = imageData[( j + (rev_val - i) * self.width) * 3 + 1]
+                im[(j + i * self.width) * bytesPerPixel + 2] = imageData[( j + (rev_val - i) * self.width) * 3 + 2]
+                if self.image.HasAlpha():
+                    im[(j + i * self.width) * bytesPerPixel + 3] = alphaData[( j + (rev_val - i) * self.width)]
+        wx.EndBusyCursor()
+        
+        id = Nviz_load_image(im, self.width, self.height, self.image.HasAlpha())
+        
+        return id
+        
+    def Draw(self):
+        """!Draw texture as an image"""
+        Nviz_draw_image(self.coords[0], self.coords[1], self.width, self.height, self.textureId)
+    
+        
+    def SetBounds(self, rect):
+        """!Set Bounding Rectangle"""
+        self.bounds = rect
+        
+    def HitTest(self, x, y, radius):
+        copy = wx.Rect(*self.bounds)
+        copy.Inflate(radius, radius)
+        return copy.ContainsXY(x, y)
+    
+    def MoveTexture(self, dx, dy):
+        """!Move texture on the screen"""
+        self.coords[0] += dx
+        self.coords[1] += dy
+        self.bounds.OffsetXY(dx, dy)
+    
+    def SetCoords(self, coords):
+        """!Set coordinates"""
+        dx = coords[0] - self.coords[0]
+        dy = coords[1] - self.coords[1]
+        self.MoveTexture(dx, dy)
+        
+    def GetId(self):
+        """!Returns image id."""
+        return self.id
+    
+    def SetActive(self, active = True):
+        self.active = active
+        
+    def IsActive(self):
+        return self.active
+        
+class ImageTexture(Texture):
+    """!Class representing OpenGL texture as an overlay image"""
+    def __init__(self, filepath, overlayId, coords, cmd):
+        """!Load image to texture
+
+        @param filepath path to image file
+        @param overlayId id of overlay (1 for legend)
+        @param coords image coordinates
+        @param cmd d.legend command      
+        """
+        Texture.__init__(self, filepath = filepath, overlayId = overlayId, coords = coords)
+        
+        self.cmd = cmd
+        
+    def GetCmd(self):
+        """!Returns overlay command."""
+        return self.cmd
+        
+    def Corresponds(self, item):
+        return sorted(self.GetCmd()) == sorted(item.GetCmd())
+        
+class TextTexture(Texture):
+    """!Class representing OpenGL texture as a text label"""
+    def __init__(self, filepath, overlayId, coords, textDict):
+        """!Load image to texture
+
+        @param filepath path to image file
+        @param overlayId id of overlay (101 and more for text)
+        @param coords text coordinates
+        @param textDict text properties      
+        """
+        Texture.__init__(self, filepath = filepath, overlayId = overlayId, coords = coords)
+        
+        self.textDict = textDict
+        
+    def GetTextDict(self):
+        """!Returns text properties."""
+        return self.textDict
+        
+        
+    def Corresponds(self, item):
+        t = self.GetTextDict()
+        for prop in t.keys():
+            if prop in ('coords','bbox'): continue
+            if t[prop] != item[prop]:
+                return False
+                
+        return True


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/psmap/dialogs.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/psmap/dialogs.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/psmap/dialogs.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,4969 @@
+"""!
+ at package psmap.dialogs
+
+ at brief dialogs for wxPsMap
+
+Classes:
+ - dialogs::TCValidator
+ - dialogs::PenStyleComboBox
+ - dialogs::CheckListCtrl
+ - dialogs::PsmapDialog
+ - dialogs::PageSetupDialog
+ - dialogs::MapDialog
+ - dialogs::MapFramePanel
+ - dialogs::RasterPanel
+ - dialogs::VectorPanel
+ - dialogs::RasterDialog
+ - dialogs::MainVectorDialog
+ - dialogs::VPropertiesDialog
+ - dialogs::LegendDialog
+ - dialogs::MapinfoDialog
+ - dialogs::ScalebarDialog
+ - dialogs::TextDialog
+ - dialogs::ImageDialog
+ - dialogs::NorthArrowDialog
+ - dialogs::PointDialog
+ - dialogs::RectangleDialog
+
+(C) 2011-2012 by Anna Kratochvilova, and 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 Anna Kratochvilova <kratochanna gmail.com> (bachelor's project)
+ at author Martin Landa <landa.martin gmail.com> (mentor)
+"""
+
+import os
+import sys
+import string
+from copy import deepcopy
+
+import wx
+import wx.lib.scrolledpanel    as scrolled
+import wx.lib.filebrowsebutton as filebrowse
+from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin
+from wx.lib.expando         import ExpandoTextCtrl, EVT_ETC_LAYOUT_NEEDED
+try:
+    import wx.lib.agw.floatspin as fs
+except ImportError:
+    fs = None
+
+import grass.script as grass
+
+from core               import globalvar
+from dbmgr.vinfo        import VectorDBInfo
+from gui_core.gselect   import Select
+from core.gcmd          import RunCommand, GError, GMessage
+from gui_core.dialogs   import SymbolDialog
+from psmap.utils        import *
+from psmap.instructions import *
+
+# grass.set_raise_on_error(True)
+
+PSMAP_COLORS = ['aqua', 'black', 'blue', 'brown', 'cyan', 'gray', 'grey', 'green', 'indigo',
+                'magenta','orange', 'purple', 'red', 'violet', 'white', 'yellow']
+
+    
+class TCValidator(wx.PyValidator):
+    """!validates input in textctrls, combobox, taken from wxpython demo"""
+    def __init__(self, flag = None):
+        wx.PyValidator.__init__(self)
+        self.flag = flag
+        self.Bind(wx.EVT_CHAR, self.OnChar)
+
+    def Clone(self):
+        return TCValidator(self.flag)
+
+    def Validate(self, win):
+        
+        tc = self.GetWindow()
+        val = tc.GetValue()
+
+        if self.flag == 'DIGIT_ONLY':
+            for x in val:
+                if x not in string.digits:
+                    return False
+        return True
+
+    def OnChar(self, event):
+        key = event.GetKeyCode()
+        if key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255:
+            event.Skip()
+            return
+        if self.flag == 'DIGIT_ONLY' and chr(key) in string.digits + '.-':
+            event.Skip()
+            return
+##        if self.flag == 'SCALE' and chr(key) in string.digits + ':':
+##            event.Skip()
+##            return
+        if self.flag == 'ZERO_AND_ONE_ONLY' and chr(key) in '01':
+            event.Skip()
+            return
+        if not wx.Validator_IsSilent():
+            wx.Bell()
+        # Returning without calling even.Skip eats the event before it
+        # gets to the text control
+        return  
+
+
+class PenStyleComboBox(wx.combo.OwnerDrawnComboBox):
+    """!Combo for selecting line style, taken from wxpython demo"""
+
+    # Overridden from OwnerDrawnComboBox, called to draw each
+    # item in the list
+    def OnDrawItem(self, dc, rect, item, flags):
+        if item == wx.NOT_FOUND:
+            # painting the control, but there is no valid item selected yet
+            return
+
+        r = wx.Rect(*rect)  # make a copy
+        r.Deflate(3, 5)
+
+        penStyle = wx.SOLID
+        if item == 1:
+            penStyle = wx.LONG_DASH
+        elif item == 2:
+            penStyle = wx.DOT
+        elif item == 3:
+            penStyle = wx.DOT_DASH
+
+        pen = wx.Pen(dc.GetTextForeground(), 3, penStyle)
+        dc.SetPen(pen)
+
+        # for painting the items in the popup
+        dc.DrawText(self.GetString(item ),
+                    r.x + 3,
+                    (r.y + 0) + ((r.height/2) - dc.GetCharHeight() )/2
+                    )
+        dc.DrawLine(r.x+5, r.y+((r.height/4)*3)+1, r.x+r.width - 5, r.y+((r.height/4)*3)+1 )
+
+        
+    def OnDrawBackground(self, dc, rect, item, flags):
+        """!Overridden from OwnerDrawnComboBox, called for drawing the
+        background area of each item."""
+        # If the item is selected, or its item # iseven, or we are painting the
+        # combo control itself, then use the default rendering.
+        if (item & 1 == 0 or flags & (wx.combo.ODCB_PAINTING_CONTROL |
+                                      wx.combo.ODCB_PAINTING_SELECTED)):
+            wx.combo.OwnerDrawnComboBox.OnDrawBackground(self, dc, rect, item, flags)
+            return
+
+        # Otherwise, draw every other background with different colour.
+        bgCol = wx.Colour(240,240,250)
+        dc.SetBrush(wx.Brush(bgCol))
+        dc.SetPen(wx.Pen(bgCol))
+        dc.DrawRectangleRect(rect);
+
+    def OnMeasureItem(self, item):
+        """!Overridden from OwnerDrawnComboBox, should return the height
+        needed to display an item in the popup, or -1 for default"""
+        return 30
+
+    def OnMeasureItemWidth(self, item):
+        """!Overridden from OwnerDrawnComboBox.  Callback for item width, or
+        -1 for default/undetermined"""
+        return -1; # default - will be measured from text width  
+    
+    
+class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin):
+    """!List control for managing order and labels of vector maps in legend"""
+    def __init__(self, parent):
+        wx.ListCtrl.__init__(self, parent, id = wx.ID_ANY, 
+                             style = wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.BORDER_SUNKEN|wx.LC_VRULES|wx.LC_HRULES)
+        CheckListCtrlMixin.__init__(self) 
+        ListCtrlAutoWidthMixin.__init__(self)
+        
+
+class PsmapDialog(wx.Dialog):
+    def __init__(self, parent, id,  title, settings, apply = True):
+        wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, 
+                            title = title, size = wx.DefaultSize,
+                            style = wx.CAPTION|wx.MINIMIZE_BOX|wx.CLOSE_BOX)
+        self.apply = apply
+        self.id = id
+        self.parent = parent
+        self.instruction = settings
+        self.objectType = None
+        self.unitConv = UnitConversion(self)
+        self.spinCtrlSize = (65, -1)
+        
+        self.Bind(wx.EVT_CLOSE, self.OnClose)
+        
+    
+        
+    def AddUnits(self, parent, dialogDict):
+        parent.units = dict()
+        parent.units['unitsLabel'] = wx.StaticText(parent, id = wx.ID_ANY, label = _("Units:"))
+        choices = self.unitConv.getPageUnitsNames()
+        parent.units['unitsCtrl'] = wx.Choice(parent, id = wx.ID_ANY, choices = choices)  
+        parent.units['unitsCtrl'].SetStringSelection(self.unitConv.findName(dialogDict['unit']))
+          
+    def AddPosition(self, parent, dialogDict):
+        if not hasattr(parent, "position"):
+            parent.position = dict()
+        parent.position['comment'] = wx.StaticText(parent, id = wx.ID_ANY,\
+                    label = _("Position of the top left corner\nfrom the top left edge of the paper"))
+        parent.position['xLabel'] = wx.StaticText(parent, id = wx.ID_ANY, label = _("X:"))
+        parent.position['yLabel'] = wx.StaticText(parent, id = wx.ID_ANY, label = _("Y:"))
+        parent.position['xCtrl'] = wx.TextCtrl(parent, id = wx.ID_ANY, value = str(dialogDict['where'][0]), validator = TCValidator(flag = 'DIGIT_ONLY'))
+        parent.position['yCtrl'] = wx.TextCtrl(parent, id = wx.ID_ANY, value = str(dialogDict['where'][1]), validator = TCValidator(flag = 'DIGIT_ONLY'))
+        if dialogDict.has_key('unit'):
+            x = self.unitConv.convert(value = dialogDict['where'][0], fromUnit = 'inch', toUnit = dialogDict['unit'])
+            y = self.unitConv.convert(value = dialogDict['where'][1], fromUnit = 'inch', toUnit = dialogDict['unit'])
+            parent.position['xCtrl'].SetValue("%5.3f" % x)
+            parent.position['yCtrl'].SetValue("%5.3f" % y)
+        
+    def AddExtendedPosition(self, panel, gridBagSizer, dialogDict):
+        """!Add widgets for setting position relative to paper and to map"""
+        panel.position = dict()
+        positionLabel = wx.StaticText(panel, id = wx.ID_ANY, label = _("Position is given:"))
+        panel.position['toPaper'] = wx.RadioButton(panel, id = wx.ID_ANY, label = _("relatively to paper"), style = wx.RB_GROUP)
+        panel.position['toMap'] = wx.RadioButton(panel, id = wx.ID_ANY, label = _("by map coordinates"))
+        panel.position['toPaper'].SetValue(dialogDict['XY'])
+        panel.position['toMap'].SetValue(not dialogDict['XY'])
+        
+        gridBagSizer.Add(positionLabel, pos = (0,0), span = (1,3), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT, border = 0)
+        gridBagSizer.Add(panel.position['toPaper'], pos = (1,0), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT, border = 0)
+        gridBagSizer.Add(panel.position['toMap'], pos = (1,1),flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT, border = 0)
+        
+        # first box - paper coordinates
+        box1   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = "")
+        sizerP = wx.StaticBoxSizer(box1, wx.VERTICAL)
+        self.gridBagSizerP = wx.GridBagSizer (hgap = 5, vgap = 5)
+        self.gridBagSizerP.AddGrowableCol(1)
+        self.gridBagSizerP.AddGrowableRow(3)
+        
+        self.AddPosition(parent = panel, dialogDict = dialogDict)
+        panel.position['comment'].SetLabel(_("Position from the top left\nedge of the paper"))
+        self.AddUnits(parent = panel, dialogDict = dialogDict)
+        self.gridBagSizerP.Add(panel.units['unitsLabel'], pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerP.Add(panel.units['unitsCtrl'], pos = (0,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerP.Add(panel.position['xLabel'], pos = (1,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerP.Add(panel.position['xCtrl'], pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerP.Add(panel.position['yLabel'], pos = (2,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerP.Add(panel.position['yCtrl'], pos = (2,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerP.Add(panel.position['comment'], pos = (3,0), span = (1,2), flag = wx.ALIGN_BOTTOM, border = 0)
+        
+        sizerP.Add(self.gridBagSizerP, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        gridBagSizer.Add(sizerP, pos = (2,0),span = (1,1), flag = wx.ALIGN_CENTER_HORIZONTAL|wx.EXPAND, border = 0)
+        
+        # second box - map coordinates
+        box2   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = "")
+        sizerM = wx.StaticBoxSizer(box2, wx.VERTICAL)
+        self.gridBagSizerM = wx.GridBagSizer (hgap = 5, vgap = 5)
+        self.gridBagSizerM.AddGrowableCol(0)
+        self.gridBagSizerM.AddGrowableCol(1)
+        
+        eastingLabel  = wx.StaticText(panel, id = wx.ID_ANY, label = "E:")
+        northingLabel  = wx.StaticText(panel, id = wx.ID_ANY, label = "N:")
+        panel.position['eCtrl'] = wx.TextCtrl(panel, id = wx.ID_ANY, value = "")
+        panel.position['nCtrl'] = wx.TextCtrl(panel, id = wx.ID_ANY, value = "")
+        east, north = PaperMapCoordinates(mapInstr = self.instruction[self.mapId], x = dialogDict['where'][0], y = dialogDict['where'][1], paperToMap = True)
+        panel.position['eCtrl'].SetValue(str(east))
+        panel.position['nCtrl'].SetValue(str(north))
+        
+        self.gridBagSizerM.Add(eastingLabel, pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerM.Add(northingLabel, pos = (1,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerM.Add(panel.position['eCtrl'], pos = (0,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerM.Add(panel.position['nCtrl'], pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        sizerM.Add(self.gridBagSizerM, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        gridBagSizer.Add(sizerM, pos = (2,1), flag = wx.ALIGN_LEFT|wx.EXPAND, border = 0)
+        
+    def AddFont(self, parent, dialogDict, color = True):
+        parent.font = dict()
+##        parent.font['fontLabel'] = wx.StaticText(parent, id = wx.ID_ANY, label = _("Choose font:"))
+##        parent.font['fontCtrl'] = wx.FontPickerCtrl(parent, id = wx.ID_ANY)
+##        
+##        parent.font['fontCtrl'].SetSelectedFont(
+##                        wx.FontFromNativeInfoString(dialogDict['font'] + " " + str(dialogDict['fontsize'])))
+##        parent.font['fontCtrl'].SetMaxPointSize(50)
+##        
+##        if color:
+##            parent.font['colorLabel'] = wx.StaticText(parent, id = wx.ID_ANY, label = _("Choose color:"))
+##            parent.font['colorCtrl'] = wx.ColourPickerCtrl(parent, id = wx.ID_ANY, style=wx.FNTP_FONTDESC_AS_LABEL)
+##            parent.font['colorCtrl'].SetColour(dialogDict['color'])
+           
+##        parent.font['colorCtrl'].SetColour(convertRGB(dialogDict['color'])) 
+           
+        parent.font['fontLabel'] = wx.StaticText(parent, id = wx.ID_ANY, label = _("Font:"))
+        parent.font['fontSizeLabel'] = wx.StaticText(parent, id = wx.ID_ANY, label = _("Font size:"))
+        fontChoices = [ 'Times-Roman', 'Times-Italic', 'Times-Bold', 'Times-BoldItalic', 'Helvetica',\
+                        'Helvetica-Oblique', 'Helvetica-Bold', 'Helvetica-BoldOblique', 'Courier',\
+                        'Courier-Oblique', 'Courier-Bold', 'Courier-BoldOblique'] 
+        parent.font['fontCtrl'] = wx.Choice(parent, id = wx.ID_ANY, choices = fontChoices)
+        if dialogDict['font'] in fontChoices:
+            parent.font['fontCtrl'].SetStringSelection(dialogDict['font'])
+        else:
+            parent.font['fontCtrl'].SetStringSelection('Helvetica')
+        parent.font['fontSizeCtrl'] = wx.SpinCtrl(parent, id = wx.ID_ANY, min = 4, max = 50, initial = 10)
+        parent.font['fontSizeCtrl'].SetValue(dialogDict['fontsize'])
+         
+        if color:
+            parent.font['colorLabel'] = wx.StaticText(parent, id = wx.ID_ANY, label = _("Choose color:"))
+            parent.font['colorCtrl'] = wx.ColourPickerCtrl(parent, id = wx.ID_ANY)
+            parent.font['colorCtrl'].SetColour(convertRGB(dialogDict['color']))
+##            parent.font['colorLabel'] = wx.StaticText(parent, id = wx.ID_ANY, label = _("Color:"))
+##            colorChoices = [  'aqua', 'black', 'blue', 'brown', 'cyan', 'gray', 'green', 'indigo', 'magenta',\
+##                                'orange', 'purple', 'red', 'violet', 'white', 'yellow']
+##            parent.colorCtrl = wx.Choice(parent, id = wx.ID_ANY, choices = colorChoices)
+##            parent.colorCtrl.SetStringSelection(parent.rLegendDict['color'])
+##            parent.font['colorCtrl'] = wx.ColourPickerCtrl(parent, id = wx.ID_ANY)
+##            parent.font['colorCtrl'].SetColour(dialogDict['color'])   
+    def OnApply(self, event):
+        ok = self.update()
+        if ok:
+            self.parent.DialogDataChanged(id = self.id)
+            return True 
+        else:
+            return False
+        
+    def OnOK(self, event):
+        """!Apply changes, close dialog"""
+        ok = self.OnApply(event)
+        if ok:
+            self.Close()
+    
+    def OnCancel(self, event):
+        """!Close dialog"""
+        self.Close()
+
+    def OnClose(self, event):
+        """!Destroy dialog and delete it from open dialogs"""
+        if self.objectType:
+            for each in  self.objectType:
+                if each in self.parent.openDialogs:
+                    del self.parent.openDialogs[each]
+        event.Skip()
+        self.Destroy()
+        
+    def _layout(self, panel):
+        #buttons
+        btnCancel = wx.Button(self, wx.ID_CANCEL)
+        btnOK = wx.Button(self, wx.ID_OK)
+        btnOK.SetDefault()
+        if self.apply:
+            btnApply = wx.Button(self, wx.ID_APPLY)
+        
+
+        # bindigs
+        btnOK.Bind(wx.EVT_BUTTON, self.OnOK)
+        btnOK.SetToolTipString(_("Close dialog and apply changes"))
+        #btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+        btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
+        btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+        if self.apply:
+            btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+            btnApply.SetToolTipString(_("Apply changes"))
+        
+        # sizers
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(btnCancel)
+        if self.apply:
+            btnSizer.AddButton(btnApply)
+        btnSizer.AddButton(btnOK)
+        btnSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = panel, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+        
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Layout()
+        mainSizer.Fit(self) 
+            
+class PageSetupDialog(PsmapDialog):
+    def __init__(self, parent, id, settings):
+        PsmapDialog.__init__(self, parent = parent, id = id, title = "Page setup",  settings = settings)
+        
+        self.cat = ['Units', 'Format', 'Orientation', 'Width', 'Height', 'Left', 'Right', 'Top', 'Bottom']
+        labels = [_('Units'), _('Format'), _('Orientation'), _('Width'), _('Height'),
+                  _('Left'), _('Right'), _('Top'), _('Bottom')]
+        self.catsLabels = dict(zip(self.cat, labels))
+        paperString = RunCommand('ps.map', flags = 'p', read = True, quiet = True)
+        self.paperTable = self._toList(paperString) 
+        self.unitsList = self.unitConv.getPageUnitsNames()
+        self.pageSetupDict = settings[id].GetInstruction()
+
+        self._layout()
+        
+        if self.pageSetupDict:
+            self.getCtrl('Units').SetStringSelection(self.unitConv.findName(self.pageSetupDict['Units']))
+            if self.pageSetupDict['Format'] == 'custom':
+                self.getCtrl('Format').SetSelection(self.getCtrl('Format').GetCount() - 1)
+            else:
+                self.getCtrl('Format').SetStringSelection(self.pageSetupDict['Format'])
+            if self.pageSetupDict['Orientation'] == 'Portrait':
+                self.getCtrl('Orientation').SetSelection(0)
+            else:
+                self.getCtrl('Orientation').SetSelection(1)
+                
+            for item in self.cat[3:]:
+                val = self.unitConv.convert(value = self.pageSetupDict[item],
+                                            fromUnit = 'inch', toUnit = self.pageSetupDict['Units'])
+                self.getCtrl(item).SetValue("%4.3f" % val)
+
+       
+        if self.getCtrl('Format').GetSelection() != self.getCtrl('Format').GetCount() - 1: # custom
+            self.getCtrl('Width').Disable()
+            self.getCtrl('Height').Disable()
+        else:
+            self.getCtrl('Orientation').Disable()
+        # events
+        self.getCtrl('Units').Bind(wx.EVT_CHOICE, self.OnChoice)
+        self.getCtrl('Format').Bind(wx.EVT_CHOICE, self.OnChoice)
+        self.getCtrl('Orientation').Bind(wx.EVT_CHOICE, self.OnChoice)
+        self.btnOk.Bind(wx.EVT_BUTTON, self.OnOK)
+
+    
+    def update(self):
+        self.pageSetupDict['Units'] = self.unitConv.findUnit(self.getCtrl('Units').GetStringSelection())
+        self.pageSetupDict['Format'] = self.paperTable[self.getCtrl('Format').GetSelection()]['Format']
+        if self.getCtrl('Orientation').GetSelection() == 0:
+            self.pageSetupDict['Orientation'] = 'Portrait'
+        else:
+            self.pageSetupDict['Orientation'] = 'Landscape'
+        for item in self.cat[3:]:
+            self.pageSetupDict[item] = self.unitConv.convert(value = float(self.getCtrl(item).GetValue()),
+                                        fromUnit = self.pageSetupDict['Units'], toUnit = 'inch')
+            
+
+            
+    def OnOK(self, event):
+        try:
+            self.update()
+        except ValueError:
+                wx.MessageBox(message = _("Literal is not allowed!"), caption = _('Invalid input'),
+                                    style = wx.OK|wx.ICON_ERROR)
+        else:
+            event.Skip()
+        
+    def _layout(self):
+        size = (110,-1)
+        #sizers
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        pageBox = wx.StaticBox(self, id = wx.ID_ANY, label = " %s " % _("Page size"))
+        pageSizer = wx.StaticBoxSizer(pageBox, wx.VERTICAL)
+        marginBox = wx.StaticBox(self, id = wx.ID_ANY, label = " %s " % _("Margins"))
+        marginSizer = wx.StaticBoxSizer(marginBox, wx.VERTICAL)
+        horSizer = wx.BoxSizer(wx.HORIZONTAL) 
+        #staticText + choice
+        choices = [self.unitsList, [item['Format'] for item in self.paperTable], [_('Portrait'), _('Landscape')]]
+        propor = [0,1,1]
+        border = [5,3,3]
+        self.hBoxDict={}
+        for i, item in enumerate(self.cat[:3]):
+            hBox = wx.BoxSizer(wx.HORIZONTAL)
+            stText = wx.StaticText(self, id = wx.ID_ANY, label = self.catsLabels[item] + ':')
+            choice = wx.Choice(self, id = wx.ID_ANY, choices = choices[i], size = size)
+            hBox.Add(stText, proportion = propor[i], flag = wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = border[i])
+            hBox.Add(choice, proportion = 0, flag = wx.ALL, border = border[i])
+            if item == 'Units':
+                hBox.Add(size,1) 
+            self.hBoxDict[item] = hBox    
+
+        #staticText + TextCtrl
+        for item in self.cat[3:]:
+            hBox = wx.BoxSizer(wx.HORIZONTAL)
+            label = wx.StaticText(self, id = wx.ID_ANY, label = self.catsLabels[item] + ':')
+            textctrl = wx.TextCtrl(self, id = wx.ID_ANY, size = size, value = '')
+            hBox.Add(label, proportion = 1, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 3)
+            hBox.Add(textctrl, proportion = 0, flag = wx.ALIGN_CENTRE|wx.ALL, border = 3)
+            self.hBoxDict[item] = hBox
+         
+        sizer = list([mainSizer] + [pageSizer]*4 + [marginSizer]*4)
+        for i, item in enumerate(self.cat):
+                sizer[i].Add(self.hBoxDict[item], 0, wx.GROW|wx.RIGHT|wx.LEFT,5)
+        # OK button
+        btnSizer = wx.StdDialogButtonSizer()
+        self.btnOk = wx.Button(self, wx.ID_OK)
+        self.btnOk.SetDefault()
+        btnSizer.AddButton(self.btnOk)
+        btn = wx.Button(self, wx.ID_CANCEL)
+        btnSizer.AddButton(btn)
+        btnSizer.Realize()
+    
+    
+        horSizer.Add(pageSizer, proportion = 0, flag = wx.LEFT|wx.RIGHT|wx.BOTTOM, border = 10)
+        horSizer.Add(marginSizer, proportion = 0, flag = wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.EXPAND, border = 10)
+        mainSizer.Add(horSizer, proportion = 0, border = 10)  
+        mainSizer.Add(btnSizer, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT|wx.ALL,  border = 10)      
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+    
+    def OnChoice(self, event):
+        currPaper = self.paperTable[self.getCtrl('Format').GetSelection()]
+        currUnit = self.unitConv.findUnit(self.getCtrl('Units').GetStringSelection())
+        currOrientIdx = self.getCtrl('Orientation').GetSelection()
+        newSize = dict()
+        for item in self.cat[3:]:
+            newSize[item] = self.unitConv.convert(float(currPaper[item]), fromUnit = 'inch', toUnit = currUnit)
+
+        enable = True
+        if currPaper['Format'] != _('custom'):
+            if currOrientIdx == 1: # portrait
+                newSize['Width'], newSize['Height'] = newSize['Height'], newSize['Width']
+            for item in self.cat[3:]:
+                self.getCtrl(item).ChangeValue("%4.3f" % newSize[item])
+            enable = False
+        self.getCtrl('Width').Enable(enable)
+        self.getCtrl('Height').Enable(enable)
+        self.getCtrl('Orientation').Enable(not enable)
+
+
+    def getCtrl(self, item):
+         return self.hBoxDict[item].GetItem(1).GetWindow()
+        
+    def _toList(self, paperStr):
+        
+        sizeList = list()
+        for line in paperStr.strip().split('\n'):
+            d = dict(zip([self.cat[1]]+ self.cat[3:],line.split()))
+            sizeList.append(d)
+        d = {}.fromkeys([self.cat[1]]+ self.cat[3:], 100)
+        d.update(Format = _('custom'))
+        sizeList.append(d)
+        return sizeList
+    
+class MapDialog(PsmapDialog):
+    """!Dialog for map frame settings and optionally  raster and vector map selection"""
+    def __init__(self, parent, id, settings,  rect = None, notebook = False):
+        PsmapDialog.__init__(self, parent = parent, id = id, title = "", settings = settings)
+ 
+        self.isNotebook = notebook
+        if self.isNotebook:
+            self.objectType = ('mapNotebook',) 
+        else:
+            self.objectType = ('map',)
+
+        
+        #notebook
+        if self.isNotebook:
+            self.notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
+            self.mPanel = MapFramePanel(parent = self.notebook, id = self.id[0], settings = self.instruction, 
+                                        rect = rect, notebook = True)
+            self.id[0] = self.mPanel.getId()
+            self.rPanel = RasterPanel(parent = self.notebook, id = self.id[1], settings = self.instruction, 
+                                        notebook = True)
+            self.id[1] = self.rPanel.getId()
+            self.vPanel = VectorPanel(parent = self.notebook, id = self.id[2], settings = self.instruction,
+                                        notebook = True)
+            self.id[2] = self.vPanel.getId()
+            self._layout(self.notebook)
+            self.SetTitle(_("Map settings"))
+        else:
+            self.mPanel = MapFramePanel(parent = self, id = self.id[0], settings = self.instruction, 
+                                        rect = rect, notebook = False)
+            self.id[0] = self.mPanel.getId()
+            self._layout(self.mPanel)
+            self.SetTitle(_("Map frame settings"))
+        
+        
+    def OnApply(self, event):
+        """!Apply changes"""
+        if self.isNotebook:
+            okV = self.vPanel.update()
+            okR = self.rPanel.update()
+            if okV and self.id[2] in self.instruction:
+                self.parent.DialogDataChanged(id = self.id[2])
+            if okR and self.id[1] in self.instruction:
+                self.parent.DialogDataChanged(id = self.id[1])
+            if not okR or not okV:
+                return False
+
+        ok = self.mPanel.update()
+        if ok:
+            self.parent.DialogDataChanged(id = self.id[0])
+            return True 
+        
+        return False
+    
+    def OnCancel(self, event):
+        """!Close dialog and remove tmp red box"""
+        self.parent.canvas.pdcTmp.RemoveId(self.parent.canvas.idZoomBoxTmp)
+        self.parent.canvas.Refresh() 
+        self.Close()
+        
+    def updateDialog(self):
+        """!Update raster and vector information"""
+        if self.mPanel.scaleChoice.GetSelection() == 0:
+            if self.mPanel.rasterTypeRadio.GetValue():
+                if 'raster' in self.parent.openDialogs:
+                    if self.parent.openDialogs['raster'].rPanel.rasterYesRadio.GetValue() and \
+                            self.parent.openDialogs['raster'].rPanel.rasterSelect.GetValue() == self.mPanel.select.GetValue():
+                            self.mPanel.drawMap.SetValue(True)
+                    else:
+                        self.mPanel.drawMap.SetValue(False)
+            else:
+                if 'vector' in self.parent.openDialogs:
+                    found = False
+                    for each in self.parent.openDialogs['vector'].vPanel.vectorList:
+                        if each[0] == self.mPanel.select.GetValue():
+                            found = True
+                    self.mPanel.drawMap.SetValue(found)    
+                        
+class MapFramePanel(wx.Panel):
+    """!wx.Panel with map (scale, region, border) settings"""
+    def __init__(self, parent, id, settings, rect, notebook = True):
+        wx.Panel.__init__(self, parent, id = wx.ID_ANY, style = wx.TAB_TRAVERSAL)
+
+        self.id = id
+        self.instruction = settings
+        
+        if notebook:
+            self.book = parent
+            self.book.AddPage(page = self, text = _("Map frame"))
+            self.mapDialog = self.book.GetParent()
+        else:
+            self.mapDialog = parent
+            
+        if self.id is not None:
+            self.mapFrameDict = self.instruction[self.id].GetInstruction() 
+        else:
+            self.id = wx.NewId()
+            mapFrame = MapFrame(self.id)
+            self.mapFrameDict = mapFrame.GetInstruction()
+            self.mapFrameDict['rect'] = rect
+
+            
+        self._layout()
+
+        self.scale = [None]*4
+        self.center = [None]*4
+        
+        
+        
+        self.selectedMap = self.mapFrameDict['map']
+        self.selectedRegion = self.mapFrameDict['region']
+        self.scaleType = self.mapFrameDict['scaleType']
+        self.mapType = self.mapFrameDict['mapType']
+        self.scaleChoice.SetSelection(self.mapFrameDict['scaleType'])
+        if self.instruction[self.id]:
+            self.drawMap.SetValue(self.mapFrameDict['drawMap'])
+        else:
+            self.drawMap.SetValue(True)
+        if self.mapFrameDict['scaleType'] == 0 and self.mapFrameDict['map']:
+            self.select.SetValue(self.mapFrameDict['map'])
+            if self.mapFrameDict['mapType'] == 'raster':
+                self.rasterTypeRadio.SetValue(True)
+                self.vectorTypeRadio.SetValue(False)
+            else:
+                self.rasterTypeRadio.SetValue(False)
+                self.vectorTypeRadio.SetValue(True)
+        elif self.mapFrameDict['scaleType'] == 1 and self.mapFrameDict['region']:
+            self.select.SetValue(self.mapFrameDict['region'])
+        
+        
+        self.OnMap(None)
+        self.scale[self.mapFrameDict['scaleType']] = self.mapFrameDict['scale']
+        self.center[self.mapFrameDict['scaleType']] = self.mapFrameDict['center']
+        self.OnScaleChoice(None)
+        self.OnElementType(None)
+        self.OnBorder(None)
+        
+        
+        
+    def _layout(self):
+        """!Do layout"""
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        box   = wx.StaticBox (parent = self, id = wx.ID_ANY, label = " %s " % _("Map frame"))
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        gridBagSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+
+
+        #scale options
+        frameText = wx.StaticText(self, id = wx.ID_ANY, label = _("Map frame options:"))
+        scaleChoices = [_("fit frame to match selected map"),
+                        _("fit frame to match saved region"),
+                        _("fit frame to match current computational region"),
+                        _("fixed scale and map center")]
+        self.scaleChoice = wx.Choice(self, id = wx.ID_ANY, choices = scaleChoices)
+        
+        
+        gridBagSizer.Add(frameText, pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.scaleChoice, pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        
+        #map and region selection
+        self.staticBox = wx.StaticBox (parent = self, id = wx.ID_ANY, label = " %s " % _("Map selection"))
+        sizerM = wx.StaticBoxSizer(self.staticBox, wx.HORIZONTAL)
+        self.mapSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+
+        self.rasterTypeRadio = wx.RadioButton(self, id = wx.ID_ANY, label = " %s " % _("raster"), style = wx.RB_GROUP)
+        self.vectorTypeRadio = wx.RadioButton(self, id = wx.ID_ANY, label = " %s " % _("vector"))
+        self.drawMap = wx.CheckBox(self, id = wx.ID_ANY, label = "add selected map")
+        
+        self.mapOrRegionText = [_("Map:"), _("Region:")] 
+        dc = wx.ClientDC(self)# determine size of labels
+        width = max(dc.GetTextExtent(self.mapOrRegionText[0])[0], dc.GetTextExtent(self.mapOrRegionText[1])[0])
+        self.mapText = wx.StaticText(self, id = wx.ID_ANY, label = self.mapOrRegionText[0], size = (width, -1))
+        self.select = Select(self, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
+                             type = 'raster', multiple = False,
+                             updateOnPopup = True, onPopup = None)
+                            
+        self.mapSizer.Add(self.rasterTypeRadio, pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.mapSizer.Add(self.vectorTypeRadio, pos = (0, 2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.mapSizer.Add(self.drawMap, pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, border = 0)
+        self.mapSizer.Add(self.mapText, pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.mapSizer.Add(self.select, pos = (1, 1), span = (1, 3), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+                 
+        sizerM.Add(self.mapSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        gridBagSizer.Add(sizerM, pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        
+
+        #map scale and center
+        boxC   = wx.StaticBox (parent = self, id = wx.ID_ANY, label = " %s " % _("Map scale and center"))
+        sizerC = wx.StaticBoxSizer(boxC, wx.HORIZONTAL)
+        self.centerSizer = wx.FlexGridSizer(rows = 2, cols = 5, hgap = 5, vgap = 5)        
+                
+                           
+        centerText = wx.StaticText(self, id = wx.ID_ANY, label = _("Center:"))
+        self.eastingText = wx.StaticText(self, id = wx.ID_ANY, label = _("E:"))
+        self.northingText = wx.StaticText(self, id = wx.ID_ANY, label = _("N:"))
+        self.eastingTextCtrl = wx.TextCtrl(self, id = wx.ID_ANY, style = wx.TE_RIGHT, validator = TCValidator(flag = 'DIGIT_ONLY'))
+        self.northingTextCtrl = wx.TextCtrl(self, id = wx.ID_ANY, style = wx.TE_RIGHT, validator = TCValidator(flag = 'DIGIT_ONLY'))
+        scaleText = wx.StaticText(self, id = wx.ID_ANY, label = _("Scale:"))
+        scalePrefixText = wx.StaticText(self, id = wx.ID_ANY, label = _("1 :"))
+        self.scaleTextCtrl = wx.TextCtrl(self, id = wx.ID_ANY, value = "", style = wx.TE_RIGHT, validator = TCValidator('DIGIT_ONLY'))
+        
+        self.centerSizer.Add(centerText, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border = 10)
+        self.centerSizer.Add(self.eastingText, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, border = 0)
+        self.centerSizer.Add(self.eastingTextCtrl, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.centerSizer.Add(self.northingText, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, border = 0)
+        self.centerSizer.Add(self.northingTextCtrl, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        self.centerSizer.Add(scaleText, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border = 10)
+        self.centerSizer.Add(scalePrefixText, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, border = 0)
+        self.centerSizer.Add(self.scaleTextCtrl, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        sizerC.Add(self.centerSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        gridBagSizer.Add(sizerC, pos = (3, 0), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        
+        
+        #resolution
+        flexSizer = wx.FlexGridSizer(rows = 1, cols = 2, hgap = 5, vgap = 5)
+        
+        resolutionText = wx.StaticText(self, id = wx.ID_ANY, label = _("Map max resolution (dpi):"))
+        self.resolutionSpin = wx.SpinCtrl(self, id = wx.ID_ANY, min = 1, max = 1000, initial = 300)
+        
+        flexSizer.Add(resolutionText, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        flexSizer.Add(self.resolutionSpin, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.resolutionSpin.SetValue(self.mapFrameDict['resolution'])
+        
+        gridBagSizer.Add(flexSizer, pos = (4, 0), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        # border
+        box   = wx.StaticBox (parent = self, id = wx.ID_ANY, label = " %s " % _("Border"))        
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        gridBagSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        
+        self.borderCheck = wx.CheckBox(self, id = wx.ID_ANY, label = (_("draw border around map frame")))
+        if self.mapFrameDict['border'] == 'y':
+            self.borderCheck.SetValue(True)
+        else: 
+            self.borderCheck.SetValue(False)
+        
+        self.borderColorText = wx.StaticText(self, id = wx.ID_ANY, label = _("border color:"))
+        self.borderWidthText = wx.StaticText(self, id = wx.ID_ANY, label = _("border width (pts):"))
+        self.borderColourPicker = wx.ColourPickerCtrl(self, id = wx.ID_ANY)
+        self.borderWidthCtrl = wx.SpinCtrl(self, id = wx.ID_ANY, min = 1, max = 100, initial = 1)
+        
+        if self.mapFrameDict['border'] == 'y':
+            self.borderWidthCtrl.SetValue(int(self.mapFrameDict['width']))
+            self.borderColourPicker.SetColour(convertRGB(self.mapFrameDict['color']))
+        
+        
+        gridBagSizer.Add(self.borderCheck, pos = (0, 0), span = (1,2), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        gridBagSizer.Add(self.borderColorText, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.borderWidthText, pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.borderColourPicker, pos = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        gridBagSizer.Add(self.borderWidthCtrl, pos = (2, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        self.SetSizer(border)
+        self.Fit()
+        
+        
+        if projInfo()['proj'] == 'll':
+            self.scaleChoice.SetItems(self.scaleChoice.GetItems()[0:3])
+            boxC.Hide()
+            for each in self.centerSizer.GetChildren():
+                each.GetWindow().Hide()
+
+            
+        # bindings
+        self.scaleChoice.Bind(wx.EVT_CHOICE, self.OnScaleChoice)
+        self.select.GetTextCtrl().Bind(wx.EVT_TEXT, self.OnMap)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnElementType, self.vectorTypeRadio)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnElementType, self.rasterTypeRadio)
+        self.Bind(wx.EVT_CHECKBOX, self.OnBorder, self.borderCheck)
+        
+        
+     
+    def OnMap(self, event):
+        """!Selected map or region changing"""
+        
+        if self.select.GetValue():
+            self.selected = self.select.GetValue() 
+        else:
+            self.selected = None
+
+        if self.scaleChoice.GetSelection() == 0:
+            self.selectedMap = self.selected
+            if self.rasterTypeRadio.GetValue():
+                mapType = 'raster' 
+            else:
+                mapType = 'vector'
+
+            self.scale[0], self.center[0], foo = AutoAdjust(self, scaleType = 0, map = self.selected,
+                                                mapType = mapType, rect = self.mapFrameDict['rect'])
+            #self.center[0] = self.RegionCenter(self.RegionDict(scaleType = 0))
+
+        elif self.scaleChoice.GetSelection() == 1:
+            self.selectedRegion = self.selected
+            self.scale[1], self.center[1],  foo = AutoAdjust(self, scaleType = 1, region = self.selected, rect = self.mapFrameDict['rect'])
+            #self.center[1] = self.RegionCenter(self.RegionDict(scaleType = 1))
+        elif self.scaleChoice.GetSelection() == 2:
+            self.scale[2], self.center[2], foo = AutoAdjust(self, scaleType = 2, rect = self.mapFrameDict['rect'])
+            #self.center[2] = self.RegionCenter(self.RegionDict(scaleType = 2))
+            
+        else:
+            self.scale[3] = None        
+            self.center[3] = None
+            
+        self.OnScaleChoice(None)
+        
+            
+    def OnScaleChoice(self, event):
+        """!Selected scale type changing"""
+        
+        scaleType = self.scaleChoice.GetSelection()
+        if self.scaleType != scaleType:
+            self.scaleType = scaleType
+            self.select.SetValue("")
+        
+        if scaleType in (0, 1): # automatic - region from raster map, saved region
+            if scaleType == 0:
+                # set map selection
+                self.rasterTypeRadio.Show()
+                self.vectorTypeRadio.Show()
+                self.drawMap.Show()
+                self.staticBox.SetLabel(" %s " % _("Map selection"))
+                if self.rasterTypeRadio.GetValue():
+                    stype = 'raster' 
+                else:
+                    stype = 'vector'
+
+                self.select.SetElementList(type = stype)
+                self.mapText.SetLabel(self.mapOrRegionText[0])
+                self.select.SetToolTipString(_("Region is set to match this map,\nraster or vector map must be added later"))
+                    
+            if scaleType == 1:
+                # set region selection
+                self.rasterTypeRadio.Hide()
+                self.vectorTypeRadio.Hide()
+                self.drawMap.Hide()
+                self.staticBox.SetLabel(" %s " % _("Region selection"))
+                stype = 'region'
+                self.select.SetElementList(type = stype)
+                self.mapText.SetLabel(self.mapOrRegionText[1])
+                self.select.SetToolTipString("")
+
+            for each in self.mapSizer.GetChildren():
+                each.GetWindow().Enable()
+            for each in self.centerSizer.GetChildren():
+                each.GetWindow().Disable()
+                    
+            if self.scale[scaleType]:
+                
+                self.scaleTextCtrl.SetValue("%.0f" % (1/self.scale[scaleType]))
+            if self.center[scaleType]:
+                self.eastingTextCtrl.SetValue(str(self.center[scaleType][0]))
+                self.northingTextCtrl.SetValue(str(self.center[scaleType][1]))
+        elif scaleType == 2:
+            for each in self.mapSizer.GetChildren():
+                each.GetWindow().Disable()
+            for each in self.centerSizer.GetChildren():
+                each.GetWindow().Disable()
+                
+            if self.scale[scaleType]:
+                self.scaleTextCtrl.SetValue("%.0f" % (1/self.scale[scaleType]))
+            if self.center[scaleType]:
+                self.eastingTextCtrl.SetValue(str(self.center[scaleType][0]))
+                self.northingTextCtrl.SetValue(str(self.center[scaleType][1]))
+        else: # fixed
+            for each in self.mapSizer.GetChildren():
+                each.GetWindow().Disable()
+            for each in self.centerSizer.GetChildren():
+                each.GetWindow().Enable()
+                    
+            if self.scale[scaleType]:
+                self.scaleTextCtrl.SetValue("%.0f" % (1/self.scale[scaleType]))
+            if self.center[scaleType]:
+                self.eastingTextCtrl.SetValue(str(self.center[scaleType][0]))
+                self.northingTextCtrl.SetValue(str(self.center[scaleType][1]))
+                
+    def OnElementType(self, event):
+        """!Changes data in map selection tree ctrl popup"""
+        if self.rasterTypeRadio.GetValue():
+            mapType = 'raster'
+        else:
+            mapType = 'vector'
+        self.select.SetElementList(type  = mapType)
+        if self.mapType != mapType and event is not None:
+            self.mapType = mapType
+            self.select.SetValue('')
+        self.mapType = mapType    
+        
+    def OnBorder(self, event):
+        """!Enables/disable the part relating to border of map frame"""
+        for each in (self.borderColorText, self.borderWidthText, self.borderColourPicker, self.borderWidthCtrl):
+            each.Enable(self.borderCheck.GetValue())
+            
+    def getId(self):
+        """!Returns id of raster map"""
+        return self.id
+            
+    def update(self):
+        """!Save changes"""
+        mapFrameDict = dict(self.mapFrameDict)
+        # resolution
+        mapFrameDict['resolution'] = self.resolutionSpin.GetValue()
+        #scale
+        scaleType = self.scaleType
+        mapFrameDict['scaleType'] = scaleType
+        
+        if mapFrameDict['scaleType'] == 0:
+            if self.select.GetValue():
+                mapFrameDict['drawMap'] = self.drawMap.GetValue()
+                mapFrameDict['map'] = self.select.GetValue()
+                mapFrameDict['mapType'] = self.mapType
+                mapFrameDict['region'] = None
+                
+                if mapFrameDict['drawMap']:
+
+                    if mapFrameDict['mapType'] == 'raster':
+                        mapFile = grass.find_file(mapFrameDict['map'], element = 'cell')
+                        if mapFile['file'] == '':
+                            GMessage("Raster %s not found" % mapFrameDict['map'])
+                            return False
+                        raster = self.instruction.FindInstructionByType('raster')
+                        if raster:
+                            raster['raster'] = mapFrameDict['map']
+                        else:
+                            raster = Raster(wx.NewId())
+                            raster['raster'] = mapFrameDict['map']
+                            raster['isRaster'] = True
+                            self.instruction.AddInstruction(raster)
+
+                    elif mapFrameDict['mapType'] == 'vector':
+                        
+                        mapFile = grass.find_file(mapFrameDict['map'], element = 'vector')
+                        if mapFile['file'] == '':
+                            GMessage("Vector %s not found" % mapFrameDict['map'])
+                            return False
+                        
+                        vector = self.instruction.FindInstructionByType('vector')
+                        isAdded = False
+                        if vector:
+                            for each in vector['list']:
+                                if each[0] == mapFrameDict['map']:
+                                    isAdded = True
+                        if not isAdded:
+                            topoInfo = grass.vector_info_topo(map = mapFrameDict['map'])
+                            if topoInfo:
+                                if bool(topoInfo['areas']):
+                                    topoType = 'areas'
+                                elif bool(topoInfo['lines']):
+                                    topoType = 'lines'
+                                else:
+                                    topoType = 'points'
+                                label = '('.join(mapFrameDict['map'].split('@')) + ')'
+                           
+                                if not vector:
+                                    vector = Vector(wx.NewId())
+                                    vector['list'] = []
+                                    self.instruction.AddInstruction(vector)
+                                id = wx.NewId()
+                                vector['list'].insert(0, [mapFrameDict['map'], topoType, id, 1, label])
+                                vProp = VProperties(id, topoType)
+                                vProp['name'], vProp['label'], vProp['lpos'] = mapFrameDict['map'], label, 1
+                                self.instruction.AddInstruction(vProp)
+                            else:
+                                return False
+                            
+                self.scale[0], self.center[0], self.rectAdjusted = AutoAdjust(self, scaleType = 0, map = mapFrameDict['map'],
+                                                                   mapType = self.mapType, rect = self.mapFrameDict['rect'])
+                                               
+                if self.rectAdjusted:
+                    mapFrameDict['rect'] = self.rectAdjusted 
+                else:
+                    mapFrameDict['rect'] = self.mapFrameDict['rect']
+
+                mapFrameDict['scale'] = self.scale[0]
+                
+                mapFrameDict['center'] = self.center[0]
+                # set region
+                if self.mapType == 'raster':
+                    RunCommand('g.region', rast = mapFrameDict['map'])
+                if self.mapType == 'vector':
+                    raster = self.instruction.FindInstructionByType('raster')
+                    if raster:
+                        rasterId = raster.id 
+                    else:
+                        rasterId = None
+
+                    if rasterId:
+                        
+                        RunCommand('g.region', vect = mapFrameDict['map'], rast = self.instruction[rasterId]['raster'])
+                    else:
+                        RunCommand('g.region', vect = mapFrameDict['map'])
+                
+                    
+                
+            else:
+                wx.MessageBox(message = _("No map selected!"),
+                                    caption = _('Invalid input'), style = wx.OK|wx.ICON_ERROR)
+                return False    
+            
+        elif mapFrameDict['scaleType'] == 1:
+            if self.select.GetValue():
+                mapFrameDict['drawMap'] = False
+                mapFrameDict['map'] = None
+                mapFrameDict['mapType'] = None
+                mapFrameDict['region'] = self.select.GetValue()
+                self.scale[1], self.center[1], self.rectAdjusted = AutoAdjust(self, scaleType = 1, region = mapFrameDict['region'],
+                                                                                rect = self.mapFrameDict['rect'])
+                if self.rectAdjusted:
+                    mapFrameDict['rect'] = self.rectAdjusted 
+                else:
+                    mapFrameDict['rect'] = self.mapFrameDict['rect']
+
+                mapFrameDict['scale'] = self.scale[1]
+                mapFrameDict['center'] = self.center[1]
+                # set region
+                RunCommand('g.region', region = mapFrameDict['region'])
+            else:
+                wx.MessageBox(message = _("No region selected!"),
+                                    caption = _('Invalid input'), style = wx.OK|wx.ICON_ERROR)
+                return False 
+                               
+        elif scaleType == 2:
+            mapFrameDict['drawMap'] = False
+            mapFrameDict['map'] = None
+            mapFrameDict['mapType'] = None
+            mapFrameDict['region'] = None
+            self.scale[2], self.center[2], self.rectAdjusted = AutoAdjust(self, scaleType = 2, rect = self.mapFrameDict['rect'])
+            if self.rectAdjusted:
+                mapFrameDict['rect'] = self.rectAdjusted 
+            else:
+                mapFrameDict['rect'] = self.mapFrameDict['rect']
+
+            mapFrameDict['scale'] = self.scale[2]
+            mapFrameDict['center'] = self.center[2]
+            
+            env = grass.gisenv()
+            windFilePath = os.path.join(env['GISDBASE'], env['LOCATION_NAME'], env['MAPSET'], 'WIND')
+            try:
+                windFile = open(windFilePath, 'r').read()
+                region = grass.parse_key_val(windFile, sep = ':', val_type = float)
+            except IOError:
+                region = grass.region()
+            
+            raster = self.instruction.FindInstructionByType('raster')
+            if raster:
+                rasterId = raster.id 
+            else:
+                rasterId = None
+
+            if rasterId: # because of resolution
+                RunCommand('g.region', n = region['north'], s = region['south'],
+                            e = region['east'], w = region['west'], rast = self.instruction[rasterId]['raster'])
+            else:
+                RunCommand('g.region', n = region['north'], s = region['south'],
+                           e = region['east'], w = region['west'])
+            
+        elif scaleType == 3:
+            mapFrameDict['drawMap'] = False
+            mapFrameDict['map'] = None
+            mapFrameDict['mapType'] = None
+            mapFrameDict['region'] = None
+            mapFrameDict['rect'] = self.mapFrameDict['rect']
+            try:
+                scaleNumber = float(self.scaleTextCtrl.GetValue())
+                centerE = float(self.eastingTextCtrl.GetValue()) 
+                centerN = float(self.northingTextCtrl.GetValue())
+            except (ValueError, SyntaxError):
+                wx.MessageBox(message = _("Invalid scale or map center!"),
+                                    caption = _('Invalid input'), style = wx.OK|wx.ICON_ERROR)
+                return False  
+            mapFrameDict['scale'] = 1/scaleNumber
+            mapFrameDict['center'] = centerE, centerN
+        
+            ComputeSetRegion(self, mapDict = mapFrameDict)
+        
+        # check resolution
+        SetResolution(dpi = mapFrameDict['resolution'], width = mapFrameDict['rect'].width,
+                                                        height = mapFrameDict['rect'].height)
+        # border
+        if self.borderCheck.GetValue():
+            mapFrameDict['border'] = 'y' 
+        else:
+            mapFrameDict['border'] = 'n'
+
+        if mapFrameDict['border'] == 'y':
+            mapFrameDict['width'] = self.borderWidthCtrl.GetValue()
+            mapFrameDict['color'] = convertRGB(self.borderColourPicker.GetColour())
+            
+        if self.id not in self.instruction:
+            mapFrame = MapFrame(self.id)
+            self.instruction.AddInstruction(mapFrame)
+        self.instruction[self.id].SetInstruction(mapFrameDict)
+
+        if self.id not in self.mapDialog.parent.objectId:
+            self.mapDialog.parent.objectId.insert(0, self.id)# map frame is drawn first
+        return True
+        
+class RasterPanel(wx.Panel):
+    """!Panel for raster map settings"""
+    def __init__(self, parent, id, settings,  notebook = True):
+        wx.Panel.__init__(self, parent, id = wx.ID_ANY, style = wx.TAB_TRAVERSAL)
+        self.instruction = settings
+        
+        if notebook:
+            self.book = parent
+            self.book.AddPage(page = self, text = _("Raster map"))
+            self.mainDialog = self.book.GetParent()
+        else:
+            self.mainDialog = parent
+        if id:
+            self.id = id
+            self.rasterDict = self.instruction[self.id].GetInstruction()
+        else:
+            self.id = wx.NewId()
+            raster = Raster(self.id)
+            self.rasterDict = raster.GetInstruction()
+            
+            
+        self._layout()
+        self.OnRaster(None)
+            
+    def _layout(self):
+        """!Do layout"""
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        # choose raster map
+        
+        box   = wx.StaticBox (parent = self, id = wx.ID_ANY, label = " %s " % _("Choose raster map"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridBagSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+        
+        self.rasterNoRadio = wx.RadioButton(self, id = wx.ID_ANY, label = _("no raster map"), style = wx.RB_GROUP)
+        self.rasterYesRadio = wx.RadioButton(self, id = wx.ID_ANY, label = _("raster:"))
+        
+        self.rasterSelect = Select(self, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
+                             type = 'raster', multiple = False,
+                             updateOnPopup = True, onPopup = None)
+        if self.rasterDict['isRaster']:
+            self.rasterYesRadio.SetValue(True)
+            self.rasterNoRadio.SetValue(False)
+            self.rasterSelect.SetValue(self.rasterDict['raster'])
+        else:
+            self.rasterYesRadio.SetValue(False)
+            self.rasterNoRadio.SetValue(True)
+            mapId = self.instruction.FindInstructionByType('map').id
+
+            if self.instruction[mapId]['map'] and self.instruction[mapId]['mapType'] == 'raster':
+                self.rasterSelect.SetValue(self.instruction[mapId]['map'])# raster map from map frame dialog if possible
+            else:
+                self.rasterSelect.SetValue('')                
+        gridBagSizer.Add(self.rasterNoRadio, pos = (0, 0), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)            
+        gridBagSizer.Add(self.rasterYesRadio, pos = (1, 0),  flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.rasterSelect, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        #self.rasterSelect.GetTextCtrl().Bind(wx.EVT_TEXT, self.OnRaster)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnRaster, self.rasterNoRadio)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnRaster, self.rasterYesRadio)
+        
+        self.SetSizer(border)
+        self.Fit()
+        
+    def OnRaster(self, event):
+        """!Enable/disable raster selection"""
+        self.rasterSelect.Enable(self.rasterYesRadio.GetValue())
+        
+    def update(self):
+        #draw raster
+        mapInstr = self.instruction.FindInstructionByType('map')
+        if not mapInstr: # no map frame
+            GMessage(message = _("Please, create map frame first."))
+            return
+            
+        if self.rasterNoRadio.GetValue() or not self.rasterSelect.GetValue():
+            self.rasterDict['isRaster'] = False
+            self.rasterDict['raster'] = None
+            mapInstr['drawMap'] = False
+            if self.id in self.instruction:
+                del self.instruction[self.id]
+
+        else:
+            self.rasterDict['isRaster'] = True
+            self.rasterDict['raster'] = self.rasterSelect.GetValue()
+            if self.rasterDict['raster'] != mapInstr['drawMap']:
+                mapInstr['drawMap'] = False
+
+            raster = self.instruction.FindInstructionByType('raster')
+            if not raster:
+                raster = Raster(self.id)
+                self.instruction.AddInstruction(raster)
+                self.instruction[self.id].SetInstruction(self.rasterDict)
+            else:
+                self.instruction[raster.id].SetInstruction(self.rasterDict)
+            
+        if 'map' in self.mainDialog.parent.openDialogs:
+            self.mainDialog.parent.openDialogs['map'].updateDialog()
+        return True
+        
+    def getId(self):
+        return self.id
+  
+class VectorPanel(wx.Panel):
+    """!Panel for vector maps settings"""
+    def __init__(self, parent, id, settings, notebook = True):
+        wx.Panel.__init__(self, parent, id = wx.ID_ANY, style = wx.TAB_TRAVERSAL)
+        
+        self.parent = parent
+        self.instruction = settings
+        self.tmpDialogDict = {}
+        vectors = self.instruction.FindInstructionByType('vProperties', list = True)
+        for vector in vectors:
+            self.tmpDialogDict[vector.id] = dict(self.instruction[vector.id].GetInstruction())
+        
+        if id:
+            self.id = id
+            self.vectorList = deepcopy(self.instruction[id]['list'])
+        else:
+            self.id = wx.NewId()
+            self.vectorList = []
+
+        vLegend = self.instruction.FindInstructionByType('vectorLegend')
+        if vLegend:
+            self.vLegendId = vLegend.id 
+        else:
+            self.vLegendId = None
+
+         
+        self._layout()
+        
+        if notebook:
+            self.parent.AddPage(page = self, text = _("Vector maps"))
+            self.parent = self.parent.GetParent()
+            
+    def _layout(self):
+        """!Do layout"""
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        # choose vector map
+        
+        box   = wx.StaticBox (parent = self, id = wx.ID_ANY, label = " %s " % _("Add map"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridBagSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+        
+        text = wx.StaticText(self, id = wx.ID_ANY, label = _("Map:"))
+        self.select = Select(self, id = wx.ID_ANY,# size = globalvar.DIALOG_GSELECT_SIZE,
+                             type = 'vector', multiple = False,
+                             updateOnPopup = True, onPopup = None)
+        topologyTypeTr = [_("points"), _("lines"), _("areas")]
+        self.topologyTypeList = ["points", "lines", "areas"]
+        self.vectorType = wx.RadioBox(self, id = wx.ID_ANY, label = " %s " % _("Data Type"), choices = topologyTypeTr,
+                                      majorDimension = 3, style = wx.RA_SPECIFY_COLS)
+            
+        self.AddVector = wx.Button(self, id = wx.ID_ANY, label = _("Add"))
+        
+        gridBagSizer.Add(text, pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.select, pos = (0,1), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.vectorType, pos = (1,1), flag = wx.ALIGN_CENTER, border = 0)
+        gridBagSizer.Add(self.AddVector, pos = (1,2), flag = wx.ALIGN_BOTTOM|wx.ALIGN_RIGHT, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        # manage vector layers
+        
+        box   = wx.StaticBox (parent = self, id = wx.ID_ANY, label = " %s " % _("Manage vector maps"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridBagSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+        gridBagSizer.AddGrowableCol(0,2)
+        gridBagSizer.AddGrowableCol(1,1)
+
+        
+        
+        text = wx.StaticText(self, id = wx.ID_ANY, label = _("The topmost vector map overlaps the others"))
+        self.listbox = wx.ListBox(self, id = wx.ID_ANY, choices = [], style = wx.LB_SINGLE|wx.LB_NEEDED_SB)
+        self.btnUp = wx.Button(self, id = wx.ID_ANY, label = _("Up"))
+        self.btnDown = wx.Button(self, id = wx.ID_ANY, label = _("Down"))
+        self.btnDel = wx.Button(self, id = wx.ID_ANY, label = _("Delete"))
+        self.btnProp = wx.Button(self, id = wx.ID_ANY, label = _("Properties..."))
+        
+        self.updateListBox(selected=0)
+        
+        
+        gridBagSizer.Add(text, pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.listbox, pos = (1,0), span = (4, 1), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        gridBagSizer.Add(self.btnUp, pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        gridBagSizer.Add(self.btnDown, pos = (2,1), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        gridBagSizer.Add(self.btnDel, pos = (3,1), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        gridBagSizer.Add(self.btnProp, pos = (4,1), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 0, flag = wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        self.Bind(wx.EVT_BUTTON, self.OnAddVector, self.AddVector)
+        self.Bind(wx.EVT_BUTTON, self.OnDelete, self.btnDel)
+        self.Bind(wx.EVT_BUTTON, self.OnUp, self.btnUp)
+        self.Bind(wx.EVT_BUTTON, self.OnDown, self.btnDown)
+        self.Bind(wx.EVT_BUTTON, self.OnProperties, self.btnProp)
+        self.select.GetTextCtrl().Bind(wx.EVT_TEXT, self.OnVector)
+        
+        self.SetSizer(border)
+        self.Fit()
+        
+        self.Bind(wx.EVT_LISTBOX_DCLICK, self.OnProperties, self.listbox)
+
+    def OnVector(self, event):
+        """!Gets info about toplogy and enables/disables choices point/line/area"""
+        vmap = self.select.GetValue()   
+        try:     
+            topoInfo = grass.vector_info_topo(map = vmap)
+        except grass.ScriptError:
+            return
+        
+        if topoInfo:
+            self.vectorType.EnableItem(2, bool(topoInfo['areas']))
+            self.vectorType.EnableItem(1, bool(topoInfo['boundaries']) or bool(topoInfo['lines']))
+            self.vectorType.EnableItem(0, bool(topoInfo['centroids'] or bool(topoInfo['points']) ))
+            for item in range(2,-1,-1):
+                if self.vectorType.IsItemEnabled(item):
+                    self.vectorType.SetSelection(item)
+                    break
+            
+            self.AddVector.SetFocus()        
+            
+    def OnAddVector(self, event):
+        """!Adds vector map to list"""
+        vmap = self.select.GetValue()
+        if vmap:
+            mapname = vmap.split('@')[0]
+            try:
+                mapset = '(' + vmap.split('@')[1] + ')'
+            except IndexError:
+                mapset = ''
+            idx = self.vectorType.GetSelection()
+            ttype = self.topologyTypeList[idx]
+            record = "%s - %s" % (vmap, ttype)
+            id = wx.NewId()
+            lpos = 1
+            label = mapname + mapset 
+            self.vectorList.insert(0, [vmap, ttype, id, lpos, label])
+            self.reposition()
+            self.listbox.InsertItems([record], 0)
+            
+            vector = VProperties(id, ttype)
+            self.tmpDialogDict[id] = vector.GetInstruction()
+            self.tmpDialogDict[id]['name'] = vmap
+
+            
+            self.listbox.SetSelection(0)  
+            self.listbox.EnsureVisible(0)
+            self.btnProp.SetFocus()
+            self.enableButtons()
+            
+    def OnDelete(self, event):
+        """!Deletes vector map from the list"""
+        if self.listbox.GetSelections():
+            pos = self.listbox.GetSelection()
+            id = self.vectorList[pos][2]
+            del self.vectorList[pos]
+            del self.tmpDialogDict[id]
+            
+            for i in range(pos, len(self.vectorList)):
+                if self.vectorList[i][3]:# can be 0
+                    self.vectorList[i][3] -= 1
+            
+            if pos < len(self.vectorList) -1:
+                selected = pos
+            else:
+                selected = len(self.vectorList) -1
+            self.updateListBox(selected = selected)
+            if self.listbox.IsEmpty():
+                self.enableButtons(False)
+            
+            
+    def OnUp(self, event):
+        """!Moves selected map to top"""
+        if self.listbox.GetSelections():
+            pos = self.listbox.GetSelection()
+            if pos:
+                self.vectorList.insert(pos - 1, self.vectorList.pop(pos))
+            if not self.vLegendId:
+                self.reposition()
+                
+            if pos > 0:
+                self.updateListBox(selected = (pos - 1)) 
+            else:
+                self.updateListBox(selected = 0)
+
+            
+    def OnDown(self, event):
+        """!Moves selected map to bottom"""
+        if self.listbox.GetSelections():
+            pos = self.listbox.GetSelection()
+            if pos != len(self.vectorList) - 1:
+                self.vectorList.insert(pos + 1, self.vectorList.pop(pos))
+                if not self.vLegendId:
+                    self.reposition()
+            if pos < len(self.vectorList) -1:
+                self.updateListBox(selected = (pos + 1)) 
+            else:
+                self.updateListBox(selected = len(self.vectorList) -1)
+
+    
+    def OnProperties(self, event):
+        """!Opens vector map properties dialog"""
+        if self.listbox.GetSelections():
+            pos = self.listbox.GetSelection()
+            id = self.vectorList[pos][2]
+
+            dlg = VPropertiesDialog(self, id = id, settings = self.instruction, 
+                                    vectors = self.vectorList, tmpSettings = self.tmpDialogDict[id])
+            dlg.ShowModal()
+            
+            self.parent.FindWindowById(wx.ID_OK).SetFocus()
+           
+    def enableButtons(self, enable = True):
+        """!Enable/disable up, down, properties, delete buttons"""
+        self.btnUp.Enable(enable)
+        self.btnDown.Enable(enable)
+        self.btnProp.Enable(enable)
+        self.btnDel.Enable(enable)
+    
+    def updateListBox(self, selected = None):
+        mapList = ["%s - %s" % (item[0], item[1]) for item in self.vectorList]
+        self.listbox.Set(mapList)
+        if self.listbox.IsEmpty():
+            self.enableButtons(False)
+        else:
+            self.enableButtons(True)
+            if selected is not None:
+                self.listbox.SetSelection(selected)  
+                self.listbox.EnsureVisible(selected)  
+              
+    def reposition(self):
+        """!Update position in legend, used only if there is no vlegend yet"""
+        for i in range(len(self.vectorList)):
+            if self.vectorList[i][3]:
+                self.vectorList[i][3] = i + 1
+                
+    def getId(self):
+        return self.id
+        
+    def update(self):
+        vectors = self.instruction.FindInstructionByType('vProperties', list = True)
+        
+        for vector in vectors:
+            del self.instruction[vector.id]
+        if self.id in self.instruction:
+            del self.instruction[self.id] 
+
+        if len(self.vectorList) > 0:
+            vector = Vector(self.id)
+            self.instruction.AddInstruction(vector)
+
+            vector.SetInstruction({'list': deepcopy(self.vectorList)})
+            
+            # save new vectors
+            for item in self.vectorList:
+                id = item[2]
+
+                vLayer = VProperties(id, item[1])
+                self.instruction.AddInstruction(vLayer)
+                vLayer.SetInstruction(self.tmpDialogDict[id])
+                vLayer['name'] = item[0]
+                vLayer['label'] = item[4]
+                vLayer['lpos'] = item[3]
+            
+        else:
+            if self.id in self.instruction:
+                del self.instruction[self.id]
+                
+        if 'map' in self.parent.parent.openDialogs:
+            self.parent.parent.openDialogs['map'].updateDialog()
+
+        return True
+    
+class RasterDialog(PsmapDialog):
+    def __init__(self, parent, id, settings):
+        PsmapDialog.__init__(self, parent = parent, id = id, title = _("Raster map settings"), settings = settings)
+        self.objectType = ('raster',)
+        
+        self.rPanel = RasterPanel(parent = self, id = self.id, settings = self.instruction, notebook = False)
+
+        self.id = self.rPanel.getId()
+        self._layout(self.rPanel)
+    
+    def update(self):
+        ok = self.rPanel.update()
+        if ok:
+            return True
+        return False
+    
+    def OnApply(self, event):
+        ok = self.update()
+        if not ok:
+            return False
+
+        if self.id in self.instruction:
+            self.parent.DialogDataChanged(id = self.id)
+        else:
+            mapId = self.instruction.FindInstructionByType('map').id
+            self.parent.DialogDataChanged(id = mapId)
+        return True
+    
+    def updateDialog(self):
+        """!Update information (not used)"""
+        pass
+##        if 'map' in self.parent.openDialogs:
+##            if self.parent.openDialogs['map'].mPanel.rasterTypeRadio.GetValue()\
+##                    and self.parent.openDialogs['map'].mPanel.select.GetValue():
+##                if self.parent.openDialogs['map'].mPanel.drawMap.IsChecked():
+##                    self.rPanel.rasterSelect.SetValue(self.parent.openDialogs['map'].mPanel.select.GetValue())   
+                
+class MainVectorDialog(PsmapDialog):
+    def __init__(self, parent, id, settings):
+        PsmapDialog.__init__(self, parent = parent, id = id, title = _("Vector maps settings"), settings = settings)
+        self.objectType = ('vector',)
+        self.vPanel = VectorPanel(parent = self, id = self.id, settings = self.instruction, notebook = False)
+
+        self.id = self.vPanel.getId()
+        self._layout(self.vPanel)
+    
+    def update(self):
+        self.vPanel.update()
+        
+    def OnApply(self, event):
+        self.update()
+        if self.id in self.instruction:
+            self.parent.DialogDataChanged(id = self.id)
+        else:
+            mapId = self.instruction.FindInstructionByType('map').id
+            self.parent.DialogDataChanged(id = mapId)
+        return True
+        
+    def updateDialog(self):
+        """!Update information (not used)"""
+        pass
+        
+class VPropertiesDialog(PsmapDialog):
+    def __init__(self, parent, id, settings, vectors, tmpSettings):
+        PsmapDialog.__init__(self, parent = parent, id = id, title = "", settings = settings, apply = False)
+        
+        vectorList = vectors
+        self.vPropertiesDict = tmpSettings
+        
+        # determine map and its type
+        for item in vectorList:
+            if id == item[2]:
+                self.vectorName = item[0]
+                self.type = item[1]
+        self.SetTitle(_("%s properties") % self.vectorName)
+        
+        #vector map info
+        self.connection = True
+        try:
+            self.mapDBInfo = VectorDBInfo(self.vectorName)
+            self.layers = self.mapDBInfo.layers.keys()
+        except grass.ScriptError:
+            self.connection = False
+            self.layers = []
+        if not self.layers:
+            self.connection = False
+            self.layers = []
+            
+        self.currLayer = self.vPropertiesDict['layer']
+        
+        #path to symbols, patterns
+        gisbase = os.getenv("GISBASE")
+        self.symbolPath = os.path.join(gisbase, 'etc', 'symbol')
+        self.symbols = []
+        for dir in os.listdir(self.symbolPath):
+            for symbol in os.listdir(os.path.join(self.symbolPath, dir)):
+                self.symbols.append(os.path.join(dir, symbol))
+        self.patternPath = os.path.join(gisbase, 'etc', 'paint', 'patterns')
+
+        #notebook
+        notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
+        self.DSpanel = self._DataSelectionPanel(notebook)
+        self.EnableLayerSelection(enable = self.connection)
+        selectPanel = { 'points': [self._ColorsPointAreaPanel, self._StylePointPanel], 
+                        'lines': [self._ColorsLinePanel, self._StyleLinePanel], 
+                        'areas': [self._ColorsPointAreaPanel, self._StyleAreaPanel]}
+        self.ColorsPanel = selectPanel[self.type][0](notebook)
+        
+        self.OnOutline(None)
+        if self.type in ('points', 'areas'):
+            self.OnFill(None)
+        self.OnColor(None)
+        
+        self.StylePanel = selectPanel[self.type][1](notebook)
+        if self.type == 'points':
+            self.OnSize(None)
+            self.OnRotation(None)
+            self.OnSymbology(None)
+        if self.type == 'areas':
+            self.OnPattern(None)
+        
+        self._layout(notebook)
+        
+    def _DataSelectionPanel(self, notebook):
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY, size = (-1, -1), style = wx.TAB_TRAVERSAL)
+        notebook.AddPage(page = panel, text = _("Data selection"))
+        
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        # data type
+        self.checkType1 = self.checkType2 = None
+        if self.type in ('lines', 'points'):
+            box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Feature type"))
+            sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+            gridBagSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+            if self.type == 'points':
+                label = (_("points"), _("centroids"))
+            else: 
+                label = (_("lines"), _("boundaries"))
+            if self.type == 'points':
+                name = ("point", "centroid")
+            else:
+                name = ("line", "boundary")
+            self.checkType1 = wx.CheckBox(panel, id = wx.ID_ANY, label = label[0], name = name[0])
+            self.checkType2 = wx.CheckBox(panel, id = wx.ID_ANY, label = label[1], name = name[1])
+            self.checkType1.SetValue(self.vPropertiesDict['type'].find(name[0]) >= 0)
+            self.checkType2.SetValue(self.vPropertiesDict['type'].find(name[1]) >= 0)
+            
+            gridBagSizer.Add(self.checkType1, pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+            gridBagSizer.Add(self.checkType2, pos = (0,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+            sizer.Add(gridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+            border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        # layer selection
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Layer selection"))
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        self.gridBagSizerL = wx.GridBagSizer(hgap = 5, vgap = 5)
+        
+        self.warning =  wx.StaticText(panel, id = wx.ID_ANY, label = "")
+        if not self.connection:
+            self.warning = wx.StaticText(panel, id = wx.ID_ANY, label = _("Database connection is not defined in DB file."))
+        text = wx.StaticText(panel, id = wx.ID_ANY, label = _("Select layer:"))
+        self.layerChoice = wx.Choice(panel, id = wx.ID_ANY, choices = map(str, self.layers), size = self.spinCtrlSize)
+        
+        self.layerChoice.SetStringSelection(self.currLayer)
+                
+        if self.connection:
+            table = self.mapDBInfo.layers[int(self.currLayer)]['table'] 
+        else:
+            table = ""
+
+        self.radioWhere = wx.RadioButton(panel, id = wx.ID_ANY, label = "SELECT * FROM %s WHERE" % table, style = wx.RB_GROUP)
+        self.textCtrlWhere = wx.TextCtrl(panel, id = wx.ID_ANY, value = "")
+        
+        
+        if self.connection:
+            cols = self.mapDBInfo.GetColumns(self.mapDBInfo.layers[int(self.currLayer)]['table']) 
+        else:
+            cols = []
+
+        self.choiceColumns = wx.Choice(panel, id = wx.ID_ANY, choices = cols)
+        
+        self.radioCats = wx.RadioButton(panel, id = wx.ID_ANY, label = "Choose categories ")
+        self.textCtrlCats = wx.TextCtrl(panel, id = wx.ID_ANY, value = "")
+        self.textCtrlCats.SetToolTipString(_("list of categories (e.g. 1,3,5-7)"))
+        
+        if self.vPropertiesDict.has_key('cats'):
+            self.radioCats.SetValue(True)
+            self.textCtrlCats.SetValue(self.vPropertiesDict['cats'])
+        if self.vPropertiesDict.has_key('where'):
+            self.radioWhere.SetValue(True)
+            where = self.vPropertiesDict['where'].strip().split(" ",1)
+            self.choiceColumns.SetStringSelection(where[0])
+            self.textCtrlWhere.SetValue(where[1])
+            
+        row = 0
+        if not self.connection:
+            self.gridBagSizerL.Add(self.warning, pos = (0,0), span = (1,3), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+            row = 1
+        self.gridBagSizerL.Add(text, pos = (0 + row,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerL.Add(self.layerChoice, pos = (0 + row,1), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        self.gridBagSizerL.Add(self.radioWhere, pos = (1 + row,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerL.Add(self.choiceColumns, pos = (1 + row,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        self.gridBagSizerL.Add(self.textCtrlWhere, pos = (1 + row,2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerL.Add(self.radioCats, pos = (2 + row,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerL.Add(self.textCtrlCats, pos = (2 + row,1), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        
+        sizer.Add(self.gridBagSizerL, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        #mask
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Mask"))
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        
+        self.mask = wx.CheckBox(panel, id = wx.ID_ANY, label = _("Use current mask"))
+        if self.vPropertiesDict['masked'] == 'y':
+            self.mask.SetValue(True) 
+        else:
+            self.mask.SetValue(False)
+        
+        sizer.Add(self.mask, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+
+        self.Bind(wx.EVT_CHOICE, self.OnLayer, self.layerChoice)
+        
+        panel.SetSizer(border)
+        panel.Fit()
+        return panel
+    
+    def _ColorsPointAreaPanel(self, notebook):
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY, size = (-1, -1), style = wx.TAB_TRAVERSAL)
+        notebook.AddPage(page = panel, text = _("Colors"))
+        
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        #colors - outline
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Outline"))
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        self.gridBagSizerO = wx.GridBagSizer(hgap = 5, vgap = 2)
+        
+        self.outlineCheck = wx.CheckBox(panel, id = wx.ID_ANY, label = _("draw outline"))
+        self.outlineCheck.SetValue(self.vPropertiesDict['color'] != 'none')
+        
+        widthText = wx.StaticText(panel, id = wx.ID_ANY, label = _("Width (pts):"))
+        if fs:
+            self.widthSpin = fs.FloatSpin(panel, id = wx.ID_ANY, min_val = 0, max_val = 30,
+                                          increment = 0.5, value = 1, style = fs.FS_RIGHT)
+            self.widthSpin.SetFormat("%f")
+            self.widthSpin.SetDigits(2)
+        else:
+            self.widthSpin = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 25, initial = 1,
+                                         size = self.spinCtrlSize)
+                                        
+        if self.vPropertiesDict['color'] == None:
+            self.vPropertiesDict['color'] = 'none'
+ 
+        if self.vPropertiesDict['color'] != 'none':
+            self.widthSpin.SetValue(self.vPropertiesDict['width'] )
+        else:
+            self.widthSpin.SetValue(1)
+
+        colorText = wx.StaticText(panel, id = wx.ID_ANY, label = _("Color:"))
+        self.colorPicker = wx.ColourPickerCtrl(panel, id = wx.ID_ANY)
+        if self.vPropertiesDict['color'] != 'none':
+            self.colorPicker.SetColour(convertRGB(self.vPropertiesDict['color'])) 
+        else:
+            self.colorPicker.SetColour(convertRGB('black'))
+
+        self.gridBagSizerO.Add(self.outlineCheck, pos = (0, 0), span = (1,2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        self.gridBagSizerO.Add(widthText, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        self.gridBagSizerO.Add(self.widthSpin, pos = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)        
+        self.gridBagSizerO.Add(colorText, pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)                
+        self.gridBagSizerO.Add(self.colorPicker, pos = (2, 2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        
+        sizer.Add(self.gridBagSizerO, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        self.Bind(wx.EVT_CHECKBOX, self.OnOutline, self.outlineCheck)
+        
+        #colors - fill
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Fill")) 
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        self.gridBagSizerF = wx.GridBagSizer(hgap = 5, vgap = 2)
+       
+        self.fillCheck = wx.CheckBox(panel, id = wx.ID_ANY, label = _("fill color"))
+        self.fillCheck.SetValue(self.vPropertiesDict['fcolor'] != 'none' or self.vPropertiesDict['rgbcolumn'] is not None)
+
+        self.colorPickerRadio = wx.RadioButton(panel, id = wx.ID_ANY, label = _("choose color:"), style = wx.RB_GROUP)
+        #set choose color option if there is no db connection
+        if self.connection:
+            self.colorPickerRadio.SetValue(not self.vPropertiesDict['rgbcolumn'])
+        else:
+            self.colorPickerRadio.SetValue(False)            
+        self.fillColorPicker = wx.ColourPickerCtrl(panel, id = wx.ID_ANY)
+        if self.vPropertiesDict['fcolor'] != 'none':
+            self.fillColorPicker.SetColour(convertRGB(self.vPropertiesDict['fcolor'])) 
+        else:
+            self.fillColorPicker.SetColour(convertRGB('red'))        
+        
+        self.colorColRadio = wx.RadioButton(panel, id = wx.ID_ANY, label = _("color from map table column:"))
+        self.colorColChoice = self.getColsChoice(parent = panel)
+        if self.connection:
+            if self.vPropertiesDict['rgbcolumn']:
+                self.colorColRadio.SetValue(True)
+                self.colorColChoice.SetStringSelection(self.vPropertiesDict['rgbcolumn'])
+            else:
+                self.colorColRadio.SetValue(False)
+                self.colorColChoice.SetSelection(0)
+        self.colorColChoice.Enable(self.connection)
+        self.colorColRadio.Enable(self.connection)
+        
+        self.gridBagSizerF.Add(self.fillCheck, pos = (0, 0), span = (1,2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerF.Add(self.colorPickerRadio, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        self.gridBagSizerF.Add(self.fillColorPicker, pos = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        self.gridBagSizerF.Add(self.colorColRadio, pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        self.gridBagSizerF.Add(self.colorColChoice, pos = (2, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)        
+        
+        sizer.Add(self.gridBagSizerF, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+
+        self.Bind(wx.EVT_CHECKBOX, self.OnFill, self.fillCheck)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnColor, self.colorColRadio)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnColor, self.colorPickerRadio)
+        
+        panel.SetSizer(border)
+        panel.Fit()
+        return panel
+    
+    def _ColorsLinePanel(self, notebook):
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY, size = (-1, -1), style = wx.TAB_TRAVERSAL)
+        notebook.AddPage(page = panel, text = _("Colors"))
+        
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        #colors - outline
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Outline"))
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        self.gridBagSizerO = wx.GridBagSizer(hgap = 5, vgap = 2)
+        
+        if self.vPropertiesDict['hcolor'] == None:
+            self.vPropertiesDict['hcolor'] = 'none'
+        if self.vPropertiesDict['color'] == None:
+            self.vPropertiesDict['color'] = 'none'
+        
+        self.outlineCheck = wx.CheckBox(panel, id = wx.ID_ANY, label = _("draw outline"))
+        self.outlineCheck.SetValue(self.vPropertiesDict['hcolor'] != 'none')
+        self.outlineCheck.SetToolTipString(_("No effect for fill color from table column"))
+        
+        widthText = wx.StaticText(panel, id = wx.ID_ANY, label = _("Width (pts):"))
+        
+        if fs:
+            self.outWidthSpin = fs.FloatSpin(panel, id = wx.ID_ANY, min_val = 0, max_val = 30,
+                                             increment = 0.5, value = 1, style = fs.FS_RIGHT)
+            self.outWidthSpin.SetFormat("%f")
+            self.outWidthSpin.SetDigits(1)
+        else:
+            self.outWidthSpin = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 30, initial = 1,
+                                         size = self.spinCtrlSize)
+        
+        if self.vPropertiesDict['hcolor'] != 'none':
+            self.outWidthSpin.SetValue(self.vPropertiesDict['hwidth'] )
+        else:
+            self.outWidthSpin.SetValue(1)
+
+        colorText = wx.StaticText(panel, id = wx.ID_ANY, label = _("Color:"))
+        self.colorPicker = wx.ColourPickerCtrl(panel, id = wx.ID_ANY)
+        if self.vPropertiesDict['hcolor'] != 'none':
+            self.colorPicker.SetColour(convertRGB(self.vPropertiesDict['hcolor']) )
+        else:
+            self.colorPicker.SetColour(convertRGB('black'))
+
+        
+        self.gridBagSizerO.Add(self.outlineCheck, pos = (0, 0), span = (1,2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        self.gridBagSizerO.Add(widthText, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        self.gridBagSizerO.Add(self.outWidthSpin, pos = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)        
+        self.gridBagSizerO.Add(colorText, pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)                
+        self.gridBagSizerO.Add(self.colorPicker, pos = (2, 2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        
+        sizer.Add(self.gridBagSizerO, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        self.Bind(wx.EVT_CHECKBOX, self.OnOutline, self.outlineCheck)
+        
+        #colors - fill
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Fill"))
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        self.gridBagSizerF = wx.GridBagSizer(hgap = 5, vgap = 2)
+       
+        fillText = wx.StaticText(panel, id = wx.ID_ANY, label = _("Color of lines:"))
+
+        self.colorPickerRadio = wx.RadioButton(panel, id = wx.ID_ANY, label = _("choose color:"), style = wx.RB_GROUP)
+
+        #set choose color option if there is no db connection
+        if self.connection:
+            self.colorPickerRadio.SetValue(not self.vPropertiesDict['rgbcolumn'])
+        else:
+            self.colorPickerRadio.SetValue(False)            
+        self.fillColorPicker = wx.ColourPickerCtrl(panel, id = wx.ID_ANY)
+        if self.vPropertiesDict['color'] != 'none':
+            self.fillColorPicker.SetColour(convertRGB(self.vPropertiesDict['color']) )
+        else:
+            self.fillColorPicker.SetColour(convertRGB('black'))
+        
+        self.colorColRadio = wx.RadioButton(panel, id = wx.ID_ANY, label = _("color from map table column:"))
+        self.colorColChoice = self.getColsChoice(parent = panel)
+        if self.connection:
+            if self.vPropertiesDict['rgbcolumn']:
+                self.colorColRadio.SetValue(True)
+                self.colorColChoice.SetStringSelection(self.vPropertiesDict['rgbcolumn'])
+            else:
+                self.colorColRadio.SetValue(False)
+                self.colorColChoice.SetSelection(0)
+        self.colorColChoice.Enable(self.connection)
+        self.colorColRadio.Enable(self.connection)
+        
+        self.gridBagSizerF.Add(fillText, pos = (0, 0), span = (1,2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.gridBagSizerF.Add(self.colorPickerRadio, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        self.gridBagSizerF.Add(self.fillColorPicker, pos = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        self.gridBagSizerF.Add(self.colorColRadio, pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        self.gridBagSizerF.Add(self.colorColChoice, pos = (2, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)        
+        
+        sizer.Add(self.gridBagSizerF, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnColor, self.colorColRadio)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnColor, self.colorPickerRadio)
+
+        panel.SetSizer(border)
+        panel.Fit()
+        return panel
+    
+    def _StylePointPanel(self, notebook):
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY, size = (-1, -1), style = wx.TAB_TRAVERSAL)
+        notebook.AddPage(page = panel, text = _("Size and style"))
+        
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        #symbology
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Symbology"))        
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        gridBagSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        gridBagSizer.AddGrowableCol(1)
+    
+        self.symbolRadio = wx.RadioButton(panel, id = wx.ID_ANY, label = _("symbol:"), style = wx.RB_GROUP)
+        self.symbolRadio.SetValue(bool(self.vPropertiesDict['symbol']))
+            
+        self.symbolName = wx.StaticText(panel, id = wx.ID_ANY)
+        self.symbolName.SetLabel(self.vPropertiesDict['symbol'])
+        bitmap = wx.Bitmap(os.path.join(globalvar.ETCSYMBOLDIR,
+                                        self.vPropertiesDict['symbol']) + '.png')
+        self.symbolButton = wx.BitmapButton(panel, id = wx.ID_ANY, bitmap = bitmap)
+            
+        self.epsRadio = wx.RadioButton(panel, id = wx.ID_ANY, label = _("eps file:"))
+        self.epsRadio.SetValue(bool(self.vPropertiesDict['eps']))
+        
+        self.epsFileCtrl = filebrowse.FileBrowseButton(panel, id = wx.ID_ANY, labelText = '',
+                                buttonText =  _("Browse"), toolTip = _("Type filename or click browse to choose file"), 
+                                dialogTitle = _("Choose a file"), startDirectory = '', initialValue = '',
+                                fileMask = "Encapsulated PostScript (*.eps)|*.eps|All files (*.*)|*.*", fileMode = wx.OPEN)
+        if not self.vPropertiesDict['eps']:
+            self.epsFileCtrl.SetValue('')
+        else: #eps chosen
+            self.epsFileCtrl.SetValue(self.vPropertiesDict['eps'])
+            
+        gridBagSizer.AddGrowableCol(2)
+        gridBagSizer.Add(self.symbolRadio, pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.symbolName, pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border = 10)
+        gridBagSizer.Add(self.symbolButton, pos = (0, 2), flag = wx.ALIGN_RIGHT , border = 0)
+        gridBagSizer.Add(self.epsRadio, pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.epsFileCtrl, pos = (1, 1), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        self.Bind(wx.EVT_BUTTON, self.OnSymbolSelection, self.symbolButton)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnSymbology, self.symbolRadio)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnSymbology, self.epsRadio)
+        
+        #size
+        
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Size"))
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        gridBagSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        gridBagSizer.AddGrowableCol(0)
+        
+        self.sizeRadio = wx.RadioButton(panel, id = wx.ID_ANY, label = _("size:"), style = wx.RB_GROUP)
+        self.sizeSpin = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 50, initial = 1)
+        self.sizecolumnRadio = wx.RadioButton(panel, id = wx.ID_ANY, label = _("size from map table column:"))
+        self.sizeColChoice = self.getColsChoice(panel)
+        self.scaleText = wx.StaticText(panel, id = wx.ID_ANY, label = _("scale:"))
+        self.scaleSpin = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 25, initial = 1)
+        
+        self.sizeRadio.SetValue(self.vPropertiesDict['size'] is not None)
+        self.sizecolumnRadio.SetValue(bool(self.vPropertiesDict['sizecolumn']))
+        if self.vPropertiesDict['size']:
+            self.sizeSpin.SetValue(self.vPropertiesDict['size'])
+        else: self.sizeSpin.SetValue(5)
+        if self.vPropertiesDict['sizecolumn']:
+            self.scaleSpin.SetValue(self.vPropertiesDict['scale'])
+            self.sizeColChoice.SetStringSelection(self.vPropertiesDict['sizecolumn'])
+        else:
+            self.scaleSpin.SetValue(1)
+            self.sizeColChoice.SetSelection(0)
+        if not self.connection:   
+            for each in (self.sizecolumnRadio, self.sizeColChoice, self.scaleSpin, self.scaleText):
+                each.Disable()
+            
+        gridBagSizer.Add(self.sizeRadio, pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.sizeSpin, pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.sizecolumnRadio, pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.sizeColChoice, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        gridBagSizer.Add(self.scaleText, pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, border = 0)
+        gridBagSizer.Add(self.scaleSpin, pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnSize, self.sizeRadio)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnSize, self.sizecolumnRadio)
+        
+        #rotation
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Rotation"))
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        gridBagSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        gridBagSizer.AddGrowableCol(1)
+
+        
+        self.rotateCheck = wx.CheckBox(panel, id = wx.ID_ANY, label = _("rotate symbols:"))
+        self.rotateRadio = wx.RadioButton(panel, id = wx.ID_ANY, label = _("counterclockwise in degrees:"), style = wx.RB_GROUP)
+        self.rotateSpin = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 0, max = 360, initial = 0)
+        self.rotatecolumnRadio = wx.RadioButton(panel, id = wx.ID_ANY, label = _("from map table column:"))
+        self.rotateColChoice = self.getColsChoice(panel)
+        
+        self.rotateCheck.SetValue(self.vPropertiesDict['rotation'])
+        self.rotateRadio.SetValue(self.vPropertiesDict['rotate'] is not None)
+        self.rotatecolumnRadio.SetValue(bool(self.vPropertiesDict['rotatecolumn']))
+        if self.vPropertiesDict['rotate']:
+            self.rotateSpin.SetValue(self.vPropertiesDict['rotate'])
+        else:
+            self.rotateSpin.SetValue(0)
+        if self.vPropertiesDict['rotatecolumn']:
+            self.rotateColChoice.SetStringSelection(self.vPropertiesDict['rotatecolumn'])
+        else:
+            self.rotateColChoice.SetSelection(0)
+            
+        gridBagSizer.Add(self.rotateCheck, pos = (0, 0), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.rotateRadio, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.rotateSpin, pos = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.rotatecolumnRadio, pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.rotateColChoice, pos = (2, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        self.Bind(wx.EVT_CHECKBOX, self.OnRotation, self.rotateCheck)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnRotationType, self.rotateRadio)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnRotationType, self.rotatecolumnRadio)
+        
+        panel.SetSizer(border)
+        panel.Fit()
+        return panel
+    
+    def _StyleLinePanel(self, notebook):
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY, size = (-1, -1), style = wx.TAB_TRAVERSAL)
+        notebook.AddPage(page = panel, text = _("Size and style"))
+        
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        #width
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Width"))       
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        gridBagSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        
+        widthText = wx.StaticText(panel, id = wx.ID_ANY, label = _("Set width (pts):"))
+        if fs:
+            self.widthSpin = fs.FloatSpin(panel, id = wx.ID_ANY, min_val = 0, max_val = 30,
+                                        increment = 0.5, value = 1, style = fs.FS_RIGHT)
+            self.widthSpin.SetFormat("%f")
+            self.widthSpin.SetDigits(1)
+        else:
+            self.widthSpin = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 30, initial = 1)
+            
+        self.cwidthCheck = wx.CheckBox(panel, id = wx.ID_ANY, label = _("multiply width by category value"))
+        
+        if self.vPropertiesDict['width']:
+            self.widthSpin.SetValue(self.vPropertiesDict['width'])
+            self.cwidthCheck.SetValue(False)
+        else:
+            self.widthSpin.SetValue(self.vPropertiesDict['cwidth'])
+            self.cwidthCheck.SetValue(True)
+        
+        gridBagSizer.Add(widthText, pos = (0, 0),  flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.widthSpin, pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.cwidthCheck, pos = (1, 0), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        #style
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Line style"))
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        gridBagSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        
+        styleText = wx.StaticText(panel, id = wx.ID_ANY, label = _("Choose line style:"))
+        penStyles = ["solid", "dashed", "dotted", "dashdotted"]
+        self.styleCombo = PenStyleComboBox(panel, choices = penStyles, validator = TCValidator(flag = 'ZERO_AND_ONE_ONLY'))
+##        self.styleCombo = wx.ComboBox(panel, id = wx.ID_ANY,
+##                            choices = ["solid", "dashed", "dotted", "dashdotted"],
+##                            validator = TCValidator(flag = 'ZERO_AND_ONE_ONLY'))
+##        self.styleCombo.SetToolTipString(_("It's possible to enter a series of 0's and 1's too. "\
+##                                    "The first block of repeated zeros or ones represents 'draw', "\
+##                                    "the second block represents 'blank'. An even number of blocks "\
+##                                    "will repeat the pattern, an odd number of blocks will alternate the pattern."))
+        linecapText = wx.StaticText(panel, id = wx.ID_ANY, label = _("Choose linecap:"))
+        self.linecapChoice = wx.Choice(panel, id = wx.ID_ANY, choices = ["butt", "round", "extended_butt"])
+        
+        self.styleCombo.SetValue(self.vPropertiesDict['style'])
+        self.linecapChoice.SetStringSelection(self.vPropertiesDict['linecap'])
+        
+        gridBagSizer.Add(styleText, pos = (0, 0),  flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.styleCombo, pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        gridBagSizer.Add(linecapText, pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.linecapChoice, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        panel.SetSizer(border)
+        panel.Fit()
+        return panel
+        
+    def _StyleAreaPanel(self, notebook):
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY, size = (-1, -1), style = wx.TAB_TRAVERSAL)
+        notebook.AddPage(page = panel, text = _("Size and style"))
+        
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        #pattern
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Pattern"))
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        gridBagSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        gridBagSizer.AddGrowableCol(1)
+        
+        self.patternCheck = wx.CheckBox(panel, id = wx.ID_ANY, label = _("use pattern:"))
+        self.patFileCtrl = filebrowse.FileBrowseButton(panel, id = wx.ID_ANY, labelText = _("Choose pattern file:"),
+                                buttonText =  _("Browse"), toolTip = _("Type filename or click browse to choose file"), 
+                                dialogTitle = _("Choose a file"), startDirectory = self.patternPath, initialValue = '',
+                                fileMask = "Encapsulated PostScript (*.eps)|*.eps|All files (*.*)|*.*", fileMode = wx.OPEN)
+        self.patWidthText = wx.StaticText(panel, id = wx.ID_ANY, label = _("pattern line width (pts):"))
+        self.patWidthSpin = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 25, initial = 1)
+        self.patScaleText = wx.StaticText(panel, id = wx.ID_ANY, label = _("pattern scale factor:"))
+        self.patScaleSpin = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 25, initial = 1)
+        
+        self.patternCheck.SetValue(bool(self.vPropertiesDict['pat']))
+        if self.patternCheck.GetValue():
+            self.patFileCtrl.SetValue(self.vPropertiesDict['pat'])
+            self.patWidthSpin.SetValue(self.vPropertiesDict['pwidth'])
+            self.patScaleSpin.SetValue(self.vPropertiesDict['scale'])
+        
+        gridBagSizer.Add(self.patternCheck, pos = (0, 0),  flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.patFileCtrl, pos = (1, 0), span = (1, 2),flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        gridBagSizer.Add(self.patWidthText, pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.patWidthSpin, pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.patScaleText, pos = (3, 0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.patScaleSpin, pos = (3, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        self.Bind(wx.EVT_CHECKBOX, self.OnPattern, self.patternCheck)
+        
+        panel.SetSizer(border)
+        panel.Fit()
+        return panel
+
+    def OnLayer(self, event):
+        """!Change columns on layer change """
+        if self.layerChoice.GetStringSelection() == self.currLayer:
+            return
+        self.currLayer = self.layerChoice.GetStringSelection()
+        if self.connection:
+            cols = self.mapDBInfo.GetColumns(self.mapDBInfo.layers[int(self.currLayer)]['table']) 
+        else:
+            cols = []
+
+        self.choiceColumns.SetItems(cols)
+
+        self.choiceColumns.SetSelection(0)
+        if self.type in ('points', 'lines'):
+            self.colorColChoice.SetItems(cols)
+            self.colorColChoice.SetSelection(0)
+            
+    def OnOutline(self, event):
+        for widget in self.gridBagSizerO.GetChildren():
+            if widget.GetWindow() != self.outlineCheck:
+                widget.GetWindow().Enable(self.outlineCheck.GetValue())
+                
+    def OnFill(self, event):
+        enable = self.fillCheck.GetValue()
+        
+        self.colorColChoice.Enable(enable)
+        self.colorColRadio.Enable(enable)
+        self.fillColorPicker.Enable(enable)
+        self.colorPickerRadio.Enable(enable)
+        if enable:
+            self.OnColor(None)
+        if not self.connection:
+            self.colorColChoice.Disable()
+            self.colorColRadio.Disable()
+            
+    def OnColor(self, event):
+        self.colorColChoice.Enable(self.colorColRadio.GetValue())
+        self.fillColorPicker.Enable(self.colorPickerRadio.GetValue())
+            
+    def OnSize(self, event):
+        self.sizeSpin.Enable(self.sizeRadio.GetValue())
+        self.sizeColChoice.Enable(self.sizecolumnRadio.GetValue())
+        self.scaleText.Enable(self.sizecolumnRadio.GetValue())
+        self.scaleSpin.Enable(self.sizecolumnRadio.GetValue())
+        
+    def OnRotation(self, event):
+        for each in (self.rotateRadio, self.rotatecolumnRadio, self.rotateColChoice, self.rotateSpin):
+            if self.rotateCheck.GetValue():
+                each.Enable()
+                self.OnRotationType(event = None)     
+            else:
+                each.Disable()
+           
+    def OnRotationType(self, event):
+        self.rotateSpin.Enable(self.rotateRadio.GetValue())
+        self.rotateColChoice.Enable(self.rotatecolumnRadio.GetValue())
+        
+    def OnPattern(self, event):
+        for each in (self.patFileCtrl, self.patWidthText, self.patWidthSpin, self.patScaleText, self.patScaleSpin):
+            each.Enable(self.patternCheck.GetValue())
+            
+    def OnSymbology(self, event):
+        useSymbol = self.symbolRadio.GetValue()
+        
+        self.symbolButton.Enable(useSymbol)
+        self.symbolName.Enable(useSymbol)
+        self.epsFileCtrl.Enable(not useSymbol)
+            
+    def OnSymbolSelection(self, event):
+        dlg = SymbolDialog(self, symbolPath = globalvar.ETCSYMBOLDIR,
+                           currentSymbol = self.symbolName.GetLabel())
+        if dlg.ShowModal() == wx.ID_OK:
+            img = dlg.GetSelectedSymbol(fullPath = True)
+            name = dlg.GetSelectedSymbol(fullPath = False)
+            self.symbolButton.SetBitmapLabel(wx.Bitmap(img + '.png'))
+            self.symbolName.SetLabel(name)
+            
+        dlg.Destroy()
+                
+    def EnableLayerSelection(self, enable = True):
+        for widget in self.gridBagSizerL.GetChildren():
+            if widget.GetWindow() != self.warning:
+                widget.GetWindow().Enable(enable)
+                
+    def getColsChoice(self, parent):
+        """!Returns a wx.Choice with table columns"""
+        if self.connection:
+            cols = self.mapDBInfo.GetColumns(self.mapDBInfo.layers[int(self.currLayer)]['table']) 
+        else:
+            cols = []
+
+        choice = wx.Choice(parent = parent, id = wx.ID_ANY, choices = cols)
+        return choice
+        
+    def update(self):
+        #feature type
+        if self.type in ('lines', 'points'):
+            featureType = None
+            if self.checkType1.GetValue():
+                featureType = self.checkType1.GetName()
+                if self.checkType2.GetValue():
+                    featureType += " or " + self.checkType2.GetName()
+            elif self.checkType2.GetValue():
+                featureType = self.checkType2.GetName()
+            if featureType:
+                self.vPropertiesDict['type'] = featureType
+            
+        # is connection
+        self.vPropertiesDict['connection'] = self.connection
+        if self.connection:
+            self.vPropertiesDict['layer'] = self.layerChoice.GetStringSelection()
+            if self.radioCats.GetValue() and not self.textCtrlCats.IsEmpty():
+                self.vPropertiesDict['cats'] = self.textCtrlCats.GetValue()
+            elif self.radioWhere.GetValue() and not self.textCtrlWhere.IsEmpty():
+                self.vPropertiesDict['where'] = self.choiceColumns.GetStringSelection() + " " \
+                                                                + self.textCtrlWhere.GetValue()
+        #mask
+        if self.mask.GetValue():
+            self.vPropertiesDict['masked'] = 'y' 
+        else:
+            self.vPropertiesDict['masked'] = 'n'
+
+        #colors
+        if self.type in ('points', 'areas'):
+            if self.outlineCheck.GetValue():
+                self.vPropertiesDict['color'] = convertRGB(self.colorPicker.GetColour())
+                self.vPropertiesDict['width'] = self.widthSpin.GetValue()
+            else:
+                self.vPropertiesDict['color'] = 'none'
+                
+            if self.fillCheck.GetValue():
+                if self.colorPickerRadio.GetValue():
+                    self.vPropertiesDict['fcolor'] = convertRGB(self.fillColorPicker.GetColour())
+                    self.vPropertiesDict['rgbcolumn'] = None
+                if self.colorColRadio.GetValue():
+                    self.vPropertiesDict['fcolor'] = 'none'# this color is taken in case of no record in rgb column
+                    self.vPropertiesDict['rgbcolumn'] = self.colorColChoice.GetStringSelection()
+            else:
+                self.vPropertiesDict['fcolor'] = 'none'    
+                
+        if self.type == 'lines':
+                #hcolor only when no rgbcolumn
+            if self.outlineCheck.GetValue():# and self.fillCheck.GetValue() and self.colorColRadio.GetValue():
+                self.vPropertiesDict['hcolor'] = convertRGB(self.colorPicker.GetColour())
+                self.vPropertiesDict['hwidth'] = self.outWidthSpin.GetValue()
+                
+            else:
+                self.vPropertiesDict['hcolor'] = 'none'
+                
+            if self.colorPickerRadio.GetValue():
+                self.vPropertiesDict['color'] = convertRGB(self.fillColorPicker.GetColour())
+                self.vPropertiesDict['rgbcolumn'] = None
+            if self.colorColRadio.GetValue():
+                self.vPropertiesDict['color'] = 'none'# this color is taken in case of no record in rgb column
+                self.vPropertiesDict['rgbcolumn'] = self.colorColChoice.GetStringSelection()
+        #
+        #size and style
+        #
+        
+        if self.type == 'points':
+            #symbols
+            if self.symbolRadio.GetValue():
+                self.vPropertiesDict['symbol'] = self.symbolName.GetLabel()
+                self.vPropertiesDict['eps'] = None
+            else:
+                self.vPropertiesDict['eps'] = self.epsFileCtrl.GetValue()
+            #size
+            if self.sizeRadio.GetValue():
+                self.vPropertiesDict['size'] = self.sizeSpin.GetValue()
+                self.vPropertiesDict['sizecolumn'] = None
+                self.vPropertiesDict['scale'] = None
+            else:
+                self.vPropertiesDict['sizecolumn'] = self.sizeColChoice.GetStringSelection()
+                self.vPropertiesDict['scale'] = self.scaleSpin.GetValue()
+                self.vPropertiesDict['size'] = None
+            
+            #rotation
+            self.vPropertiesDict['rotate'] = None
+            self.vPropertiesDict['rotatecolumn'] = None
+            self.vPropertiesDict['rotation'] = False
+            if self.rotateCheck.GetValue():
+                self.vPropertiesDict['rotation'] = True
+            if self.rotateRadio.GetValue():
+                self.vPropertiesDict['rotate'] = self.rotateSpin.GetValue()
+            else:
+                self.vPropertiesDict['rotatecolumn'] = self.rotateColChoice.GetStringSelection()
+                
+        if self.type == 'areas':
+            #pattern
+            self.vPropertiesDict['pat'] = None 
+            if self.patternCheck.GetValue() and bool(self.patFileCtrl.GetValue()):
+                self.vPropertiesDict['pat'] = self.patFileCtrl.GetValue()
+                self.vPropertiesDict['pwidth'] = self.patWidthSpin.GetValue()
+                self.vPropertiesDict['scale'] = self.patScaleSpin.GetValue()
+                
+        if self.type == 'lines':
+            #width
+            if self.cwidthCheck.GetValue():
+                self.vPropertiesDict['cwidth'] = self.widthSpin.GetValue()
+                self.vPropertiesDict['width'] = None
+            else:
+                self.vPropertiesDict['width'] = self.widthSpin.GetValue()
+                self.vPropertiesDict['cwidth'] = None
+            #line style
+            if self.styleCombo.GetValue():
+                self.vPropertiesDict['style'] = self.styleCombo.GetValue() 
+            else:
+                self.vPropertiesDict['style'] = 'solid'
+
+            self.vPropertiesDict['linecap'] = self.linecapChoice.GetStringSelection()
+            
+    def OnOK(self, event):
+        self.update()
+        event.Skip()
+        
+class LegendDialog(PsmapDialog):
+    def __init__(self, parent, id, settings, page):
+        PsmapDialog.__init__(self, parent = parent, id = id, title = "Legend settings", settings = settings)
+        self.objectType = ('rasterLegend', 'vectorLegend')
+        self.instruction = settings
+        map = self.instruction.FindInstructionByType('map')
+        if map:
+            self.mapId = map.id 
+        else:
+            self.mapId = None
+
+        vector = self.instruction.FindInstructionByType('vector')
+        if vector:
+            self.vectorId = vector.id 
+        else:
+            self.vectorId = None
+
+        raster = self.instruction.FindInstructionByType('raster')
+        if raster:
+            self.rasterId = raster.id 
+        else:
+            self.rasterId = None
+
+        self.pageId = self.instruction.FindInstructionByType('page').id
+        currPage = self.instruction[self.pageId].GetInstruction()
+        #raster legend
+        if self.id[0] is not None:
+            self.rasterLegend = self.instruction[self.id[0]]
+            self.rLegendDict = self.rasterLegend.GetInstruction()
+        else:
+            self.id[0] = wx.NewId()
+            self.rasterLegend = RasterLegend(self.id[0])
+            self.rLegendDict = self.rasterLegend.GetInstruction()
+            self.rLegendDict['where'] = currPage['Left'], currPage['Top']
+            
+            
+        #vector legend    
+        if self.id[1] is not None:
+            self.vLegendDict = self.instruction[self.id[1]].GetInstruction()
+        else:
+            self.id[1] = wx.NewId()
+            vectorLegend = VectorLegend(self.id[1])
+            self.vLegendDict = vectorLegend.GetInstruction()
+            self.vLegendDict['where'] = currPage['Left'], currPage['Top']
+            
+        if self.rasterId:
+            self.currRaster = self.instruction[self.rasterId]['raster'] 
+        else:
+            self.currRaster = None
+
+        #notebook
+        self.notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
+        self.panelRaster = self._rasterLegend(self.notebook)
+        self.panelVector = self._vectorLegend(self.notebook)  
+        self.OnRaster(None)
+        self.OnRange(None)
+        self.OnIsLegend(None)
+        self.OnSpan(None)
+        self.OnBorder(None)
+                
+        self._layout(self.notebook)
+        self.notebook.ChangeSelection(page)
+        self.notebook.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGING, self.OnPageChanging)
+        
+    def OnPageChanging(self, event):
+        """!Workaround to scroll up to see the checkbox"""
+        wx.CallAfter(self.FindWindowByName('rasterPanel').ScrollChildIntoView,
+                                            self.FindWindowByName('showRLegend'))
+        wx.CallAfter(self.FindWindowByName('vectorPanel').ScrollChildIntoView,
+                                            self.FindWindowByName('showVLegend'))
+                                            
+    def _rasterLegend(self, notebook):
+        panel = scrolled.ScrolledPanel(parent = notebook, id = wx.ID_ANY, size = (-1, 500), style = wx.TAB_TRAVERSAL)
+        panel.SetupScrolling(scroll_x = False, scroll_y = True)
+        panel.SetName('rasterPanel')
+        notebook.AddPage(page = panel, text = _("Raster legend"))
+
+        border = wx.BoxSizer(wx.VERTICAL)
+        # is legend
+        self.isRLegend = wx.CheckBox(panel, id = wx.ID_ANY, label = _("Show raster legend"))
+        self.isRLegend.SetValue(self.rLegendDict['rLegend'])
+        self.isRLegend.SetName("showRLegend")
+        border.Add(item = self.isRLegend, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+
+        # choose raster
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Source raster"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        flexSizer = wx.FlexGridSizer (cols = 2, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(1)
+        
+        self.rasterDefault = wx.RadioButton(panel, id = wx.ID_ANY, label = _("current raster"), style = wx.RB_GROUP)
+        self.rasterOther = wx.RadioButton(panel, id = wx.ID_ANY, label = _("select raster"))
+        self.rasterDefault.SetValue(self.rLegendDict['rasterDefault'])#
+        self.rasterOther.SetValue(not self.rLegendDict['rasterDefault'])#
+
+        rasterType = getRasterType(map = self.currRaster)
+
+        self.rasterCurrent = wx.StaticText(panel, id = wx.ID_ANY,
+                                label = _("%(rast)s: type %(type)s") % { 'rast' : self.currRaster,
+                                                                         'type' : rasterType })
+        self.rasterSelect = Select(panel, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
+                                    type = 'raster', multiple = False,
+                                    updateOnPopup = True, onPopup = None)
+        if not self.rLegendDict['rasterDefault']:
+            self.rasterSelect.SetValue(self.rLegendDict['raster'])
+        else:
+            self.rasterSelect.SetValue('')
+        flexSizer.Add(self.rasterDefault, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        flexSizer.Add(self.rasterCurrent, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border = 10)
+        flexSizer.Add(self.rasterOther, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        flexSizer.Add(self.rasterSelect, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, border = 0)
+        
+        sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        # type of legend
+        
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Type of legend"))        
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        self.discrete = wx.RadioButton(parent = panel, id = wx.ID_ANY, 
+                        label = " %s " % _("discrete legend (categorical maps)"), style = wx.RB_GROUP)
+        self.continuous = wx.RadioButton(parent = panel, id = wx.ID_ANY, 
+                        label = " %s " % _("continuous color gradient legend (floating point map)"))
+        
+        vbox.Add(self.discrete, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 0)
+        vbox.Add(self.continuous, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 0)
+        sizer.Add(item = vbox, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        # size, position and font
+        self.sizePositionFont(legendType = 'raster', parent = panel, mainSizer = border)
+        
+        # advanced settings
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Advanced legend settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridBagSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+        # no data
+        self.nodata = wx.CheckBox(panel, id = wx.ID_ANY, label = _('draw "no data" box'))
+        if self.rLegendDict['nodata'] == 'y':
+            self.nodata.SetValue(True)
+        else: 
+            self.nodata.SetValue(False)
+        #tickbar
+        self.ticks = wx.CheckBox(panel, id = wx.ID_ANY, label = _("draw ticks across color table"))
+        if self.rLegendDict['tickbar'] == 'y':
+            self.ticks.SetValue(True)
+        else:
+            self.ticks.SetValue(False)
+        # range
+        if self.rasterId and self.instruction[self.rasterId]['raster']:
+            rinfo = grass.raster_info(self.instruction[self.rasterId]['raster'])
+            self.minim, self.maxim = rinfo['min'], rinfo['max']
+        else:
+            self.minim, self.maxim = 0,0
+        self.range = wx.CheckBox(panel, id = wx.ID_ANY, label = _("range"))
+        self.range.SetValue(self.rLegendDict['range'])
+        self.minText =  wx.StaticText(panel, id = wx.ID_ANY, label = "min (%s)" % self.minim)
+        self.maxText =  wx.StaticText(panel, id = wx.ID_ANY, label = "max (%s)" % self.maxim)       
+        self.min = wx.TextCtrl(panel, id = wx.ID_ANY, value = str(self.rLegendDict['min']))
+        self.max = wx.TextCtrl(panel, id = wx.ID_ANY, value = str(self.rLegendDict['max']))
+        
+        gridBagSizer.Add(self.nodata, pos = (0,0), span = (1,5), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.ticks, pos = (1,0), span = (1,5), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.range, pos = (2,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.minText, pos = (2,1), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, border = 0)
+        gridBagSizer.Add(self.min, pos = (2,2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.maxText, pos = (2,3), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, border = 0)
+        gridBagSizer.Add(self.max, pos = (2,4), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+   
+        panel.SetSizer(border)
+        panel.Fit()
+        
+        # bindings
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnRaster, self.rasterDefault)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnRaster, self.rasterOther)
+        self.Bind(wx.EVT_CHECKBOX, self.OnIsLegend, self.isRLegend)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnDiscrete, self.discrete)
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnDiscrete, self.continuous)
+##        self.Bind(wx.EVT_CHECKBOX, self.OnDefaultSize, panel.defaultSize)
+        self.Bind(wx.EVT_CHECKBOX, self.OnRange, self.range)
+        self.rasterSelect.GetTextCtrl().Bind(wx.EVT_TEXT, self.OnRaster)
+        
+        return panel
+    
+    def _vectorLegend(self, notebook):
+        panel = scrolled.ScrolledPanel(parent = notebook, id = wx.ID_ANY, size = (-1, 500), style = wx.TAB_TRAVERSAL)
+        panel.SetupScrolling(scroll_x = False, scroll_y = True)
+        panel.SetName('vectorPanel')
+        notebook.AddPage(page = panel, text = _("Vector legend"))
+
+        border = wx.BoxSizer(wx.VERTICAL)
+        # is legend
+        self.isVLegend = wx.CheckBox(panel, id = wx.ID_ANY, label = _("Show vector legend"))
+        self.isVLegend.SetValue(self.vLegendDict['vLegend'])
+        self.isVLegend.SetName("showVLegend")
+        border.Add(item = self.isVLegend, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        #vector maps, their order, labels
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Source vector maps"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridBagSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+        gridBagSizer.AddGrowableCol(0,3)
+        gridBagSizer.AddGrowableCol(1,1)
+        
+        vectorText = wx.StaticText(panel, id = wx.ID_ANY, label = _("Choose vector maps and their order in legend"))
+
+        self.vectorListCtrl = CheckListCtrl(panel)
+        
+        self.vectorListCtrl.InsertColumn(0, _("Vector map"))
+        self.vectorListCtrl.InsertColumn(1, _("Label"))
+        if self.vectorId:
+            vectors = sorted(self.instruction[self.vectorId]['list'], key = lambda x: x[3])
+            
+            for vector in vectors:
+                index = self.vectorListCtrl.InsertStringItem(sys.maxint, vector[0].split('@')[0])
+                self.vectorListCtrl.SetStringItem(index, 1, vector[4])
+                self.vectorListCtrl.SetItemData(index, index)
+                self.vectorListCtrl.CheckItem(index, True)
+                if vector[3] == 0:
+                    self.vectorListCtrl.CheckItem(index, False)
+        if not self.vectorId:
+            self.vectorListCtrl.SetColumnWidth(0, 100)
+        else:
+            self.vectorListCtrl.SetColumnWidth(0, wx.LIST_AUTOSIZE)
+        self.vectorListCtrl.SetColumnWidth(1, wx.LIST_AUTOSIZE)
+        
+        self.btnUp = wx.Button(panel, id = wx.ID_ANY, label = _("Up"))
+        self.btnDown = wx.Button(panel, id = wx.ID_ANY, label = _("Down"))
+        self.btnLabel = wx.Button(panel, id = wx.ID_ANY, label = _("Edit label"))
+      
+        gridBagSizer.Add(vectorText, pos = (0,0), span = (1,2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.vectorListCtrl, pos = (1,0), span = (3,1), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        gridBagSizer.Add(self.btnUp, pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.btnDown, pos = (2,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.btnLabel, pos = (3,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        # size, position and font
+        self.sizePositionFont(legendType = 'vector', parent = panel, mainSizer = border)
+         
+        # border
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Border"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        flexGridSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
+        
+        self.borderCheck = wx.CheckBox(panel, id = wx.ID_ANY, label = _("draw border around legend"))
+        self.borderColorCtrl = wx.ColourPickerCtrl(panel, id = wx.ID_ANY, style = wx.FNTP_FONTDESC_AS_LABEL)
+        if self.vLegendDict['border'] == 'none':
+            self.borderColorCtrl.SetColour(wx.BLACK)
+            self.borderCheck.SetValue(False)
+        else:
+            self.borderColorCtrl.SetColour(convertRGB(self.vLegendDict['border']))
+            self.borderCheck.SetValue(True)
+            
+        flexGridSizer.Add(self.borderCheck, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)    
+        flexGridSizer.Add(self.borderColorCtrl, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        sizer.Add(item = flexGridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        self.Bind(wx.EVT_BUTTON, self.OnUp, self.btnUp)
+        self.Bind(wx.EVT_BUTTON, self.OnDown, self.btnDown)  
+        self.Bind(wx.EVT_BUTTON, self.OnEditLabel, self.btnLabel)
+        self.Bind(wx.EVT_CHECKBOX, self.OnIsLegend, self.isVLegend)    
+        self.Bind(wx.EVT_CHECKBOX, self.OnSpan, panel.spanRadio)  
+        self.Bind(wx.EVT_CHECKBOX, self.OnBorder, self.borderCheck)
+        self.Bind(wx.EVT_FONTPICKER_CHANGED, self.OnFont, panel.font['fontCtrl']) 
+        
+        panel.SetSizer(border)
+        
+        panel.Fit()
+        return panel
+    
+    def sizePositionFont(self, legendType, parent, mainSizer):
+        """!Insert widgets for size, position and font control"""
+        if legendType == 'raster':
+            legendDict = self.rLegendDict  
+        else:
+            legendDict = self.vLegendDict
+        panel = parent
+        border = mainSizer
+        
+        # size and position        
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Size and position"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        #unit
+        self.AddUnits(parent = panel, dialogDict = legendDict)
+        unitBox = wx.BoxSizer(wx.HORIZONTAL)
+        unitBox.Add(panel.units['unitsLabel'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border = 10)
+        unitBox.Add(panel.units['unitsCtrl'], proportion = 1, flag = wx.ALL, border = 5)
+        sizer.Add(unitBox, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        hBox = wx.BoxSizer(wx.HORIZONTAL)
+        posBox = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " %_("Position"))
+        posSizer = wx.StaticBoxSizer(posBox, wx.VERTICAL)       
+        sizeBox = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Size"))
+        sizeSizer = wx.StaticBoxSizer(sizeBox, wx.VERTICAL) 
+        posGridBagSizer = wx.GridBagSizer(hgap = 10, vgap = 5)
+        posGridBagSizer.AddGrowableRow(2)
+        
+        #position
+        self.AddPosition(parent = panel, dialogDict = legendDict)
+        
+        posGridBagSizer.Add(panel.position['xLabel'], pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        posGridBagSizer.Add(panel.position['xCtrl'], pos = (0,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        posGridBagSizer.Add(panel.position['yLabel'], pos = (1,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        posGridBagSizer.Add(panel.position['yCtrl'], pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        posGridBagSizer.Add(panel.position['comment'], pos = (2,0), span = (1,2), flag =wx.ALIGN_BOTTOM, border = 0)
+        posSizer.Add(posGridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        
+        #size
+        width = wx.StaticText(panel, id = wx.ID_ANY, label = _("Width:"))
+        if legendDict['width']:
+            w = self.unitConv.convert(value = float(legendDict['width']), fromUnit = 'inch', toUnit = legendDict['unit'])
+        else: 
+            w = ''
+        panel.widthCtrl = wx.TextCtrl(panel, id = wx.ID_ANY, value = str(w), validator = TCValidator("DIGIT_ONLY"))
+        panel.widthCtrl.SetToolTipString(_("Leave the edit field empty, to use default values."))
+        
+        if legendType == 'raster':
+##            panel.defaultSize = wx.CheckBox(panel, id = wx.ID_ANY, label = _("Use default size"))
+##            panel.defaultSize.SetValue(legendDict['defaultSize'])
+            
+            panel.heightOrColumnsLabel = wx.StaticText(panel, id = wx.ID_ANY, label = _("Height:"))
+            if legendDict['height']:
+                h = self.unitConv.convert(value = float(legendDict['height']), fromUnit = 'inch', toUnit = legendDict['unit'])
+            else:
+                h = ''
+            panel.heightOrColumnsCtrl = wx.TextCtrl(panel, id = wx.ID_ANY, value = str(h), validator = TCValidator("DIGIT_ONLY"))
+            
+            self.rSizeGBSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+##            self.rSizeGBSizer.Add(panel.defaultSize, pos = (0,0), span = (1,2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+            self.rSizeGBSizer.Add(width, pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+            self.rSizeGBSizer.Add(panel.widthCtrl, pos = (0,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+            self.rSizeGBSizer.Add(panel.heightOrColumnsLabel, pos = (1,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+            self.rSizeGBSizer.Add(panel.heightOrColumnsCtrl, pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+            sizeSizer.Add(self.rSizeGBSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+            
+        if legendType == 'vector':
+            panel.widthCtrl.SetToolTipString(_("Width of the color symbol (for lines)\nin front of the legend text")) 
+            #columns
+            minVect, maxVect = 0, 0
+            if self.vectorId:
+                minVect = 1
+                maxVect = min(10, len(self.instruction[self.vectorId]['list']))
+            cols = wx.StaticText(panel, id = wx.ID_ANY, label = _("Columns:"))
+            panel.colsCtrl = wx.SpinCtrl(panel, id = wx.ID_ANY, value = "",
+                                        min = minVect, max = maxVect, initial = legendDict['cols'])
+            #span
+            panel.spanRadio = wx.CheckBox(panel, id = wx.ID_ANY, label = _("column span:"))
+            panel.spanTextCtrl = wx.TextCtrl(panel, id = wx.ID_ANY, value = '')
+            panel.spanTextCtrl.SetToolTipString(_("Column separation distance between the left edges\n"\
+                                                "of two columns in a multicolumn legend"))
+            if legendDict['span']:
+                panel.spanRadio.SetValue(True)
+                s = self.unitConv.convert(value = float(legendDict['span']), fromUnit = 'inch', toUnit = legendDict['unit'])    
+                panel.spanTextCtrl.SetValue(str(s))
+            else:
+                panel.spanRadio.SetValue(False)
+                
+            self.vSizeGBSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+            self.vSizeGBSizer.AddGrowableCol(1)
+            self.vSizeGBSizer.Add(width, pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+            self.vSizeGBSizer.Add(panel.widthCtrl, pos = (0,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+            self.vSizeGBSizer.Add(cols, pos = (1,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+            self.vSizeGBSizer.Add(panel.colsCtrl, pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+            self.vSizeGBSizer.Add(panel.spanRadio, pos = (2,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+            self.vSizeGBSizer.Add(panel.spanTextCtrl, pos = (2,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+            sizeSizer.Add(self.vSizeGBSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)        
+        
+        hBox.Add(posSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 3)
+        hBox.Add(sizeSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 3)
+        sizer.Add(hBox, proportion = 0, flag = wx.EXPAND, border = 0)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+              
+        # font        
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Font settings"))
+        fontSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        flexSizer = wx.FlexGridSizer (cols = 2, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(1)
+        
+        if legendType == 'raster':
+            self.AddFont(parent = panel, dialogDict = legendDict, color = True)
+        else:
+            self.AddFont(parent = panel, dialogDict = legendDict, color = False)            
+        flexSizer.Add(panel.font['fontLabel'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        flexSizer.Add(panel.font['fontCtrl'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        flexSizer.Add(panel.font['fontSizeLabel'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        flexSizer.Add(panel.font['fontSizeCtrl'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        if legendType == 'raster':
+            flexSizer.Add(panel.font['colorLabel'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+            flexSizer.Add(panel.font['colorCtrl'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        fontSizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = fontSizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)    
+
+    #   some enable/disable methods  
+        
+    def OnIsLegend(self, event):
+        """!Enables and disables controls, it depends if raster or vector legend is checked"""
+        page = self.notebook.GetSelection()
+        if page == 0 or event is None:
+            children = self.panelRaster.GetChildren()
+            if self.isRLegend.GetValue():
+                for i,widget in enumerate(children):
+                        widget.Enable()
+                self.OnRaster(None)
+                self.OnRange(None)
+                self.OnDiscrete(None)
+            else:
+                for widget in children:
+                    if widget.GetName() != 'showRLegend':
+                        widget.Disable()
+        if page == 1 or event is None:
+            children = self.panelVector.GetChildren()
+            if self.isVLegend.GetValue():
+                for i, widget in enumerate(children):
+                        widget.Enable()
+                self.OnSpan(None)
+                self.OnBorder(None)
+            else:
+                for widget in children:
+                    if widget.GetName() != 'showVLegend':
+                        widget.Disable()
+                    
+    def OnRaster(self, event):
+        if self.rasterDefault.GetValue():#default
+            self.rasterSelect.Disable()
+            type = getRasterType(self.currRaster)
+        else:#select raster
+            self.rasterSelect.Enable()
+            map = self.rasterSelect.GetValue()
+            type = getRasterType(map)
+  
+        if type == 'CELL':
+            self.discrete.SetValue(True)
+        elif type in ('FCELL', 'DCELL'):
+            self.continuous.SetValue(True)
+        if event is None:
+            if self.rLegendDict['discrete'] == 'y':
+                self.discrete.SetValue(True)
+            elif self.rLegendDict['discrete'] == 'n':
+                self.continuous.SetValue(True)
+        self.OnDiscrete(None)
+        
+    def OnDiscrete(self, event):
+        """! Change control according to the type of legend"""
+        enabledSize = self.panelRaster.heightOrColumnsCtrl.IsEnabled()
+        self.panelRaster.heightOrColumnsCtrl.Destroy()
+        if self.discrete.GetValue():
+            self.panelRaster.heightOrColumnsLabel.SetLabel(_("Columns:"))
+            self.panelRaster.heightOrColumnsCtrl = wx.SpinCtrl(self.panelRaster, id = wx.ID_ANY, value = "", min = 1, max = 10, initial = self.rLegendDict['cols'])
+            self.panelRaster.heightOrColumnsCtrl.Enable(enabledSize)
+            self.nodata.Enable()
+            self.range.Disable()
+            self.min.Disable()
+            self.max.Disable()
+            self.minText.Disable()
+            self.maxText.Disable()
+            self.ticks.Disable()
+        else:
+            self.panelRaster.heightOrColumnsLabel.SetLabel(_("Height:"))
+            if self.rLegendDict['height']:
+                h = self.unitConv.convert(value = float(self.rLegendDict['height']), fromUnit = 'inch', toUnit = self.rLegendDict['unit'])
+            else:
+                h = ''
+            self.panelRaster.heightOrColumnsCtrl = wx.TextCtrl(self.panelRaster, id = wx.ID_ANY,
+                                                    value = str(h), validator = TCValidator("DIGIT_ONLY"))
+            self.panelRaster.heightOrColumnsCtrl.Enable(enabledSize)
+            self.nodata.Disable()
+            self.range.Enable()
+            if self.range.GetValue():
+                self.minText.Enable()
+                self.maxText.Enable()
+                self.min.Enable()
+                self.max.Enable()
+            self.ticks.Enable()
+        
+        self.rSizeGBSizer.Add(self.panelRaster.heightOrColumnsCtrl, pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        self.panelRaster.Layout()
+        self.panelRaster.Fit()   
+        
+    def OnRange(self, event):
+        if not self.range.GetValue():
+            self.min.Disable()        
+            self.max.Disable()
+            self.minText.Disable()
+            self.maxText.Disable()
+        else:
+            self.min.Enable()        
+            self.max.Enable() 
+            self.minText.Enable()
+            self.maxText.Enable()           
+     
+    def OnUp(self, event):
+        """!Moves selected map up, changes order in vector legend"""
+        if self.vectorListCtrl.GetFirstSelected() != -1:
+            pos = self.vectorListCtrl.GetFirstSelected()
+            if pos:
+                idx1 = self.vectorListCtrl.GetItemData(pos) - 1
+                idx2 = self.vectorListCtrl.GetItemData(pos - 1) + 1
+                self.vectorListCtrl.SetItemData(pos, idx1) 
+                self.vectorListCtrl.SetItemData(pos - 1, idx2) 
+                self.vectorListCtrl.SortItems(cmp)
+                if pos > 0:
+                    selected = (pos - 1) 
+                else:
+                    selected = 0
+
+                self.vectorListCtrl.Select(selected)
+       
+    def OnDown(self, event):
+        """!Moves selected map down, changes order in vector legend"""
+        if self.vectorListCtrl.GetFirstSelected() != -1:
+            pos = self.vectorListCtrl.GetFirstSelected()
+            if pos != self.vectorListCtrl.GetItemCount() - 1:
+                idx1 = self.vectorListCtrl.GetItemData(pos) + 1
+                idx2 = self.vectorListCtrl.GetItemData(pos + 1) - 1
+                self.vectorListCtrl.SetItemData(pos, idx1) 
+                self.vectorListCtrl.SetItemData(pos + 1, idx2) 
+                self.vectorListCtrl.SortItems(cmp)
+                if pos < self.vectorListCtrl.GetItemCount() -1:
+                    selected = (pos + 1) 
+                else:
+                    selected = self.vectorListCtrl.GetItemCount() -1
+
+                self.vectorListCtrl.Select(selected)
+                
+    def OnEditLabel(self, event):
+        """!Change legend label of vector map"""
+        if self.vectorListCtrl.GetFirstSelected() != -1:
+            idx = self.vectorListCtrl.GetFirstSelected()
+            default = self.vectorListCtrl.GetItem(idx, 1).GetText()
+            dlg = wx.TextEntryDialog(self, message = _("Edit legend label:"), caption = _("Edit label"),
+                                    defaultValue = default, style = wx.OK|wx.CANCEL|wx.CENTRE)
+            if dlg.ShowModal() == wx.ID_OK:
+                new = dlg.GetValue()
+                self.vectorListCtrl.SetStringItem(idx, 1, new)
+            dlg.Destroy()
+        
+    def OnSpan(self, event):
+        self.panelVector.spanTextCtrl.Enable(self.panelVector.spanRadio.GetValue())
+    def OnFont(self, event):
+        """!Changes default width according to fontsize, width [inch] = fontsize[pt]/24"""   
+##        fontsize = self.panelVector.font['fontCtrl'].GetSelectedFont().GetPointSize() 
+        fontsize = self.panelVector.font['fontSizeCtrl'].GetValue()
+        unit = self.unitConv.findUnit(self.panelVector.units['unitsCtrl'].GetStringSelection())
+        w = fontsize/24.
+        width = self.unitConv.convert(value = w, fromUnit = 'inch', toUnit = unit)
+        self.panelVector.widthCtrl.SetValue("%3.2f" % width)
+        
+    def OnBorder(self, event):
+        """!Enables/disables colorPickerCtrl for border"""    
+        self.borderColorCtrl.Enable(self.borderCheck.GetValue())
+    
+    def updateRasterLegend(self):
+        """!Save information from raster legend dialog to dictionary"""
+
+        #is raster legend
+        if not self.isRLegend.GetValue():
+            self.rLegendDict['rLegend'] = False
+        else:
+            self.rLegendDict['rLegend'] = True
+        #units
+        currUnit = self.unitConv.findUnit(self.panelRaster.units['unitsCtrl'].GetStringSelection())
+        self.rLegendDict['unit'] = currUnit
+        # raster
+        if self.rasterDefault.GetValue():
+            self.rLegendDict['rasterDefault'] = True
+            self.rLegendDict['raster'] = self.currRaster
+        else:
+            self.rLegendDict['rasterDefault'] = False
+            self.rLegendDict['raster'] = self.rasterSelect.GetValue()
+        if self.rLegendDict['rLegend'] and not self.rLegendDict['raster']:
+            wx.MessageBox(message = _("No raster map selected!"),
+                                    caption = _('No raster'), style = wx.OK|wx.ICON_ERROR)
+            return False
+            
+        if self.rLegendDict['raster']:
+            # type and range of map
+            rasterType = getRasterType(self.rLegendDict['raster'])
+            if rasterType is None:
+                return False
+            self.rLegendDict['type'] = rasterType
+            
+            
+            #discrete
+            if self.discrete.GetValue():
+                self.rLegendDict['discrete'] = 'y'
+            else:
+                self.rLegendDict['discrete'] = 'n'   
+                    
+            # font 
+            self.rLegendDict['font'] = self.panelRaster.font['fontCtrl'].GetStringSelection()
+            self.rLegendDict['fontsize'] = self.panelRaster.font['fontSizeCtrl'].GetValue()
+            color = self.panelRaster.font['colorCtrl'].GetColour()
+            self.rLegendDict['color'] = convertRGB(color)
+
+            # position
+            x = self.unitConv.convert(value = float(self.panelRaster.position['xCtrl'].GetValue()), fromUnit = currUnit, toUnit = 'inch')
+            y = self.unitConv.convert(value = float(self.panelRaster.position['yCtrl'].GetValue()), fromUnit = currUnit, toUnit = 'inch')
+            self.rLegendDict['where'] = (x, y)
+            # estimated size
+            width = self.panelRaster.widthCtrl.GetValue()
+            try:
+                width = float(width)
+                width = self.unitConv.convert(value = width, fromUnit = currUnit, toUnit = 'inch')
+            except ValueError:
+                width = None
+            self.rLegendDict['width'] = width
+            if self.rLegendDict['discrete'] == 'n':
+                height = self.panelRaster.heightOrColumnsCtrl.GetValue()    
+                try:
+                    height = float(height)
+                    height = self.unitConv.convert(value = height, fromUnit = currUnit, toUnit = 'inch')
+                except ValueError:
+                    height = None
+                self.rLegendDict['height'] = height
+            else:
+                cols = self.panelRaster.heightOrColumnsCtrl.GetValue()
+                self.rLegendDict['cols'] = cols
+            drawHeight = self.rasterLegend.EstimateHeight(raster = self.rLegendDict['raster'], discrete = self.rLegendDict['discrete'],
+                                            fontsize = self.rLegendDict['fontsize'], cols = self.rLegendDict['cols'],
+                                            height = self.rLegendDict['height'])
+            drawWidth = self.rasterLegend.EstimateWidth(raster = self.rLegendDict['raster'], discrete = self.rLegendDict['discrete'],
+                                            fontsize = self.rLegendDict['fontsize'], cols = self.rLegendDict['cols'],
+                                            width = self.rLegendDict['width'], paperInstr = self.instruction[self.pageId])
+            self.rLegendDict['rect'] = Rect2D(x = x, y = y, width = drawWidth, height = drawHeight)
+                        
+            # no data
+            if self.rLegendDict['discrete'] == 'y':
+                if self.nodata.GetValue():
+                    self.rLegendDict['nodata'] = 'y'
+                else:
+                    self.rLegendDict['nodata'] = 'n'
+            # tickbar
+            elif self.rLegendDict['discrete'] == 'n':
+                if self.ticks.GetValue():
+                    self.rLegendDict['tickbar'] = 'y'
+                else:
+                    self.rLegendDict['tickbar'] = 'n'
+            # range
+                if self.range.GetValue():
+                    self.rLegendDict['range'] = True
+                    self.rLegendDict['min'] = self.min.GetValue()
+                    self.rLegendDict['max'] = self.max.GetValue()
+                else:
+                    self.rLegendDict['range'] = False
+         
+        if not self.id[0] in self.instruction:
+            rasterLegend = RasterLegend(self.id[0])
+            self.instruction.AddInstruction(rasterLegend)
+        self.instruction[self.id[0]].SetInstruction(self.rLegendDict)
+
+        if self.id[0] not in self.parent.objectId:
+            self.parent.objectId.append(self.id[0])
+        return True
+    
+    def updateVectorLegend(self):
+        """!Save information from vector legend dialog to dictionary"""
+
+        vector = self.instruction.FindInstructionByType('vector')
+        if vector:
+            self.vectorId = vector.id 
+        else:
+            self.vectorId = None
+
+        #is vector legend
+        if not self.isVLegend.GetValue():
+            self.vLegendDict['vLegend'] = False
+        else:
+            self.vLegendDict['vLegend'] = True   
+        if self.vLegendDict['vLegend'] == True and self.vectorId is not None:
+            # labels
+            #reindex order
+            idx = 1
+            for item in range(self.vectorListCtrl.GetItemCount()):
+                if self.vectorListCtrl.IsChecked(item):
+                    self.vectorListCtrl.SetItemData(item, idx)
+                    idx += 1
+                else:
+                    self.vectorListCtrl.SetItemData(item, 0)
+            if idx == 1: 
+                self.vLegendDict['vLegend'] = False     
+            else:
+                vList = self.instruction[self.vectorId]['list']
+                for i, vector in enumerate(vList):
+                    item = self.vectorListCtrl.FindItem(start = -1, str = vector[0].split('@')[0])
+                    vList[i][3] = self.vectorListCtrl.GetItemData(item)
+                    vList[i][4] = self.vectorListCtrl.GetItem(item, 1).GetText()
+                vmaps = self.instruction.FindInstructionByType('vProperties', list = True)
+                for vmap, vector in zip(vmaps, vList):
+                    self.instruction[vmap.id]['lpos'] = vector[3]
+                    self.instruction[vmap.id]['label'] = vector[4]
+                #units
+                currUnit = self.unitConv.findUnit(self.panelVector.units['unitsCtrl'].GetStringSelection())
+                self.vLegendDict['unit'] = currUnit
+                # position
+                x = self.unitConv.convert(value = float(self.panelVector.position['xCtrl'].GetValue()),
+                                                                fromUnit = currUnit, toUnit = 'inch')
+                y = self.unitConv.convert(value = float(self.panelVector.position['yCtrl'].GetValue()),
+                                                                fromUnit = currUnit, toUnit = 'inch')
+                self.vLegendDict['where'] = (x, y)
+                
+                # font 
+                self.vLegendDict['font'] = self.panelVector.font['fontCtrl'].GetStringSelection()
+                self.vLegendDict['fontsize'] = self.panelVector.font['fontSizeCtrl'].GetValue()
+                dc = wx.ClientDC(self)
+                font = dc.GetFont()
+                dc.SetFont(wx.Font(pointSize = self.vLegendDict['fontsize'], family = font.GetFamily(),
+                                   style = font.GetStyle(), weight = wx.FONTWEIGHT_NORMAL))
+                #size
+                width = self.unitConv.convert(value = float(self.panelVector.widthCtrl.GetValue()),
+                                              fromUnit = currUnit, toUnit = 'inch')
+                self.vLegendDict['width'] = width
+                self.vLegendDict['cols'] = self.panelVector.colsCtrl.GetValue()
+                if self.panelVector.spanRadio.GetValue() and self.panelVector.spanTextCtrl.GetValue():
+                    self.vLegendDict['span'] = self.panelVector.spanTextCtrl.GetValue()
+                else:
+                    self.vLegendDict['span'] = None
+                    
+                # size estimation
+                vectors = self.instruction[self.vectorId]['list']
+                labels = [vector[4] for vector in vectors if vector[3] != 0]
+                extent = dc.GetTextExtent(max(labels, key = len))
+                wExtent = self.unitConv.convert(value = extent[0], fromUnit = 'pixel', toUnit = 'inch')
+                hExtent = self.unitConv.convert(value = extent[1], fromUnit = 'pixel', toUnit = 'inch')
+                w = (width + wExtent) * self.vLegendDict['cols']
+                h = len(labels) * hExtent / self.vLegendDict['cols']
+                h *= 1.1
+                self.vLegendDict['rect'] = Rect2D(x, y, w, h)
+                
+                #border
+                if self.borderCheck.GetValue():
+                    color = self.borderColorCtrl.GetColour()
+                    self.vLegendDict['border'] = convertRGB(color)
+                    
+                else:
+                    self.vLegendDict['border'] = 'none'
+                                 
+        if not self.id[1] in self.instruction:
+            vectorLegend = VectorLegend(self.id[1])
+            self.instruction.AddInstruction(vectorLegend)
+        self.instruction[self.id[1]].SetInstruction(self.vLegendDict)
+        if self.id[1] not in self.parent.objectId:
+            self.parent.objectId.append(self.id[1])
+        return True
+    
+    def update(self):
+        okR = self.updateRasterLegend()
+        okV = self.updateVectorLegend()
+        if okR and okV:
+            return True
+        return False
+        
+    def updateDialog(self):
+        """!Update legend coordinates after moving"""
+        
+        # raster legend    
+        if 'rect' in self.rLegendDict:
+            x, y = self.rLegendDict['rect'][:2]
+            currUnit = self.unitConv.findUnit(self.panelRaster.units['unitsCtrl'].GetStringSelection())
+            x = self.unitConv.convert(value = x, fromUnit = 'inch', toUnit = currUnit)
+            y = self.unitConv.convert(value = y, fromUnit = 'inch', toUnit = currUnit)
+            self.panelRaster.position['xCtrl'].SetValue("%5.3f" % x)
+            self.panelRaster.position['yCtrl'].SetValue("%5.3f" % y)
+        #update name and type of raster
+        raster = self.instruction.FindInstructionByType('raster')
+        if raster:
+            self.rasterId = raster.id 
+        else:
+            self.rasterId = None 
+
+        if raster:
+            currRaster = raster['raster'] 
+        else:
+            currRaster = None
+            
+        rasterType = getRasterType(map = currRaster)
+        self.rasterCurrent.SetLabel(_("%(rast)s: type %(type)s") % \
+                                        { 'rast' : currRaster, 'type' : str(rasterType) })
+        
+        # vector legend       
+        if 'rect' in self.vLegendDict:
+            x, y = self.vLegendDict['rect'][:2]
+            currUnit = self.unitConv.findUnit(self.panelVector.units['unitsCtrl'].GetStringSelection())
+            x = self.unitConv.convert(value = x, fromUnit = 'inch', toUnit = currUnit)
+            y = self.unitConv.convert(value = y, fromUnit = 'inch', toUnit = currUnit)
+            self.panelVector.position['xCtrl'].SetValue("%5.3f" % x)
+            self.panelVector.position['yCtrl'].SetValue("%5.3f" % y)
+        # update vector maps
+        if self.instruction.FindInstructionByType('vector'):
+            vectors = sorted(self.instruction.FindInstructionByType('vector')['list'], key = lambda x: x[3])
+            self.vectorListCtrl.DeleteAllItems()
+            for vector in vectors:
+                index = self.vectorListCtrl.InsertStringItem(sys.maxint, vector[0].split('@')[0])
+                self.vectorListCtrl.SetStringItem(index, 1, vector[4])
+                self.vectorListCtrl.SetItemData(index, index)
+                self.vectorListCtrl.CheckItem(index, True)
+                if vector[3] == 0:
+                    self.vectorListCtrl.CheckItem(index, False)
+            self.panelVector.colsCtrl.SetRange(1, min(10, len(self.instruction.FindInstructionByType('vector')['list'])))
+            self.panelVector.colsCtrl.SetValue(1)
+        else:
+            self.vectorListCtrl.DeleteAllItems()
+            self.panelVector.colsCtrl.SetRange(0,0)
+            self.panelVector.colsCtrl.SetValue(0)
+             
+class MapinfoDialog(PsmapDialog):
+    def __init__(self, parent, id, settings):
+        PsmapDialog.__init__(self, parent = parent, id = id, title = _("Mapinfo settings"), settings = settings)
+        
+        self.objectType = ('mapinfo',)
+        if self.id is not None:
+            self.mapinfo = self.instruction[self.id]
+            self.mapinfoDict = self.mapinfo.GetInstruction()
+        else:
+            self.id = wx.NewId()
+            self.mapinfo = Mapinfo(self.id)
+            self.mapinfoDict = self.mapinfo.GetInstruction()
+            page = self.instruction.FindInstructionByType('page').GetInstruction()
+            self.mapinfoDict['where'] = page['Left'], page['Top']
+
+        self.panel = self._mapinfoPanel()
+        
+        self._layout(self.panel)
+        self.OnIsBackground(None)
+        self.OnIsBorder(None)
+
+    def _mapinfoPanel(self):
+        panel = wx.Panel(parent = self, id = wx.ID_ANY, size = (-1, -1), style = wx.TAB_TRAVERSAL)
+        #panel.SetupScrolling(scroll_x = False, scroll_y = True)
+        border = wx.BoxSizer(wx.VERTICAL)
+                
+        # position     
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Position"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridBagSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+        gridBagSizer.AddGrowableCol(1)
+        
+        self.AddPosition(parent = panel, dialogDict = self.mapinfoDict)
+        self.AddUnits(parent = panel, dialogDict = self.mapinfoDict)
+        gridBagSizer.Add(panel.units['unitsLabel'], pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.units['unitsCtrl'], pos = (0,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.position['xLabel'], pos = (1,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.position['xCtrl'], pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.position['yLabel'], pos = (2,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.position['yCtrl'], pos = (2,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.position['comment'], pos = (3,0), span = (1,2), flag =wx.ALIGN_BOTTOM, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        # font
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Font settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridBagSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+        gridBagSizer.AddGrowableCol(1)
+        
+        self.AddFont(parent = panel, dialogDict = self.mapinfoDict)#creates font color too, used below
+        
+        gridBagSizer.Add(panel.font['fontLabel'], pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.font['fontCtrl'], pos = (0,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.font['fontSizeLabel'], pos = (1,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.font['fontSizeCtrl'], pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.font['colorLabel'], pos = (2,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        gridBagSizer.Add(panel.font['colorCtrl'], pos = (2,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        sizer.Add(item = gridBagSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        # colors
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " %_("Color settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        flexSizer = wx.FlexGridSizer (cols = 2, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(1)
+        
+        self.colors = {}
+        self.colors['borderCtrl'] = wx.CheckBox(panel, id = wx.ID_ANY, label = _("use border color:"))
+        self.colors['backgroundCtrl'] = wx.CheckBox(panel, id = wx.ID_ANY, label = _("use background color:"))
+        self.colors['borderColor'] = wx.ColourPickerCtrl(panel, id = wx.ID_ANY)
+        self.colors['backgroundColor'] = wx.ColourPickerCtrl(panel, id = wx.ID_ANY)
+        
+        if self.mapinfoDict['border'] == None:
+            self.mapinfoDict['border'] = 'none'
+        if self.mapinfoDict['border'] != 'none':
+            self.colors['borderCtrl'].SetValue(True) 
+            self.colors['borderColor'].SetColour(convertRGB(self.mapinfoDict['border']))
+        else:
+            self.colors['borderCtrl'].SetValue(False)
+            self.colors['borderColor'].SetColour(convertRGB('black'))
+            
+        if self.mapinfoDict['background'] == None:
+            self.mapinfoDict['background'] == 'none'
+        if self.mapinfoDict['background'] != 'none':
+            self.colors['backgroundCtrl'].SetValue(True) 
+            self.colors['backgroundColor'].SetColour(convertRGB(self.mapinfoDict['background']))
+        else:
+            self.colors['backgroundCtrl'].SetValue(False)
+            self.colors['backgroundColor'].SetColour(convertRGB('white'))
+                    
+        flexSizer.Add(self.colors['borderCtrl'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        flexSizer.Add(self.colors['borderColor'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        flexSizer.Add(self.colors['backgroundCtrl'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        flexSizer.Add(self.colors['backgroundColor'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        panel.SetSizer(border)
+        
+        self.Bind(wx.EVT_CHECKBOX, self.OnIsBorder, self.colors['borderCtrl'])
+        self.Bind(wx.EVT_CHECKBOX, self.OnIsBackground, self.colors['backgroundCtrl'])
+        
+        return panel
+    
+    def OnIsBackground(self, event):
+        if self.colors['backgroundCtrl'].GetValue():
+            self.colors['backgroundColor'].Enable()
+            self.update()
+        else:
+            self.colors['backgroundColor'].Disable()
+                        
+    def OnIsBorder(self, event):
+        if self.colors['borderCtrl'].GetValue():
+            self.colors['borderColor'].Enable()
+            self.update()
+        else:
+            self.colors['borderColor'].Disable() 
+                                           
+    def update(self):
+
+        #units
+        currUnit = self.unitConv.findUnit(self.panel.units['unitsCtrl'].GetStringSelection())
+        self.mapinfoDict['unit'] = currUnit
+        
+        # position
+        if self.panel.position['xCtrl'].GetValue():
+            x = self.panel.position['xCtrl'].GetValue() 
+        else:
+            x = self.mapinfoDict['where'][0]
+
+        if self.panel.position['yCtrl'].GetValue():
+            y = self.panel.position['yCtrl'].GetValue() 
+        else:
+            y = self.mapinfoDict['where'][1]
+
+        x = self.unitConv.convert(value = float(self.panel.position['xCtrl'].GetValue()), fromUnit = currUnit, toUnit = 'inch')
+        y = self.unitConv.convert(value = float(self.panel.position['yCtrl'].GetValue()), fromUnit = currUnit, toUnit = 'inch')
+        self.mapinfoDict['where'] = (x, y)
+        
+        # font
+        self.mapinfoDict['font'] =  self.panel.font['fontCtrl'].GetStringSelection()
+        self.mapinfoDict['fontsize'] = self.panel.font['fontSizeCtrl'].GetValue()
+
+        #colors
+        color = self.panel.font['colorCtrl'].GetColour()
+        self.mapinfoDict['color'] = convertRGB(color)
+        
+        if self.colors['backgroundCtrl'].GetValue():    
+            background = self.colors['backgroundColor'].GetColour()
+            self.mapinfoDict['background'] = convertRGB(background)
+        else:
+            self.mapinfoDict['background'] = 'none'
+
+        if self.colors['borderCtrl'].GetValue():    
+            border = self.colors['borderColor'].GetColour()
+            self.mapinfoDict['border'] = convertRGB(border)
+        else:
+            self.mapinfoDict['border'] = 'none'
+        
+        # estimation of size
+        self.mapinfoDict['rect'] = self.mapinfo.EstimateRect(self.mapinfoDict)
+
+        if self.id not in self.instruction:
+            mapinfo = Mapinfo(self.id)
+            self.instruction.AddInstruction(mapinfo)
+            
+        self.instruction[self.id].SetInstruction(self.mapinfoDict)
+
+        if self.id not in self.parent.objectId:
+            self.parent.objectId.append(self.id)
+            
+        self.updateDialog()
+
+        return True
+    
+    def updateDialog(self):
+        """!Update mapinfo coordinates, after moving"""
+        x, y = self.mapinfoDict['where']
+        currUnit = self.unitConv.findUnit(self.panel.units['unitsCtrl'].GetStringSelection())
+        x = self.unitConv.convert(value = x, fromUnit = 'inch', toUnit = currUnit)
+        y = self.unitConv.convert(value = y, fromUnit = 'inch', toUnit = currUnit)
+        self.panel.position['xCtrl'].SetValue("%5.3f" % x)
+        self.panel.position['yCtrl'].SetValue("%5.3f" % y)
+             
+class ScalebarDialog(PsmapDialog):
+    """!Dialog for scale bar"""
+    def __init__(self, parent, id, settings):
+        PsmapDialog.__init__(self, parent = parent, id = id, title = "Scale bar settings", settings = settings)
+        self.objectType = ('scalebar',)
+        if self.id is not None:
+            self.scalebar = self.instruction[id]
+            self.scalebarDict = self.scalebar.GetInstruction()
+        else:
+            self.id = wx.NewId()
+            self.scalebar = Scalebar(self.id)
+            self.scalebarDict = self.scalebar.GetInstruction()
+            page = self.instruction.FindInstructionByType('page').GetInstruction()
+            self.scalebarDict['where'] = page['Left'], page['Top']
+
+        self.panel = self._scalebarPanel()
+        
+        self._layout(self.panel)
+        
+        self.mapUnit = projInfo()['units'].lower()
+        if projInfo()['proj'] == 'xy':
+            self.mapUnit = 'meters'
+        if self.mapUnit not in self.unitConv.getAllUnits():
+            wx.MessageBox(message = _("Units of current projection are not supported,\n meters will be used!"),
+                            caption = _('Unsupported units'),
+                                    style = wx.OK|wx.ICON_ERROR)
+            self.mapUnit = 'meters'
+            
+    def _scalebarPanel(self):
+        panel = wx.Panel(parent = self, id = wx.ID_ANY, style = wx.TAB_TRAVERSAL)
+        border = wx.BoxSizer(wx.VERTICAL)
+        #        
+        # position
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Position"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridBagSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+        gridBagSizer.AddGrowableCol(1)
+        
+        self.AddUnits(parent = panel, dialogDict = self.scalebarDict)
+        self.AddPosition(parent = panel, dialogDict = self.scalebarDict)
+        
+        if self.scalebarDict['rect']: # set position, ref point is center and not left top corner
+            
+            x = self.unitConv.convert(value = self.scalebarDict['where'][0] - self.scalebarDict['rect'].Get()[2]/2,
+                                                    fromUnit = 'inch', toUnit = self.scalebarDict['unit'])
+            y = self.unitConv.convert(value = self.scalebarDict['where'][1] - self.scalebarDict['rect'].Get()[3]/2,
+                                                    fromUnit = 'inch', toUnit = self.scalebarDict['unit'])
+            panel.position['xCtrl'].SetValue("%5.3f" % x)
+            panel.position['yCtrl'].SetValue("%5.3f" % y)
+        
+        gridBagSizer.Add(panel.units['unitsLabel'], pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.units['unitsCtrl'], pos = (0,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.position['xLabel'], pos = (1,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.position['xCtrl'], pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.position['yLabel'], pos = (2,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.position['yCtrl'], pos = (2,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(panel.position['comment'], pos = (3,0), span = (1,2), flag =wx.ALIGN_BOTTOM, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        # size
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Size"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridBagSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+        gridBagSizer.AddGrowableCol(1)
+        
+        lengthText = wx.StaticText(panel, id = wx.ID_ANY, label = _("Length:"))
+        heightText = wx.StaticText(panel, id = wx.ID_ANY, label = _("Height:"))
+        
+        self.lengthTextCtrl = wx.TextCtrl(panel, id = wx.ID_ANY, validator = TCValidator('DIGIT_ONLY'))
+        self.lengthTextCtrl.SetToolTipString(_("Scalebar length is given in map units"))
+        
+        self.heightTextCtrl = wx.TextCtrl(panel, id = wx.ID_ANY, validator = TCValidator('DIGIT_ONLY'))
+        self.heightTextCtrl.SetToolTipString(_("Scalebar height is real height on paper"))
+        
+        choices = [_('default')] + self.unitConv.getMapUnitsNames()
+        self.unitsLength = wx.Choice(panel, id = wx.ID_ANY, choices = choices)
+        choices = self.unitConv.getPageUnitsNames()
+        self.unitsHeight = wx.Choice(panel, id = wx.ID_ANY, choices = choices)
+        
+        # set values
+        unitName = self.unitConv.findName(self.scalebarDict['unitsLength'])
+        if unitName:
+            self.unitsLength.SetStringSelection(unitName)
+        else:
+            if self.scalebarDict['unitsLength'] == 'auto':
+                 self.unitsLength.SetSelection(0)
+            elif self.scalebarDict['unitsLength'] == 'nautmiles':
+                 self.unitsLength.SetStringSelection(self.unitConv.findName("nautical miles"))
+        self.unitsHeight.SetStringSelection(self.unitConv.findName(self.scalebarDict['unitsHeight']))
+        if self.scalebarDict['length']:
+            self.lengthTextCtrl.SetValue(str(self.scalebarDict['length']))
+        else: #estimate default
+            reg = grass.region()
+            w = int((reg['e'] - reg['w'])/3)
+            w = round(w, -len(str(w)) + 2) #12345 -> 12000
+            self.lengthTextCtrl.SetValue(str(w))
+            
+        h = self.unitConv.convert(value = self.scalebarDict['height'], fromUnit = 'inch',
+                                                toUnit =  self.scalebarDict['unitsHeight']) 
+        self.heightTextCtrl.SetValue(str(h))
+        
+        gridBagSizer.Add(lengthText, pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.lengthTextCtrl, pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.unitsLength, pos = (0, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        gridBagSizer.Add(heightText, pos = (1,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.heightTextCtrl, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.unitsHeight, pos = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+      
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        #
+        #style
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Style"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridBagSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+        
+        
+        sbTypeText = wx.StaticText(panel, id = wx.ID_ANY, label = _("Type:"))
+        self.sbCombo = wx.combo.BitmapComboBox(panel, style = wx.CB_READONLY)
+        # only temporary, images must be moved away
+        imagePath = os.path.join(globalvar.ETCIMGDIR, "scalebar-fancy.png"), os.path.join(globalvar.ETCIMGDIR, "scalebar-simple.png") 
+        for item, path in zip(['fancy', 'simple'], imagePath):
+            if not os.path.exists(path):
+                bitmap = wx.EmptyBitmap(0,0)
+            else:
+                bitmap = wx.Bitmap(path)
+            self.sbCombo.Append(item = '', bitmap = bitmap, clientData = item[0])
+        #self.sbCombo.Append(item = 'simple', bitmap = wx.Bitmap("./images/scalebar-simple.png"), clientData = 's')
+        if self.scalebarDict['scalebar'] == 'f':
+            self.sbCombo.SetSelection(0)
+        elif self.scalebarDict['scalebar'] == 's':
+            self.sbCombo.SetSelection(1)
+            
+        sbSegmentsText = wx.StaticText(panel, id = wx.ID_ANY, label = _("Number of segments:"))
+        self.sbSegmentsCtrl = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 30, initial = 4)
+        self.sbSegmentsCtrl.SetValue(self.scalebarDict['segment'])
+        
+        sbLabelsText1 = wx.StaticText(panel, id = wx.ID_ANY, label = _("Label every "))
+        sbLabelsText2 = wx.StaticText(panel, id = wx.ID_ANY, label = _("segments"))
+        self.sbLabelsCtrl = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 30, initial = 1)
+        self.sbLabelsCtrl.SetValue(self.scalebarDict['numbers'])
+        
+        #font
+        fontsizeText = wx.StaticText(panel, id = wx.ID_ANY, label = _("Font size:"))
+        self.fontsizeCtrl = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 4, max = 30, initial = 10)
+        self.fontsizeCtrl.SetValue(self.scalebarDict['fontsize'])
+        
+        self.backgroundCheck = wx.CheckBox(panel, id = wx.ID_ANY, label = _("transparent text background"))
+        if self.scalebarDict['background'] == 'y':
+            self.backgroundCheck.SetValue(False)
+        else:
+            self.backgroundCheck.SetValue(True)
+
+        gridBagSizer.Add(sbTypeText, pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.sbCombo, pos = (0,1), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border = 0)
+        gridBagSizer.Add(sbSegmentsText, pos = (1,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.sbSegmentsCtrl, pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(sbLabelsText1, pos = (2,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.sbLabelsCtrl, pos = (2,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(sbLabelsText2, pos = (2,2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(fontsizeText, pos = (3,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.fontsizeCtrl, pos = (3,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.backgroundCheck, pos = (4, 0), span = (1,3), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.ALIGN_CENTER_VERTICAL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        panel.SetSizer(border)
+        
+        return panel
+                           
+    def update(self):
+        """!Save information from dialog"""
+
+        #units
+        currUnit = self.unitConv.findUnit(self.panel.units['unitsCtrl'].GetStringSelection())
+        self.scalebarDict['unit'] = currUnit
+        # position
+        if self.panel.position['xCtrl'].GetValue():
+            x = self.panel.position['xCtrl'].GetValue() 
+        else:
+            x = self.scalebarDict['where'][0]
+
+        if self.panel.position['yCtrl'].GetValue():
+            y = self.panel.position['yCtrl'].GetValue() 
+        else:
+            y = self.scalebarDict['where'][1]
+
+        x = self.unitConv.convert(value = float(self.panel.position['xCtrl'].GetValue()), fromUnit = currUnit, toUnit = 'inch')
+        y = self.unitConv.convert(value = float(self.panel.position['yCtrl'].GetValue()), fromUnit = currUnit, toUnit = 'inch')
+        
+        #style
+        self.scalebarDict['scalebar'] = self.sbCombo.GetClientData(self.sbCombo.GetSelection())
+        self.scalebarDict['segment'] = self.sbSegmentsCtrl.GetValue()
+        self.scalebarDict['numbers'] = self.sbLabelsCtrl.GetValue()
+        self.scalebarDict['fontsize'] = self.fontsizeCtrl.GetValue()
+        if self.backgroundCheck.GetValue():
+            self.scalebarDict['background'] = 'n' 
+        else:
+            self.scalebarDict['background'] = 'y'
+
+        
+        # size
+        
+        # height
+        self.scalebarDict['unitsHeight'] = self.unitConv.findUnit(self.unitsHeight.GetStringSelection())
+        try:
+            height = float(self.heightTextCtrl.GetValue())  
+            height = self.unitConv.convert(value = height, fromUnit = self.scalebarDict['unitsHeight'], toUnit = 'inch') 
+        except (ValueError, SyntaxError):
+            height = 0.1 #default in inch
+        self.scalebarDict['height'] = height    
+        
+        #length
+        if self.unitsLength.GetSelection() == 0:
+            selected = 'auto'
+        else:
+            selected = self.unitConv.findUnit(self.unitsLength.GetStringSelection())
+            if selected == 'nautical miles':
+                selected = 'nautmiles'
+        self.scalebarDict['unitsLength'] = selected
+        try:
+            length = float(self.lengthTextCtrl.GetValue())
+        except (ValueError, SyntaxError):
+            wx.MessageBox(message = _("Length of scale bar is not defined"),
+                                    caption = _('Invalid input'), style = wx.OK|wx.ICON_ERROR)
+            return False
+        self.scalebarDict['length'] = length
+            
+        # estimation of size
+        map = self.instruction.FindInstructionByType('map')
+        if not map:
+            map = self.instruction.FindInstructionByType('initMap')
+        mapId = map.id
+         
+        rectSize = self.scalebar.EstimateSize(scalebarDict = self.scalebarDict,
+                                                                scale = self.instruction[mapId]['scale'])
+        self.scalebarDict['rect'] = Rect2D(x = x, y = y, width = rectSize[0], height = rectSize[1])
+        self.scalebarDict['where'] = self.scalebarDict['rect'].GetCentre() 
+
+        if self.id not in self.instruction:
+            scalebar = Scalebar(self.id)
+            self.instruction.AddInstruction(scalebar)
+        self.instruction[self.id].SetInstruction(self.scalebarDict)
+        if self.id not in self.parent.objectId:
+            self.parent.objectId.append(self.id)
+            
+        return True
+    
+    def updateDialog(self):
+        """!Update scalebar coordinates, after moving"""
+        x, y = self.scalebarDict['rect'][:2]
+        currUnit = self.unitConv.findUnit(self.panel.units['unitsCtrl'].GetStringSelection())
+        x = self.unitConv.convert(value = x, fromUnit = 'inch', toUnit = currUnit)
+        y = self.unitConv.convert(value = y, fromUnit = 'inch', toUnit = currUnit)
+        self.panel.position['xCtrl'].SetValue("%5.3f" % x)
+        self.panel.position['yCtrl'].SetValue("%5.3f" % y)
+        
+ 
+        
+class TextDialog(PsmapDialog):
+    def __init__(self, parent, id, settings):
+        PsmapDialog.__init__(self, parent = parent, id = id, title = "Text settings", settings = settings)
+        self.objectType = ('text',)
+        if self.id is not None:
+            self.textDict = self.instruction[id].GetInstruction()
+        else:
+            self.id = wx.NewId()  
+            text = Text(self.id)
+            self.textDict = text.GetInstruction()
+            page = self.instruction.FindInstructionByType('page').GetInstruction()
+            self.textDict['where'] = page['Left'], page['Top'] 
+                
+        map = self.instruction.FindInstructionByType('map')
+        if not map:
+            map = self.instruction.FindInstructionByType('initMap')
+        self.mapId = map.id
+
+        self.textDict['east'], self.textDict['north'] = PaperMapCoordinates(mapInstr = map, x = self.textDict['where'][0], y = self.textDict['where'][1], paperToMap = True)
+        
+        notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)     
+        self.textPanel = self._textPanel(notebook)
+        self.positionPanel = self._positionPanel(notebook)
+        self.OnBackground(None)
+        self.OnHighlight(None)
+        self.OnBorder(None)
+        self.OnPositionType(None)
+        self.OnRotation(None)
+     
+        self._layout(notebook)
+
+    def _textPanel(self, notebook):
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY, style = wx.TAB_TRAVERSAL)
+        notebook.AddPage(page = panel, text = _("Text"))
+        
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        # text entry    
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Text"))
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        
+        textLabel = wx.StaticText(panel, id = wx.ID_ANY, label = _("Enter text:"))
+        self.textCtrl = ExpandoTextCtrl(panel, id = wx.ID_ANY, value = self.textDict['text'])
+        
+        sizer.Add(textLabel, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+        sizer.Add(self.textCtrl, proportion = 1, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)        
+        
+        #font       
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Font settings"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        flexGridSizer = wx.FlexGridSizer (rows = 3, cols = 2, hgap = 5, vgap = 5)
+        flexGridSizer.AddGrowableCol(1)
+        
+        self.AddFont(parent = panel, dialogDict = self.textDict)
+        
+        flexGridSizer.Add(panel.font['fontLabel'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        flexGridSizer.Add(panel.font['fontCtrl'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        flexGridSizer.Add(panel.font['fontSizeLabel'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        flexGridSizer.Add(panel.font['fontSizeCtrl'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        flexGridSizer.Add(panel.font['colorLabel'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)        
+        flexGridSizer.Add(panel.font['colorCtrl'], proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        sizer.Add(item = flexGridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        #text effects        
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Text effects"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridBagSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+        
+        self.effect = {}
+        self.effect['backgroundCtrl'] = wx.CheckBox(panel, id = wx.ID_ANY, label = _("text background"))
+        self.effect['backgroundColor'] = wx.ColourPickerCtrl(panel, id = wx.ID_ANY)
+        
+        self.effect['highlightCtrl'] = wx.CheckBox(panel, id = wx.ID_ANY, label = _("highlight"))
+        self.effect['highlightColor'] = wx.ColourPickerCtrl(panel, id = wx.ID_ANY)
+        self.effect['highlightWidth'] = wx.SpinCtrl(panel, id = wx.ID_ANY, size = self.spinCtrlSize, min = 0, max = 5, initial = 1)
+        self.effect['highlightWidthLabel'] = wx.StaticText(panel, id = wx.ID_ANY, label = _("Width (pts):"))
+        
+        self.effect['borderCtrl'] = wx.CheckBox(panel, id = wx.ID_ANY, label = _("text border"))
+        self.effect['borderColor'] = wx.ColourPickerCtrl(panel, id = wx.ID_ANY)
+        self.effect['borderWidth'] = wx.SpinCtrl(panel, id = wx.ID_ANY, size = self.spinCtrlSize, min = 1, max = 25, initial = 1)
+        self.effect['borderWidthLabel'] = wx.StaticText(panel, id = wx.ID_ANY, label = _("Width (pts):"))
+
+        #set values
+        if self.textDict['background'] == None:
+            self.textDict['background'] = 'none'
+        if self.textDict['background'] != 'none':
+            self.effect['backgroundCtrl'].SetValue(True) 
+            self.effect['backgroundColor'].SetColour(convertRGB(self.textDict['background']))
+        else:
+            self.effect['backgroundCtrl'].SetValue(False)
+            self.effect['backgroundColor'].SetColour(convertRGB('white'))
+
+        if self.textDict['hcolor'] == None:
+             self.textDict['hcolor'] = 'none'
+        if self.textDict['hcolor'] != 'none':
+            self.effect['highlightCtrl'].SetValue(True) 
+            self.effect['highlightColor'].SetColour(convertRGB(self.textDict['hcolor']))
+        else:
+            self.effect['highlightCtrl'].SetValue(False)
+            self.effect['highlightColor'].SetColour(convertRGB('grey'))
+
+        self.effect['highlightWidth'].SetValue(float(self.textDict['hwidth']))
+        
+        if self.textDict['border'] == None:
+            self.textDict['border'] = 'none'
+        if self.textDict['border'] != 'none':
+            self.effect['borderCtrl'].SetValue(True) 
+            self.effect['borderColor'].SetColour(convertRGB(self.textDict['border'])) 
+        else:
+            self.effect['borderCtrl'].SetValue(False)
+            self.effect['borderColor'].SetColour(convertRGB('black'))
+
+        self.effect['borderWidth'].SetValue(float(self.textDict['width']))
+        
+        gridBagSizer.Add(self.effect['backgroundCtrl'], pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.effect['backgroundColor'], pos = (0,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.effect['highlightCtrl'], pos = (1,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.effect['highlightColor'], pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.effect['highlightWidthLabel'], pos = (1,2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.effect['highlightWidth'], pos = (1,3), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.effect['borderCtrl'], pos = (2,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.effect['borderColor'], pos = (2,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.effect['borderWidthLabel'], pos = (2,2), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizer.Add(self.effect['borderWidth'], pos = (2,3), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        sizer.Add(item = gridBagSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        self.Bind(EVT_ETC_LAYOUT_NEEDED, self.OnRefit, self.textCtrl)
+        self.Bind(wx.EVT_CHECKBOX, self.OnBackground, self.effect['backgroundCtrl'])
+        self.Bind(wx.EVT_CHECKBOX, self.OnHighlight, self.effect['highlightCtrl'])
+        self.Bind(wx.EVT_CHECKBOX, self.OnBorder, self.effect['borderCtrl'])
+        
+        panel.SetSizer(border)
+        panel.Fit()
+        
+        return panel 
+        
+    def _positionPanel(self, notebook):
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY, style = wx.TAB_TRAVERSAL)
+        notebook.AddPage(page = panel, text = _("Position"))
+
+        border = wx.BoxSizer(wx.VERTICAL) 
+
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Position"))
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        gridBagSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        gridBagSizer.AddGrowableCol(0)
+        gridBagSizer.AddGrowableCol(1)
+        
+        #Position
+        self.AddExtendedPosition(panel, gridBagSizer, self.textDict)
+        
+        #offset
+        box3   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " %_("Offset"))
+        sizerO = wx.StaticBoxSizer(box3, wx.VERTICAL)
+        gridBagSizerO = wx.GridBagSizer (hgap = 5, vgap = 5)
+        self.xoffLabel = wx.StaticText(panel, id = wx.ID_ANY, label = _("horizontal (pts):"))
+        self.yoffLabel = wx.StaticText(panel, id = wx.ID_ANY, label = _("vertical (pts):"))
+        self.xoffCtrl = wx.SpinCtrl(panel, id = wx.ID_ANY, size = (50, -1), min = -50, max = 50, initial = 0)
+        self.yoffCtrl = wx.SpinCtrl(panel, id = wx.ID_ANY, size = (50, -1), min = -50, max = 50, initial = 0) 
+        self.xoffCtrl.SetValue(self.textDict['xoffset'])       
+        self.yoffCtrl.SetValue(self.textDict['yoffset'])
+        gridBagSizerO.Add(self.xoffLabel, pos = (0,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizerO.Add(self.yoffLabel, pos = (1,0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizerO.Add(self.xoffCtrl, pos = (0,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridBagSizerO.Add(self.yoffCtrl, pos = (1,1), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        
+        sizerO.Add(gridBagSizerO, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        gridBagSizer.Add(sizerO, pos = (3,0), flag = wx.ALIGN_CENTER_HORIZONTAL|wx.EXPAND, border = 0)
+        # reference point
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " %_(" Reference point"))
+        sizerR = wx.StaticBoxSizer(box, wx.VERTICAL)
+        flexSizer = wx.FlexGridSizer(rows = 3, cols = 3, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(0)
+        flexSizer.AddGrowableCol(1)
+        flexSizer.AddGrowableCol(2)
+        ref = []
+        for row in ["upper", "center", "lower"]:
+            for col in ["left", "center", "right"]:
+                ref.append(row + " " + col)
+        self.radio = [wx.RadioButton(panel, id = wx.ID_ANY, label = '', style = wx.RB_GROUP, name = ref[0])]
+        self.radio[0].SetValue(False)
+        flexSizer.Add(self.radio[0], proportion = 0, flag = wx.ALIGN_CENTER, border = 0)
+        for i in range(1,9):
+            self.radio.append(wx.RadioButton(panel, id = wx.ID_ANY, label = '', name = ref[i]))
+            self.radio[-1].SetValue(False)
+            flexSizer.Add(self.radio[-1], proportion = 0, flag = wx.ALIGN_CENTER, border = 0)
+        self.FindWindowByName(self.textDict['ref']).SetValue(True)
+                
+        sizerR.Add(flexSizer, proportion = 1, flag = wx.EXPAND, border = 0)
+        gridBagSizer.Add(sizerR, pos = (3,1), flag = wx.ALIGN_LEFT|wx.EXPAND, border = 0)
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+                
+        #rotation
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Text rotation"))
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+
+        self.rotCtrl = wx.CheckBox(panel, id = wx.ID_ANY, label = _("rotate text (counterclockwise)"))
+        self.rotValue = wx.SpinCtrl(panel, wx.ID_ANY, size = (50, -1), min = 0, max = 360, initial = 0)
+        if self.textDict['rotate']:
+            self.rotValue.SetValue(int(self.textDict['rotate']))
+            self.rotCtrl.SetValue(True)
+        else:
+            self.rotValue.SetValue(0)
+            self.rotCtrl.SetValue(False)
+        sizer.Add(self.rotCtrl, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.ALL, border = 5)
+        sizer.Add(self.rotValue, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.ALL, border = 5)
+        
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        panel.SetSizer(border)
+        panel.Fit()
+          
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnPositionType, panel.position['toPaper']) 
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnPositionType, panel.position['toMap'])
+        self.Bind(wx.EVT_CHECKBOX, self.OnRotation, self.rotCtrl)
+        
+        return panel
+     
+    def OnRefit(self, event):
+        self.Fit()
+        
+    def OnRotation(self, event):
+        if self.rotCtrl.GetValue():
+            self.rotValue.Enable()
+        else: 
+            self.rotValue.Disable()
+            
+    def OnPositionType(self, event):
+        if self.positionPanel.position['toPaper'].GetValue():
+            for widget in self.gridBagSizerP.GetChildren():
+                widget.GetWindow().Enable()
+            for widget in self.gridBagSizerM.GetChildren():
+                widget.GetWindow().Disable()
+        else:
+            for widget in self.gridBagSizerM.GetChildren():
+                widget.GetWindow().Enable()
+            for widget in self.gridBagSizerP.GetChildren():
+                widget.GetWindow().Disable()
+                
+    def OnBackground(self, event):
+        if self.effect['backgroundCtrl'].GetValue():
+            self.effect['backgroundColor'].Enable()
+            self.update()
+        else:
+            self.effect['backgroundColor'].Disable()
+    
+    def OnHighlight(self, event):
+        if self.effect['highlightCtrl'].GetValue():
+            self.effect['highlightColor'].Enable()
+            self.effect['highlightWidth'].Enable()
+            self.effect['highlightWidthLabel'].Enable()
+            self.update()
+        else:
+            self.effect['highlightColor'].Disable()
+            self.effect['highlightWidth'].Disable()
+            self.effect['highlightWidthLabel'].Disable()
+            
+    def OnBorder(self, event):
+        if self.effect['borderCtrl'].GetValue():
+            self.effect['borderColor'].Enable()
+            self.effect['borderWidth'].Enable()
+            self.effect['borderWidthLabel'].Enable()
+            self.update()
+        else:
+            self.effect['borderColor'].Disable()
+            self.effect['borderWidth'].Disable()
+            self.effect['borderWidthLabel'].Disable()
+            
+    def update(self): 
+        #text
+        self.textDict['text'] = self.textCtrl.GetValue()
+        if not self.textDict['text']:
+            wx.MessageBox(_("No text entered!"), _("Error"))
+            return False
+            
+        #font
+        self.textDict['font'] = self.textPanel.font['fontCtrl'].GetStringSelection()
+        self.textDict['fontsize'] = self.textPanel.font['fontSizeCtrl'].GetValue()
+        color = self.textPanel.font['colorCtrl'].GetColour()
+        self.textDict['color'] = convertRGB(color)
+
+        #effects
+        if self.effect['backgroundCtrl'].GetValue():
+            background = self.effect['backgroundColor'].GetColour()
+            self.textDict['background'] = convertRGB(background)
+        else:
+            self.textDict['background'] = 'none'        
+                
+        if self.effect['borderCtrl'].GetValue():
+            border = self.effect['borderColor'].GetColour()
+            self.textDict['border'] = convertRGB(border)
+        else:
+            self.textDict['border'] = 'none' 
+                     
+        self.textDict['width'] = self.effect['borderWidth'].GetValue()
+        
+        if self.effect['highlightCtrl'].GetValue():
+            highlight = self.effect['highlightColor'].GetColour()
+            self.textDict['hcolor'] = convertRGB(highlight)
+        else:
+            self.textDict['hcolor'] = 'none'
+
+        self.textDict['hwidth'] = self.effect['highlightWidth'].GetValue()
+        
+        #offset
+        self.textDict['xoffset'] = self.xoffCtrl.GetValue()
+        self.textDict['yoffset'] = self.yoffCtrl.GetValue()
+
+        #position
+        if self.positionPanel.position['toPaper'].GetValue():
+            self.textDict['XY'] = True
+            currUnit = self.unitConv.findUnit(self.positionPanel.units['unitsCtrl'].GetStringSelection())
+            self.textDict['unit'] = currUnit
+            if self.positionPanel.position['xCtrl'].GetValue():
+                x = self.positionPanel.position['xCtrl'].GetValue() 
+            else:
+                x = self.textDict['where'][0]
+
+            if self.positionPanel.position['yCtrl'].GetValue():
+                y = self.positionPanel.position['yCtrl'].GetValue() 
+            else:
+                y = self.textDict['where'][1]
+
+            x = self.unitConv.convert(value = float(x), fromUnit = currUnit, toUnit = 'inch')
+            y = self.unitConv.convert(value = float(y), fromUnit = currUnit, toUnit = 'inch')
+            self.textDict['where'] = x, y
+            self.textDict['east'], self.textDict['north'] = PaperMapCoordinates(self.instruction[self.mapId], x, y, paperToMap = True)
+        else:
+            self.textDict['XY'] = False
+            if self.positionPanel.position['eCtrl'].GetValue():
+                self.textDict['east'] = self.positionPanel.position['eCtrl'].GetValue() 
+            else:
+                self.textDict['east'] = self.textDict['east']
+
+            if self.positionPanel.position['nCtrl'].GetValue():
+                self.textDict['north'] = self.positionPanel.position['nCtrl'].GetValue() 
+            else:
+                self.textDict['north'] = self.textDict['north']
+
+            self.textDict['where'] = PaperMapCoordinates(mapInstr = self.instruction[self.mapId], x = float(self.textDict['east']),
+                                                            y = float(self.textDict['north']), paperToMap = False)
+        #rotation
+        if self.rotCtrl.GetValue():
+            self.textDict['rotate'] = self.rotValue.GetValue()
+        else:
+            self.textDict['rotate'] = None
+        #reference point
+        for radio in self.radio:
+            if radio.GetValue() == True:
+                self.textDict['ref'] = radio.GetName()
+                
+        if self.id not in self.instruction:
+            text = Text(self.id)
+            self.instruction.AddInstruction(text)
+        self.instruction[self.id].SetInstruction(self.textDict)
+        
+        if self.id not in self.parent.objectId:
+            self.parent.objectId.append(self.id)
+
+#        self.updateDialog()
+
+        return True
+    
+    def updateDialog(self):
+        """!Update text coordinates, after moving"""
+        # XY coordinates
+        x, y = self.textDict['where'][:2]
+        currUnit = self.unitConv.findUnit(self.positionPanel.units['unitsCtrl'].GetStringSelection())
+        x = self.unitConv.convert(value = x, fromUnit = 'inch', toUnit = currUnit)
+        y = self.unitConv.convert(value = y, fromUnit = 'inch', toUnit = currUnit)
+        self.positionPanel.position['xCtrl'].SetValue("%5.3f" % x)
+        self.positionPanel.position['yCtrl'].SetValue("%5.3f" % y)
+        # EN coordinates
+        e, n = self.textDict['east'], self.textDict['north']
+        self.positionPanel.position['eCtrl'].SetValue(str(self.textDict['east']))
+        self.positionPanel.position['nCtrl'].SetValue(str(self.textDict['north']))
+        
+class ImageDialog(PsmapDialog):
+    """!Dialog for setting image properties.
+    
+    It's base dialog for North Arrow dialog.
+    """
+    def __init__(self, parent, id, settings, imagePanelName = _("Image")):
+        PsmapDialog.__init__(self, parent = parent, id = id, title = "Image settings",
+                             settings = settings)
+        
+        self.objectType = ('image',)
+        if self.id is not None:
+            self.imageObj = self.instruction[self.id]
+            self.imageDict = self.instruction[id].GetInstruction()
+        else:
+            self.id = wx.NewId()
+            self.imageObj = self._newObject()
+            self.imageDict = self.imageObj.GetInstruction()
+            page = self.instruction.FindInstructionByType('page').GetInstruction()
+            self.imageDict['where'] = page['Left'], page['Top'] 
+                
+        map = self.instruction.FindInstructionByType('map')
+        if not map:
+            map = self.instruction.FindInstructionByType('initMap')
+        self.mapId = map.id
+
+        self.imageDict['east'], self.imageDict['north'] = PaperMapCoordinates(mapInstr = map, x = self.imageDict['where'][0], y = self.imageDict['where'][1], paperToMap = True)
+        
+        notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
+        self.imagePanelName = imagePanelName
+        self.imagePanel = self._imagePanel(notebook)
+        self.positionPanel = self._positionPanel(notebook)
+        self.OnPositionType(None)
+        
+        if self.imageDict['epsfile']:
+            self.imagePanel.image['dir'].SetValue(os.path.dirname(self.imageDict['epsfile']))
+        else:
+            self.imagePanel.image['dir'].SetValue(self._getImageDirectory())
+        self.OnDirChanged(None)
+     
+        self._layout(notebook)
+        
+        
+    def _newObject(self):
+        """!Create corresponding instruction object"""
+        return Image(self.id, self.instruction)
+        
+    def _imagePanel(self, notebook):
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY, size = (-1, -1), style = wx.TAB_TRAVERSAL)
+        notebook.AddPage(page = panel, text = self.imagePanelName)
+        border = wx.BoxSizer(wx.VERTICAL)
+        #
+        # choose image
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Image"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        # choose directory
+        panel.image = {}
+        if self.imageDict['epsfile']:
+            startDir = os.path.dirname(self.imageDict['epsfile'])
+        else:
+            startDir = self._getImageDirectory()
+        dir = filebrowse.DirBrowseButton(parent = panel, id = wx.ID_ANY,
+                                         labelText = _("Choose a directory:"),
+                                         dialogTitle = _("Choose a directory with images"),
+                                         buttonText = _('Browse'),
+                                         startDirectory = startDir,
+                                         changeCallback = self.OnDirChanged)
+        panel.image['dir'] = dir
+       
+        
+        sizer.Add(item = dir, proportion = 0, flag = wx.EXPAND, border = 0)
+        
+        # image list
+        hSizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        imageList = wx.ListBox(parent = panel, id = wx.ID_ANY)
+        panel.image['list'] = imageList
+        imageList.Bind(wx.EVT_LISTBOX, self.OnImageSelectionChanged)
+        
+        hSizer.Add(item = imageList, proportion = 1, flag = wx.EXPAND | wx.RIGHT, border = 10)
+        
+        # image preview
+        vSizer = wx.BoxSizer(wx.VERTICAL)
+        self.previewSize = (150, 150)
+        img = wx.EmptyImage(*self.previewSize)
+        panel.image['preview'] = wx.StaticBitmap(parent = panel, id = wx.ID_ANY,
+                                                bitmap = wx.BitmapFromImage(img))
+        vSizer.Add(item = panel.image['preview'], proportion = 0, flag = wx.EXPAND | wx.BOTTOM, border = 5)
+        panel.image['sizeInfo'] = wx.StaticText(parent = panel, id = wx.ID_ANY)
+        vSizer.Add(item = panel.image['sizeInfo'], proportion = 0, flag = wx.ALIGN_CENTER, border = 0)
+        
+        hSizer.Add(item = vSizer, proportion = 0, flag = wx.EXPAND, border = 0)
+        sizer.Add(item = hSizer, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 3)
+        
+        epsInfo = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                label = _("Note: only EPS format supported"))
+        sizer.Add(item = epsInfo, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 3)
+        
+        
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        #
+        # rotation
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Scale And Rotation"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        
+        scaleLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Scale:"))
+        if fs:
+            panel.image['scale'] = fs.FloatSpin(panel, id = wx.ID_ANY, min_val = 0, max_val = 50,
+                                          increment = 0.5, value = 1, style = fs.FS_RIGHT, size = self.spinCtrlSize)
+            panel.image['scale'].SetFormat("%f")
+            panel.image['scale'].SetDigits(1)
+        else:
+            panel.image['scale'] = wx.TextCtrl(panel, id = wx.ID_ANY, size = self.spinCtrlSize,
+                                                  validator = TCValidator(flag = 'DIGIT_ONLY'))
+        
+        if self.imageDict['scale']:
+            if fs:
+                value = float(self.imageDict['scale'])
+            else:
+                value = str(self.imageDict['scale'])
+        else:
+            if fs:
+                value = 0
+            else:
+                value = '0'
+        panel.image['scale'].SetValue(value)
+            
+        gridSizer.Add(item = scaleLabel, pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = panel.image['scale'], pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        
+        rotLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Rotation angle (deg):"))
+        if fs:
+            panel.image['rotate'] = fs.FloatSpin(panel, id = wx.ID_ANY, min_val = 0, max_val = 360,
+                                          increment = 0.5, value = 0, style = fs.FS_RIGHT, size = self.spinCtrlSize)
+            panel.image['rotate'].SetFormat("%f")
+            panel.image['rotate'].SetDigits(1)
+        else:
+            panel.image['rotate'] = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = self.spinCtrlSize,
+                                                min = 0, max = 359, initial = 0)
+        panel.image['rotate'].SetToolTipString(_("Counterclockwise rotation in degrees"))
+        if self.imageDict['rotate']:
+            panel.image['rotate'].SetValue(int(self.imageDict['rotate']))
+        else:
+            panel.image['rotate'].SetValue(0)
+            
+        gridSizer.Add(item = rotLabel, pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridSizer.Add(item = panel.image['rotate'], pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        self._addConvergence(panel = panel, gridBagSizer = gridSizer)
+        sizer.Add(item = gridSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        panel.SetSizer(border)
+        panel.Fit()
+        
+        return panel
+        
+    def _positionPanel(self, notebook):
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY, size = (-1, -1), style = wx.TAB_TRAVERSAL)
+        notebook.AddPage(page = panel, text = _("Position"))
+        border = wx.BoxSizer(wx.VERTICAL)
+        #
+        # set position
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Position"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridBagSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        gridBagSizer.AddGrowableCol(0)
+        gridBagSizer.AddGrowableCol(1)
+        
+        self.AddExtendedPosition(panel, gridBagSizer, self.imageDict)
+        
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnPositionType, panel.position['toPaper']) 
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnPositionType, panel.position['toMap'])
+        
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.ALIGN_CENTER_VERTICAL| wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        panel.SetSizer(border)
+        panel.Fit()
+        
+        return panel
+        
+    def OnDirChanged(self, event):
+        """!Image directory changed"""
+        path = self.imagePanel.image['dir'].GetValue()
+        try:
+            files = os.listdir(path)
+        except OSError: # no such directory
+            files = []
+        imageList = []
+        
+        # no setter for startDirectory?
+        try:
+            self.imagePanel.image['dir'].startDirectory = path
+        except AttributeError: # for sure
+            pass
+        for file in files:
+            if os.path.splitext(file)[1].lower() == '.eps':
+                imageList.append(file)
+        
+        imageList.sort()
+        self.imagePanel.image['list'].SetItems(imageList)
+        if self.imageDict['epsfile']:
+            file = os.path.basename(self.imageDict['epsfile'])
+            self.imagePanel.image['list'].SetStringSelection(file)
+        elif imageList:
+            self.imagePanel.image['list'].SetSelection(0)
+        self.OnImageSelectionChanged(None)
+        
+    def OnPositionType(self, event):
+        if self.positionPanel.position['toPaper'].GetValue():
+            for widget in self.gridBagSizerP.GetChildren():
+                widget.GetWindow().Enable()
+            for widget in self.gridBagSizerM.GetChildren():
+                widget.GetWindow().Disable()
+        else:
+            for widget in self.gridBagSizerM.GetChildren():
+                widget.GetWindow().Enable()
+            for widget in self.gridBagSizerP.GetChildren():
+                widget.GetWindow().Disable()
+                
+    def _getImageDirectory(self):
+        """!Default image directory"""
+        return os.getcwd()
+        
+    def _addConvergence(self, panel, gridBagSizer):
+        pass
+        
+    def OnImageSelectionChanged(self, event):
+        """!Image selected, show preview and size"""
+        if not self.imagePanel.image['dir']: # event is emitted when closing dialog an it causes error
+            return
+            
+        if not havePILImage:
+            self.DrawWarningText(_("PIL\nmissing"))
+            return
+        
+        imageName = self.imagePanel.image['list'].GetStringSelection()
+        if not imageName:
+            self.ClearPreview()
+            return
+        basePath = self.imagePanel.image['dir'].GetValue()
+        file = os.path.join(basePath, imageName)
+        if not os.path.exists(file):
+            return
+            
+        if os.path.splitext(file)[1].lower() == '.eps':
+            try:
+                pImg = PILImage.open(file)
+                img = PilImageToWxImage(pImg)
+            except IOError, e:
+                GError(message = _("Unable to read file %s") % file)
+                self.ClearPreview()
+                return
+            self.SetSizeInfoLabel(img)
+            img = self.ScaleToPreview(img)
+            bitmap = img.ConvertToBitmap()
+            self.DrawBitmap(bitmap)
+            
+        else:
+            # TODO: read other formats and convert by PIL to eps
+            pass
+    
+    def ScaleToPreview(self, img):
+        """!Scale image to preview size"""
+        w = img.GetWidth()
+        h = img.GetHeight()
+        if w <= self.previewSize[0] and h <= self.previewSize[1]:
+            return img
+        if w > h:
+            newW = self.previewSize[0]
+            newH = self.previewSize[0] * h / w
+        else:
+            newH = self.previewSize[0]
+            newW = self.previewSize[0] * w / h
+        return img.Scale(newW, newH, wx.IMAGE_QUALITY_HIGH)
+        
+    def DrawWarningText(self, warning):
+        """!Draw text on preview window"""
+        buffer = wx.EmptyBitmap(*self.previewSize)
+        dc = wx.MemoryDC()
+        dc.SelectObject(buffer)
+        dc.SetBrush(wx.Brush(wx.Color(250, 250, 250)))
+        dc.Clear()
+        extent = dc.GetTextExtent(warning)
+        posX = self.previewSize[0] / 2 - extent[0] / 2
+        posY = self.previewSize[1] / 2 - extent[1] / 2
+        dc.DrawText(warning, posX, posY)
+        self.imagePanel.image['preview'].SetBitmap(buffer)
+        dc.SelectObject(wx.NullBitmap)
+        
+    def DrawBitmap(self, bitmap):
+        """!Draw bitmap, center it if smaller than preview size"""
+        if bitmap.GetWidth() <= self.previewSize[0] and bitmap.GetHeight() <= self.previewSize[1]:
+            buffer = wx.EmptyBitmap(*self.previewSize)
+            dc = wx.MemoryDC()
+            dc.SelectObject(buffer)
+            dc.SetBrush(dc.GetBrush())
+            dc.Clear()
+            posX = self.previewSize[0] / 2 - bitmap.GetWidth() / 2
+            posY = self.previewSize[1] / 2 - bitmap.GetHeight() / 2
+            dc.DrawBitmap(bitmap, posX, posY)
+            self.imagePanel.image['preview'].SetBitmap(buffer)
+            dc.SelectObject(wx.NullBitmap)
+        else:
+            self.imagePanel.image['preview'].SetBitmap(bitmap)
+            
+    def SetSizeInfoLabel(self, image):
+        """!Update image size label"""
+        self.imagePanel.image['sizeInfo'].SetLabel(_("size: %(width)s x %(height)s pts") % \
+                                                       { 'width'  : image.GetWidth(),
+                                                         'height' : image.GetHeight() })
+        self.imagePanel.image['sizeInfo'].GetContainingSizer().Layout()
+        
+    def ClearPreview(self):
+        """!Clear preview window"""
+        buffer = wx.EmptyBitmap(*self.previewSize)
+        dc = wx.MemoryDC()
+        dc.SelectObject(buffer)
+        dc.SetBrush(wx.WHITE_BRUSH)
+        dc.Clear()
+        mask = wx.Mask(buffer, wx.WHITE)
+        buffer.SetMask(mask)
+        self.imagePanel.image['preview'].SetBitmap(buffer)
+        dc.SelectObject(wx.NullBitmap)
+        
+    def update(self): 
+        # epsfile
+        selected = self.imagePanel.image['list'].GetStringSelection()
+        basePath = self.imagePanel.image['dir'].GetValue()
+        if not selected:
+            GMessage(parent = self, message = _("No image selected."))
+            return False
+            
+        self.imageDict['epsfile'] = os.path.join(basePath, selected)
+        
+        #position
+        if self.positionPanel.position['toPaper'].GetValue():
+            self.imageDict['XY'] = True
+            currUnit = self.unitConv.findUnit(self.positionPanel.units['unitsCtrl'].GetStringSelection())
+            self.imageDict['unit'] = currUnit
+            if self.positionPanel.position['xCtrl'].GetValue():
+                x = self.positionPanel.position['xCtrl'].GetValue() 
+            else:
+                x = self.imageDict['where'][0]
+
+            if self.positionPanel.position['yCtrl'].GetValue():
+                y = self.positionPanel.position['yCtrl'].GetValue() 
+            else:
+                y = self.imageDict['where'][1]
+
+            x = self.unitConv.convert(value = float(x), fromUnit = currUnit, toUnit = 'inch')
+            y = self.unitConv.convert(value = float(y), fromUnit = currUnit, toUnit = 'inch')
+            self.imageDict['where'] = x, y
+            
+        else:
+            self.imageDict['XY'] = False
+            if self.positionPanel.position['eCtrl'].GetValue():
+                e = self.positionPanel.position['eCtrl'].GetValue() 
+            else:
+                self.imageDict['east'] = self.imageDict['east']
+
+            if self.positionPanel.position['nCtrl'].GetValue():
+                n = self.positionPanel.position['nCtrl'].GetValue() 
+            else:
+                self.imageDict['north'] = self.imageDict['north']
+
+            x, y = PaperMapCoordinates(mapInstr = self.instruction[self.mapId], x = float(self.imageDict['east']),
+                                       y = float(self.imageDict['north']), paperToMap = False)
+
+        #rotation
+        rot = self.imagePanel.image['rotate'].GetValue()
+        if rot == 0:
+            self.imageDict['rotate'] = None
+        else:
+            self.imageDict['rotate'] = rot
+        
+        #scale
+        self.imageDict['scale'] = self.imagePanel.image['scale'].GetValue()
+                
+        # scale
+        w, h = self.imageObj.GetImageOrigSize(self.imageDict['epsfile'])
+        if self.imageDict['rotate']:
+            self.imageDict['size'] = BBoxAfterRotation(w, h, self.imageDict['rotate'])
+        else:
+            self.imageDict['size'] = w, h
+            
+        w = self.unitConv.convert(value = self.imageDict['size'][0],
+                                  fromUnit = 'point', toUnit = 'inch')
+        h = self.unitConv.convert(value = self.imageDict['size'][1],
+                                  fromUnit = 'point', toUnit = 'inch')
+                                  
+    
+        self.imageDict['rect'] = Rect2D(x = x, y = y,
+                                        width = w * self.imageDict['scale'],
+                                        height = h * self.imageDict['scale'])
+        
+        if self.id not in self.instruction:
+            image = self._newObject()
+            self.instruction.AddInstruction(image)
+        self.instruction[self.id].SetInstruction(self.imageDict)
+        
+        if self.id not in self.parent.objectId:
+            self.parent.objectId.append(self.id)
+
+        return True
+        
+    def updateDialog(self):
+        """!Update text coordinates, after moving"""
+        # XY coordinates
+        x, y = self.imageDict['where'][:2]
+        currUnit = self.unitConv.findUnit(self.positionPanel.units['unitsCtrl'].GetStringSelection())
+        x = self.unitConv.convert(value = x, fromUnit = 'inch', toUnit = currUnit)
+        y = self.unitConv.convert(value = y, fromUnit = 'inch', toUnit = currUnit)
+        self.positionPanel.position['xCtrl'].SetValue("%5.3f" % x)
+        self.positionPanel.position['yCtrl'].SetValue("%5.3f" % y)
+        # EN coordinates
+        e, n = self.imageDict['east'], self.imageDict['north']
+        self.positionPanel.position['eCtrl'].SetValue(str(self.imageDict['east']))
+        self.positionPanel.position['nCtrl'].SetValue(str(self.imageDict['north']))
+        
+        
+class NorthArrowDialog(ImageDialog):
+    def __init__(self, parent, id, settings):
+        ImageDialog.__init__(self, parent = parent, id = id, settings = settings,
+                             imagePanelName = _("North Arrow"))
+        
+        self.objectType = ('northArrow',)
+        self.SetTitle(_("North Arrow settings"))
+    
+    def _newObject(self):
+        return NorthArrow(self.id, self.instruction)
+        
+    def _getImageDirectory(self):
+        gisbase = os.getenv("GISBASE")
+        return os.path.join(gisbase, 'etc', 'paint', 'decorations')
+    
+    def _addConvergence(self, panel, gridBagSizer):
+        convergence = wx.Button(parent = panel, id = wx.ID_ANY,
+                                               label = _("Compute convergence"))
+        gridBagSizer.Add(item = convergence, pos = (1, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
+        convergence.Bind(wx.EVT_BUTTON, self.OnConvergence)
+        panel.image['convergence'] = convergence
+        
+    def OnConvergence(self, event):
+        ret = RunCommand('g.region', read = True, flags = 'ng')
+        if ret:
+            convergence = float(ret.strip().split('=')[1])
+            if convergence < 0:
+                self.imagePanel.image['rotate'].SetValue(abs(convergence))
+            else:
+                self.imagePanel.image['rotate'].SetValue(360 - convergence)
+            
+        
+class PointDialog(PsmapDialog):
+    """!Dialog for setting point properties."""
+    def __init__(self, parent, id, settings, coordinates = None, pointPanelName = _("Point")):
+        PsmapDialog.__init__(self, parent = parent, id = id, title = "Point settings",
+                             settings = settings)
+        
+        self.objectType = ('point',)
+        if self.id is not None:
+            self.pointObj = self.instruction[self.id]
+            self.pointDict = self.instruction[id].GetInstruction()
+        else:
+            self.id = wx.NewId()
+            self.pointObj = Point(self.id)
+            self.pointDict = self.pointObj.GetInstruction()
+            self.pointDict['where'] = coordinates 
+        self.defaultDict = self.pointObj.defaultInstruction
+                
+        mapObj = self.instruction.FindInstructionByType('map')
+        if not mapObj:
+            mapObj = self.instruction.FindInstructionByType('initMap')
+        self.mapId = mapObj.id
+        
+        self.pointDict['east'], self.pointDict['north'] = PaperMapCoordinates(mapInstr = mapObj, x = self.pointDict['where'][0], y = self.pointDict['where'][1], paperToMap = True)
+        
+        notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
+        self.pointPanelName = pointPanelName
+        self.pointPanel = self._pointPanel(notebook)
+        self.positionPanel = self._positionPanel(notebook)
+        self.OnPositionType(None)
+        
+     
+        self._layout(notebook)
+        
+    def _pointPanel(self, notebook):
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY, size = (-1, -1), style = wx.TAB_TRAVERSAL)
+        notebook.AddPage(page = panel, text = self.pointPanelName)
+        border = wx.BoxSizer(wx.VERTICAL)
+        #
+        # choose image
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Symbol"))
+        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        
+        gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        gridSizer.AddGrowableCol(1)
+
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Select symbol:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        self.symbolLabel = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                          label = self.pointDict['symbol'])
+        gridSizer.Add(item = self.symbolLabel, pos = (0, 1),
+                      flag = wx.ALIGN_CENTER_VERTICAL )
+        bitmap = wx.Bitmap(os.path.join(globalvar.ETCSYMBOLDIR,
+                                        self.pointDict['symbol']) + '.png')
+        self.symbolButton = wx.BitmapButton(panel, id = wx.ID_ANY, bitmap = bitmap)
+        self.symbolButton.Bind(wx.EVT_BUTTON, self.OnSymbolSelection)
+
+        gridSizer.Add(self.symbolButton, pos = (0, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        self.noteLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, 
+                                       label = _("Note: Selected symbol is not displayed\n"
+                                                 "in draft mode (only in preview mode)"))
+        gridSizer.Add(self.noteLabel, pos = (1, 0), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+
+        sizer.Add(item = gridSizer, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
+        
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        #
+        # outline/fill color
+        #
+
+        # outline
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Color"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        
+        outlineLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Outline color:"))
+        self.outlineColorCtrl = wx.ColourPickerCtrl(panel, id = wx.ID_ANY)
+        self.outlineTranspCtrl = wx.CheckBox(panel, id = wx.ID_ANY, label = _("transparent"))
+
+        if self.pointDict['color'] != 'none':
+            self.outlineTranspCtrl.SetValue(False)
+            self.outlineColorCtrl.SetColour(convertRGB(self.pointDict['color']))
+        else:
+            self.outlineTranspCtrl.SetValue(True)
+            self.outlineColorCtrl.SetColour(convertRGB(self.defaultDict['color']))
+
+        gridSizer.Add(item = outlineLabel, pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = self.outlineColorCtrl, pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = self.outlineTranspCtrl, pos = (0, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+
+        fillLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Fill color:"))
+        self.fillColorCtrl = wx.ColourPickerCtrl(panel, id = wx.ID_ANY)
+        self.fillTranspCtrl = wx.CheckBox(panel, id = wx.ID_ANY, label = _("transparent"))
+
+        if self.pointDict['fcolor'] != 'none':
+            self.fillTranspCtrl.SetValue(False)
+            self.fillColorCtrl.SetColour(convertRGB(self.pointDict['fcolor']))
+        else:
+            self.fillTranspCtrl.SetValue(True)
+            self.fillColorCtrl.SetColour(convertRGB(self.defaultDict['fcolor']))
+
+        gridSizer.Add(item = fillLabel, pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = self.fillColorCtrl, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = self.fillTranspCtrl, pos = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        sizer.Add(item = gridSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+
+        #
+        # size and rotation
+        #
+
+        # size
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Size and Rotation"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        
+        sizeLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Size (pt):"))
+        self.sizeCtrl = wx.SpinCtrl(panel, id = wx.ID_ANY, size = self.spinCtrlSize)
+        self.sizeCtrl.SetToolTipString(_("Symbol size in points"))
+        self.sizeCtrl.SetValue(self.pointDict['size'])
+        
+        gridSizer.Add(item = sizeLabel, pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = self.sizeCtrl, pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # rotation
+        rotLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Rotation angle (deg):"))
+        if fs:
+            self.rotCtrl = fs.FloatSpin(panel, id = wx.ID_ANY, min_val = -360, max_val = 360,
+                                          increment = 1, value = 0, style = fs.FS_RIGHT, size = self.spinCtrlSize)
+            self.rotCtrl.SetFormat("%f")
+            self.rotCtrl.SetDigits(1)
+        else:
+            self.rotCtrl = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = self.spinCtrlSize,
+                                                min = -360, max = 360, initial = 0)
+        self.rotCtrl.SetToolTipString(_("Counterclockwise rotation in degrees"))
+        self.rotCtrl.SetValue(float(self.pointDict['rotate']))
+            
+        gridSizer.Add(item = rotLabel, pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL, border = 0)
+        gridSizer.Add(item = self.rotCtrl, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        sizer.Add(item = gridSizer, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        panel.SetSizer(border)
+        panel.Fit()
+        
+        return panel
+        
+    def _positionPanel(self, notebook):
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY, size = (-1, -1), style = wx.TAB_TRAVERSAL)
+        notebook.AddPage(page = panel, text = _("Position"))
+        border = wx.BoxSizer(wx.VERTICAL)
+        #
+        # set position
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Position"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        gridBagSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        gridBagSizer.AddGrowableCol(0)
+        gridBagSizer.AddGrowableCol(1)
+        
+        self.AddExtendedPosition(panel, gridBagSizer, self.pointDict)
+        
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnPositionType, panel.position['toPaper']) 
+        self.Bind(wx.EVT_RADIOBUTTON, self.OnPositionType, panel.position['toMap'])
+        
+        
+        sizer.Add(gridBagSizer, proportion = 1, flag = wx.ALIGN_CENTER_VERTICAL| wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        
+        panel.SetSizer(border)
+        panel.Fit()
+        
+        return panel
+        
+    def OnPositionType(self, event):
+        if self.positionPanel.position['toPaper'].GetValue():
+            for widget in self.gridBagSizerP.GetChildren():
+                widget.GetWindow().Enable()
+            for widget in self.gridBagSizerM.GetChildren():
+                widget.GetWindow().Disable()
+        else:
+            for widget in self.gridBagSizerM.GetChildren():
+                widget.GetWindow().Enable()
+            for widget in self.gridBagSizerP.GetChildren():
+                widget.GetWindow().Disable()
+                
+    def OnSymbolSelection(self, event):
+        dlg = SymbolDialog(self, symbolPath = globalvar.ETCSYMBOLDIR,
+                           currentSymbol = self.symbolLabel.GetLabel())
+        if dlg.ShowModal() == wx.ID_OK:
+            img = dlg.GetSelectedSymbol(fullPath = True)
+            name = dlg.GetSelectedSymbol(fullPath = False)
+            self.symbolButton.SetBitmapLabel(wx.Bitmap(img + '.png'))
+            self.symbolLabel.SetLabel(name)
+            
+        dlg.Destroy()
+        
+    def update(self): 
+        # symbol
+        self.pointDict['symbol'] = self.symbolLabel.GetLabel()
+
+        
+        #position
+        if self.positionPanel.position['toPaper'].GetValue():
+            self.pointDict['XY'] = True
+            currUnit = self.unitConv.findUnit(self.positionPanel.units['unitsCtrl'].GetStringSelection())
+            self.pointDict['unit'] = currUnit
+            if self.positionPanel.position['xCtrl'].GetValue():
+                x = self.positionPanel.position['xCtrl'].GetValue() 
+            else:
+                x = self.pointDict['where'][0]
+
+            if self.positionPanel.position['yCtrl'].GetValue():
+                y = self.positionPanel.position['yCtrl'].GetValue() 
+            else:
+                y = self.pointDict['where'][1]
+
+            x = self.unitConv.convert(value = float(x), fromUnit = currUnit, toUnit = 'inch')
+            y = self.unitConv.convert(value = float(y), fromUnit = currUnit, toUnit = 'inch')
+            self.pointDict['where'] = x, y
+            
+        else:
+            self.pointDict['XY'] = False
+            if self.positionPanel.position['eCtrl'].GetValue():
+                e = self.positionPanel.position['eCtrl'].GetValue() 
+            else:
+                self.pointDict['east'] = self.pointDict['east']
+
+            if self.positionPanel.position['nCtrl'].GetValue():
+                n = self.positionPanel.position['nCtrl'].GetValue() 
+            else:
+                self.pointDict['north'] = self.pointDict['north']
+
+            x, y = PaperMapCoordinates(mapInstr = self.instruction[self.mapId], x = float(self.pointDict['east']),
+                                       y = float(self.pointDict['north']), paperToMap = False)
+
+        #rotation
+        self.pointDict['rotate'] = self.rotCtrl.GetValue()
+        
+        # size
+        self.pointDict['size'] = self.sizeCtrl.GetValue()
+            
+        w = h = self.unitConv.convert(value = self.pointDict['size'],
+                                  fromUnit = 'point', toUnit = 'inch')
+                                  
+        # outline color
+        if self.outlineTranspCtrl.GetValue():
+            self.pointDict['color'] = 'none'
+        else:
+            self.pointDict['color'] = convertRGB(self.outlineColorCtrl.GetColour())
+
+        # fill color
+        if self.fillTranspCtrl.GetValue():
+            self.pointDict['fcolor'] = 'none'
+        else:
+            self.pointDict['fcolor'] = convertRGB(self.fillColorCtrl.GetColour())
+
+        self.pointDict['rect'] = Rect2D(x = x - w / 2, y = y - h / 2, width = w, height = h)
+        
+        if self.id not in self.instruction:
+            point = Point(self.id)
+            self.instruction.AddInstruction(point)
+        self.instruction[self.id].SetInstruction(self.pointDict)
+        
+        if self.id not in self.parent.objectId:
+            self.parent.objectId.append(self.id)
+
+        return True
+        
+    def updateDialog(self):
+        """!Update text coordinates, after moving"""
+        # XY coordinates
+        x, y = self.pointDict['where'][:2]
+        currUnit = self.unitConv.findUnit(self.positionPanel.units['unitsCtrl'].GetStringSelection())
+        x = self.unitConv.convert(value = x, fromUnit = 'inch', toUnit = currUnit)
+        y = self.unitConv.convert(value = y, fromUnit = 'inch', toUnit = currUnit)
+        self.positionPanel.position['xCtrl'].SetValue("%5.3f" % x)
+        self.positionPanel.position['yCtrl'].SetValue("%5.3f" % y)
+        # EN coordinates
+        e, n = self.pointDict['east'], self.pointDict['north']
+        self.positionPanel.position['eCtrl'].SetValue(str(self.pointDict['east']))
+        self.positionPanel.position['nCtrl'].SetValue(str(self.pointDict['north']))
+        
+class RectangleDialog(PsmapDialog):
+    def __init__(self, parent, id, settings, type = 'rectangle', coordinates = None):
+        """!
+
+        @param coordinates begin and end point coordinate (wx.Point, wx.Point)
+        """
+        if type == 'rectangle':
+            title = _("Rectangle settings")
+        else:
+            title = _("Line settings")
+        PsmapDialog.__init__(self, parent = parent, id = id, title = title, settings = settings)
+        
+        self.objectType = (type,)
+
+        if self.id is not None:
+            self.rectObj = self.instruction[self.id]
+            self.rectDict = self.rectObj.GetInstruction()
+        else:
+            self.id = wx.NewId()
+            if type == 'rectangle':
+                self.rectObj = Rectangle(self.id)
+            else:
+                self.rectObj = Line(self.id)
+            self.rectDict = self.rectObj.GetInstruction()
+
+            self.rectDict['rect'] = Rect2DPP(coordinates[0], coordinates[1])
+            self.rectDict['where'] = coordinates
+
+        self.defaultDict = self.rectObj.defaultInstruction
+        self.panel = self._rectPanel()
+        
+        self._layout(self.panel)
+
+    def _rectPanel(self):
+        panel = wx.Panel(parent = self, id = wx.ID_ANY, style = wx.TAB_TRAVERSAL)
+        border = wx.BoxSizer(wx.VERTICAL)
+                
+        # color
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Color"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+        
+        outlineLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Outline color:"))
+        self.outlineColorCtrl = wx.ColourPickerCtrl(panel, id = wx.ID_ANY)
+        self.outlineTranspCtrl = wx.CheckBox(panel, id = wx.ID_ANY, label = _("transparent"))
+
+        if self.rectDict['color'] != 'none':
+            self.outlineTranspCtrl.SetValue(False)
+            self.outlineColorCtrl.SetColour(convertRGB(self.rectDict['color']))
+        else:
+            self.outlineTranspCtrl.SetValue(True)
+            self.outlineColorCtrl.SetColour(convertRGB(self.defaultDict['color']))
+
+        # transparent outline makes sense only for rectangle
+        if self.objectType == ('line',):
+            self.outlineTranspCtrl.Hide()
+
+        gridSizer.Add(item = outlineLabel, pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = self.outlineColorCtrl, pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = self.outlineTranspCtrl, pos = (0, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+
+        # fill color only in rectangle
+        if self.objectType == ('rectangle',):
+            fillLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Fill color:"))
+            self.fillColorCtrl = wx.ColourPickerCtrl(panel, id = wx.ID_ANY)
+            self.fillTranspCtrl = wx.CheckBox(panel, id = wx.ID_ANY, label = _("transparent"))
+
+            if self.rectDict['fcolor'] != 'none':
+                self.fillTranspCtrl.SetValue(False)
+                self.fillColorCtrl.SetColour(convertRGB(self.rectDict['fcolor']))
+            else:
+                self.fillTranspCtrl.SetValue(True)
+                self.fillColorCtrl.SetColour(wx.WHITE)
+
+            gridSizer.Add(item = fillLabel, pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+            gridSizer.Add(item = self.fillColorCtrl, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+            gridSizer.Add(item = self.fillTranspCtrl, pos = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+
+        sizer.Add(gridSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+        gridSizer = wx.GridBagSizer (hgap = 5, vgap = 5)
+
+        # width
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Line style"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        widthLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Line width:"))
+        if fs:
+            self.widthCtrl = fs.FloatSpin(panel, id = wx.ID_ANY, min_val = 0, max_val = 50,
+                                          increment = 1, value = 0, style = fs.FS_RIGHT, size = self.spinCtrlSize)
+            self.widthCtrl.SetFormat("%f")
+            self.widthCtrl.SetDigits(1)
+        else:
+            self.widthCtrl = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = self.spinCtrlSize,
+                                                min = -360, max = 360, initial = 0)
+        self.widthCtrl.SetToolTipString(_("Line width in points"))
+        self.widthCtrl.SetValue(float(self.rectDict['width']))
+
+        gridSizer.Add(item = widthLabel, pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = self.widthCtrl, pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+
+        sizer.Add(gridSizer, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 5)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+
+        panel.SetSizer(border)
+        
+        return panel
+        
+
+    def update(self):
+        mapInstr = self.instruction.FindInstructionByType('map')
+        if not mapInstr:
+            mapInstr = self.instruction.FindInstructionByType('initMap')
+        self.mapId = mapInstr.id
+        point1 = self.rectDict['where'][0]
+        point2 = self.rectDict['where'][1]
+        self.rectDict['east1'], self.rectDict['north1'] = PaperMapCoordinates(mapInstr = mapInstr,
+                                                                                x = point1[0],
+                                                                                y = point1[1],
+                                                                                paperToMap = True)
+        self.rectDict['east2'], self.rectDict['north2'] = PaperMapCoordinates(mapInstr = mapInstr,
+                                                                                x = point2[0],
+                                                                                y = point2[1],
+                                                                                paperToMap = True)
+        # width
+        self.rectDict['width'] = self.widthCtrl.GetValue()
+        
+        # outline color
+        if self.outlineTranspCtrl.GetValue():
+            self.rectDict['color'] = 'none'
+        else:
+            self.rectDict['color'] = convertRGB(self.outlineColorCtrl.GetColour())
+
+        # fill color
+        if self.objectType == ('rectangle',):
+            if self.fillTranspCtrl.GetValue():
+                self.rectDict['fcolor'] = 'none'
+            else:
+                self.rectDict['fcolor'] = convertRGB(self.fillColorCtrl.GetColour())
+
+        if self.id not in self.instruction:
+            if self.objectType == ('rectangle',):
+                rect = Rectangle(self.id)
+            else:
+                rect = Line(self.id)
+            self.instruction.AddInstruction(rect)
+            
+        self.instruction[self.id].SetInstruction(self.rectDict)
+
+        if self.id not in self.parent.objectId:
+            self.parent.objectId.append(self.id)
+            
+        self.updateDialog()
+
+        return True
+
+    def updateDialog(self):
+        """!Update text coordinates, after moving"""
+        pass
+


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/psmap/frame.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/psmap/frame.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/psmap/frame.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,2176 @@
+"""!
+ at package psmap.frame
+
+ at brief GUI for ps.map
+
+Classes:
+ - frame::PsMapFrame
+ - frame::PsMapBufferedWindow
+
+(C) 2011-2012 by Anna Kratochvilova, and 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 Anna Kratochvilova <kratochanna gmail.com> (bachelor's project)
+ at author Martin Landa <landa.martin gmail.com> (mentor)
+"""
+
+import os
+import sys
+import textwrap
+import Queue
+from math import sin, cos, pi, sqrt
+
+if __name__ == "__main__":
+    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython'))
+from core             import globalvar
+import wx
+
+try:
+    import wx.lib.agw.flatnotebook as fnb
+except ImportError:
+    import wx.lib.flatnotebook as fnb
+
+import grass.script as grass
+
+from gui_core.menu      import Menu
+from gui_core.goutput   import CmdThread, EVT_CMD_DONE
+from psmap.toolbars     import PsMapToolbar
+from core.gcmd          import RunCommand, GError, GMessage
+from core.settings      import UserSettings
+from gui_core.forms     import GUI
+from psmap.menudata     import PsMapData
+
+from psmap.dialogs      import *
+from psmap.instructions import *
+from psmap.utils        import *
+
+class PsMapFrame(wx.Frame):
+    def __init__(self, parent = None, id = wx.ID_ANY,
+                 title = _("GRASS GIS Cartographic Composer (experimental prototype)"), **kwargs):
+        """!Main window of ps.map GUI
+        
+        @param parent parent window
+        @param id window id
+        @param title window title
+        
+        @param kwargs wx.Frames' arguments
+        """
+        self.parent = parent
+
+        wx.Frame.__init__(self, parent = parent, id = id, title = title, name = "PsMap", **kwargs)
+        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+        #menubar
+        self.menubar = Menu(parent = self, data = PsMapData())
+        self.SetMenuBar(self.menubar)
+        #toolbar
+
+        self.toolbar = PsMapToolbar(parent = self)
+        self.SetToolBar(self.toolbar)
+        
+        self.actionOld = self.toolbar.action['id']
+        self.iconsize = (16, 16)
+        #satusbar
+        self.statusbar = self.CreateStatusBar(number = 1)
+        
+        # mouse attributes -- position on the screen, begin and end of
+        # dragging, and type of drawing
+        self.mouse = {
+            'begin': [0, 0], # screen coordinates
+            'end'  : [0, 0],
+            'use'  : "pointer",
+            }
+        # available cursors
+        self.cursors = {
+            "default" : wx.StockCursor(wx.CURSOR_ARROW),
+            "cross"   : wx.StockCursor(wx.CURSOR_CROSS),
+            "hand"    : wx.StockCursor(wx.CURSOR_HAND),
+            "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
+            }
+        # pen and brush
+        self.pen = {
+            'paper': wx.Pen(colour = "BLACK", width = 1),
+            'margins': wx.Pen(colour = "GREY", width = 1),
+            'map': wx.Pen(colour = wx.Color(86, 122, 17), width = 2),
+            'rasterLegend': wx.Pen(colour = wx.Color(219, 216, 4), width = 2),
+            'vectorLegend': wx.Pen(colour = wx.Color(219, 216, 4), width = 2),
+            'mapinfo': wx.Pen(colour = wx.Color(5, 184, 249), width = 2),
+            'scalebar': wx.Pen(colour = wx.Color(150, 150, 150), width = 2),
+            'image': wx.Pen(colour = wx.Color(255, 150, 50), width = 2),
+            'northArrow': wx.Pen(colour = wx.Color(200, 200, 200), width = 2),
+            'point': wx.Pen(colour = wx.Color(100, 100, 100), width = 2),
+            'line': wx.Pen(colour = wx.Color(0, 0, 0), width = 2),
+            'box': wx.Pen(colour = 'RED', width = 2, style = wx.SHORT_DASH),
+            'select': wx.Pen(colour = 'BLACK', width = 1, style = wx.SHORT_DASH),
+            'resize': wx.Pen(colour = 'BLACK', width = 1)
+            }
+        self.brush = {
+            'paper': wx.WHITE_BRUSH,
+            'margins': wx.TRANSPARENT_BRUSH,
+            'map': wx.Brush(wx.Color(151, 214, 90)),
+            'rasterLegend': wx.Brush(wx.Color(250, 247, 112)),
+            'vectorLegend': wx.Brush(wx.Color(250, 247, 112)),
+            'mapinfo': wx.Brush(wx.Color(127, 222, 252)),
+            'scalebar': wx.Brush(wx.Color(200, 200, 200)),
+            'image': wx.Brush(wx.Color(255, 200, 50)),
+            'northArrow': wx.Brush(wx.Color(255, 255, 255)),
+            'point': wx.Brush(wx.Color(200, 200, 200)),
+            'line': wx.TRANSPARENT_BRUSH,
+            'box': wx.TRANSPARENT_BRUSH,
+            'select':wx.TRANSPARENT_BRUSH,
+            'resize': wx.BLACK_BRUSH
+            } 
+        
+
+        # list of objects to draw
+        self.objectId = []
+        
+        # instructions
+        self.instruction = Instruction(parent = self, objectsToDraw = self.objectId)
+        # open dialogs
+        self.openDialogs = dict()
+        
+        self.pageId = wx.NewId()
+        #current page of flatnotebook
+        self.currentPage = 0
+        #canvas for draft mode
+        self.canvas = PsMapBufferedWindow(parent = self, mouse = self.mouse, pen = self.pen,
+                                          brush = self.brush, cursors = self.cursors, 
+                                          instruction = self.instruction, openDialogs = self.openDialogs,
+                                          pageId = self.pageId, objectId = self.objectId,
+                                          preview = False)
+        
+        self.canvas.SetCursor(self.cursors["default"])
+        self.getInitMap()
+        
+        
+        # image path
+        env = grass.gisenv()
+        self.imgName = grass.tempfile()
+        
+        #canvas for preview
+        self.previewCanvas = PsMapBufferedWindow(parent = self, mouse = self.mouse, cursors = self.cursors,
+                                                 pen = self.pen, brush = self.brush, preview = True)
+        
+        # set WIND_OVERRIDE
+        grass.use_temp_region()
+        
+        # create queues
+        self.requestQ = Queue.Queue()
+        self.resultQ = Queue.Queue()
+        # thread
+        self.cmdThread = CmdThread(self, self.requestQ, self.resultQ)
+        
+        self._layout()
+        self.SetMinSize(wx.Size(750, 600))
+        
+        self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGING, self.OnPageChanging)
+        self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+        self.Bind(EVT_CMD_DONE, self.OnCmdDone)
+        
+        if not havePILImage:
+            wx.CallAfter(self._showErrMsg)
+        
+    def _showErrMsg(self):
+        """!Show error message (missing preview)
+        """
+        GError(parent = self,
+               message = _("Python Imaging Library is not available.\n"
+                           "'Preview' functionality won't work."),
+               showTraceback = False)
+        
+    def _layout(self):
+        """!Do layout
+        """
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        if globalvar.hasAgw:
+            self.book = fnb.FlatNotebook(parent = self, id = wx.ID_ANY,
+                                         agwStyle = fnb.FNB_FANCY_TABS | fnb.FNB_BOTTOM |
+                                         fnb.FNB_NO_NAV_BUTTONS | fnb.FNB_NO_X_BUTTON)
+        else:
+            self.book = fnb.FlatNotebook(parent = self, id = wx.ID_ANY,
+                                         style = fnb.FNB_FANCY_TABS | fnb.FNB_BOTTOM |
+                                         fnb.FNB_NO_NAV_BUTTONS | fnb.FNB_NO_X_BUTTON)
+        #self.book = fnb.FlatNotebook(self, wx.ID_ANY, style = fnb.FNB_BOTTOM)
+        self.book.AddPage(self.canvas, "Draft mode")
+        self.book.AddPage(self.previewCanvas, "Preview")
+        self.book.SetSelection(0)
+        
+        mainSizer.Add(self.book,1, wx.EXPAND)
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+
+        
+    def InstructionFile(self):
+        """!Creates mapping instructions"""
+        
+        return str(self.instruction)
+
+    def OnPSFile(self, event):
+        """!Generate PostScript"""
+        filename = self.getFile(wildcard = "PostScript (*.ps)|*.ps|Encapsulated PostScript (*.eps)|*.eps")
+        if filename:
+            self.PSFile(filename)
+    
+    def OnPsMapDialog(self, event):
+        """!Launch ps.map dialog
+        """
+        GUI(parent = self).ParseCommand(cmd = ['ps.map'])
+
+    def OnPDFFile(self, event):
+        """!Generate PDF from PS with ps2pdf if available"""
+        try:
+            p = grass.Popen(["ps2pdf"], stderr = grass.PIPE)
+            p.stderr.close()
+        
+        except OSError:
+            GMessage(parent = self,
+                     message = _("Program ps2pdf is not available. Please install it first to create PDF."))
+            return
+        
+        filename = self.getFile(wildcard = "PDF (*.pdf)|*.pdf")
+        if filename:  
+            self.PSFile(filename, pdf = True)   
+            
+    def OnPreview(self, event):
+        """!Run ps.map and show result"""
+        self.PSFile()
+        
+    def PSFile(self, filename = None, pdf = False):
+        """!Create temporary instructions file and run ps.map with output = filename"""
+        instrFile = grass.tempfile()
+        instrFileFd = open(instrFile, mode = 'w')
+        instrFileFd.write(self.InstructionFile())
+        instrFileFd.flush()
+        instrFileFd.close()
+        
+        temp = False
+        regOld = grass.region()
+        
+        if pdf:
+            pdfname = filename
+        else:
+            pdfname = None
+        #preview or pdf
+        if not filename or (filename and pdf):
+            temp = True
+            filename = grass.tempfile()
+            if not pdf: # lower resolution for preview
+                if self.instruction.FindInstructionByType('map'):
+                    mapId = self.instruction.FindInstructionByType('map').id
+                    SetResolution(dpi = 100, width = self.instruction[mapId]['rect'][2],
+                                  height = self.instruction[mapId]['rect'][3])
+        
+        cmd = ['ps.map', '--overwrite']
+        if os.path.splitext(filename)[1] == '.eps':
+            cmd.append('-e')
+        if self.instruction[self.pageId]['Orientation'] == 'Landscape':
+            cmd.append('-r')
+        cmd.append('input=%s' % instrFile)
+        cmd.append('output=%s' % filename)
+        if pdf:
+            self.SetStatusText(_('Generating PDF...'), 0)
+        elif not temp:
+            self.SetStatusText(_('Generating PostScript...'), 0)
+        else:
+            self.SetStatusText(_('Generating preview...'), 0)
+            
+        self.cmdThread.RunCmd(cmd, userData = {'instrFile' : instrFile, 'filename' : filename,
+                                               'pdfname' : pdfname, 'temp' : temp, 'regionOld' : regOld})
+        
+    def OnCmdDone(self, event):
+        """!ps.map process finished"""
+        
+        if event.returncode != 0:
+            GMessage(parent = self,
+                     message = _("Ps.map exited with return code %s") % event.returncode)
+            
+            grass.try_remove(event.userData['instrFile'])
+            if event.userData['temp']:
+                grass.try_remove(event.userData['filename']) 
+            return
+        
+        if event.userData['pdfname']:
+            try:
+                proc = grass.Popen(['ps2pdf', '-dPDFSETTINGS=/prepress', '-r1200', 
+                                    event.userData['filename'], event.userData['pdfname']])
+                
+                ret = proc.wait()                        
+                if ret > 0:
+                    GMessage(parent = self,
+                             message = _("ps2pdf exited with return code %s") % ret)
+
+            except OSError, e:
+                GError(parent = self,
+                       message = _("Program ps2pdf is not available. Please install it to create PDF.\n\n %s") % e)
+                
+        # show preview only when user doesn't want to create ps or pdf 
+        if havePILImage and event.userData['temp'] and not event.userData['pdfname']:
+            RunCommand('g.region', cols = event.userData['regionOld']['cols'], rows = event.userData['regionOld']['rows'])
+## wx.BusyInfo does not display the message
+##            busy = wx.BusyInfo(message = "Generating preview, wait please", parent = self)
+
+            try:
+                im = PILImage.open(event.userData['filename'])
+                if self.instruction[self.pageId]['Orientation'] == 'Landscape':
+                    im = im.rotate(270)
+                
+                im.save(self.imgName, format = 'PNG')
+                
+            except IOError, e:
+                GError(parent = self,
+                       message = _("Unable to generate preview. %s") % e)
+                return
+            
+                
+            rect = self.previewCanvas.ImageRect()
+            self.previewCanvas.image = wx.Image(self.imgName, wx.BITMAP_TYPE_PNG)
+            self.previewCanvas.DrawImage(rect = rect)
+            
+##            busy.Destroy()
+            self.SetStatusText(_('Preview generated'), 0)
+            self.book.SetSelection(1)
+            self.currentPage = 1
+        
+        grass.try_remove(event.userData['instrFile'])
+        if event.userData['temp']:
+            grass.try_remove(event.userData['filename'])
+        
+    def getFile(self, wildcard):
+        suffix = []
+        for filter in wildcard.split('|')[1::2]:
+            s = filter.strip('*').split('.')[1]
+            if s:
+                s = '.' + s
+            suffix.append(s)
+        raster = self.instruction.FindInstructionByType('raster')
+        if raster:
+            rasterId = raster.id 
+        else:
+            rasterId = None
+
+
+        if rasterId and self.instruction[rasterId]['raster']:
+            mapName = self.instruction[rasterId]['raster'].split('@')[0] + suffix[0]
+        else:
+            mapName = ''
+            
+        filename = ''
+        dlg = wx.FileDialog(self, message = _("Save file as"), defaultDir = "", 
+                            defaultFile = mapName, wildcard = wildcard,
+                            style = wx.CHANGE_DIR | wx.SAVE | wx.OVERWRITE_PROMPT)
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
+            suffix = suffix[dlg.GetFilterIndex()]
+            if not os.path.splitext(filename)[1]:
+                filename = filename + suffix
+            elif os.path.splitext(filename)[1] != suffix and suffix != '':
+                filename = os.path.splitext(filename)[0] + suffix
+            
+        dlg.Destroy()
+        return filename
+    
+    def OnInstructionFile(self, event):
+        filename = self.getFile(wildcard = "*.psmap|*.psmap|Text file(*.txt)|*.txt|All files(*.*)|*.*")        
+        if filename:    
+            instrFile = open(filename, "w")
+            instrFile.write(self.InstructionFile())
+            instrFile.close()   
+            
+    def OnLoadFile(self, event):
+        """!Load file and read instructions"""
+        #find file
+        filename = ''
+        dlg = wx.FileDialog(self, message = "Find instructions file", defaultDir = "", 
+                            defaultFile = '', wildcard = "All files (*.*)|*.*",
+                            style = wx.CHANGE_DIR|wx.OPEN)
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
+        dlg.Destroy()
+        if not filename:
+            return
+        # load instructions
+        readObjectId = []
+        readInstruction = Instruction(parent = self, objectsToDraw = readObjectId)
+        ok = readInstruction.Read(filename)
+        if not ok:
+            GMessage(_("Failed to read file %s.") % filename)
+        else:
+            self.instruction = self.canvas.instruction = readInstruction
+            self.objectId = self.canvas.objectId = readObjectId
+            self.pageId = self.canvas.pageId = self.instruction.FindInstructionByType('page').id
+            self.canvas.UpdateMapLabel()
+            self.canvas.dragId = -1
+            self.canvas.Clear()
+            self.canvas.SetPage()
+            #self.canvas.ZoomAll()
+            
+            self.DialogDataChanged(self.objectId)
+            
+    def OnPageSetup(self, event = None):
+        """!Specify paper size, margins and orientation"""
+        id = self.instruction.FindInstructionByType('page').id
+        dlg = PageSetupDialog(self, id = id, settings = self.instruction) 
+        dlg.CenterOnScreen()
+        val = dlg.ShowModal()
+        if val == wx.ID_OK:
+            self.canvas.SetPage()
+            self.getInitMap()
+            self.canvas.RecalculatePosition(ids = self.objectId)
+        dlg.Destroy()
+        
+    def OnPointer(self, event):
+        self.toolbar.OnTool(event)
+        self.mouse["use"] = "pointer"
+        self.canvas.SetCursor(self.cursors["default"])
+        self.previewCanvas.SetCursor(self.cursors["default"])
+        
+    def OnPan(self, event):
+        self.toolbar.OnTool(event)
+        self.mouse["use"] = "pan"
+        self.canvas.SetCursor(self.cursors["hand"])
+        self.previewCanvas.SetCursor(self.cursors["hand"])
+        
+    def OnZoomIn(self, event):
+        self.toolbar.OnTool(event)
+        self.mouse["use"] = "zoomin"
+        self.canvas.SetCursor(self.cursors["cross"])
+        self.previewCanvas.SetCursor(self.cursors["cross"])
+        
+    def OnZoomOut(self, event):
+        self.toolbar.OnTool(event)
+        self.mouse["use"] = "zoomout"
+        self.canvas.SetCursor(self.cursors["cross"])
+        self.previewCanvas.SetCursor(self.cursors["cross"])
+        
+    def OnZoomAll(self, event):
+        self.mouseOld = self.mouse['use']
+        if self.currentPage == 0:
+            self.cursorOld = self.canvas.GetCursor() 
+        else:
+            self.cursorOld = self.previewCanvas.GetCursor()
+            self.previewCanvas.GetCursor()
+        self.mouse["use"] = "zoomin"
+        if self.currentPage == 0:
+            self.canvas.ZoomAll()
+        else:
+            self.previewCanvas.ZoomAll()
+        self.mouse["use"] = self.mouseOld 
+        if self.currentPage == 0:
+            self.canvas.SetCursor(self.cursorOld)
+        else:
+            self.previewCanvas.SetCursor(self.cursorOld)
+        
+        
+    def OnAddMap(self, event, notebook = False):
+        """!Add or edit map frame"""
+        if event is not None:
+            if event.GetId() != self.toolbar.action['id']:
+                self.actionOld = self.toolbar.action['id']
+                self.mouseOld = self.mouse['use']
+                self.cursorOld = self.canvas.GetCursor()
+            self.toolbar.OnTool(event)
+        
+        if self.instruction.FindInstructionByType('map'):
+            mapId = self.instruction.FindInstructionByType('map').id
+        else: mapId = None
+        id = [mapId, None, None]
+        
+        if notebook:
+            if self.instruction.FindInstructionByType('vector'):
+                vectorId = self.instruction.FindInstructionByType('vector').id
+            else: vectorId = None
+            if self.instruction.FindInstructionByType('raster'):
+                rasterId = self.instruction.FindInstructionByType('raster').id
+            else: rasterId = None
+            id[1] = rasterId
+            id[2] = vectorId
+        
+        
+        if mapId: # map exists
+            
+            self.toolbar.ToggleTool(self.actionOld, True)
+            self.toolbar.ToggleTool(self.toolbar.action['id'], False)
+            self.toolbar.action['id'] = self.actionOld
+            try:
+                self.canvas.SetCursor(self.cursorOld) 
+            except AttributeError:
+                pass
+            
+##            dlg = MapDialog(parent = self, id  = id, settings = self.instruction,
+##                            notebook = notebook)
+##            dlg.ShowModal()  
+            if notebook:
+                #check map, raster, vector and save, destroy them
+                if 'map' in self.openDialogs:
+                    self.openDialogs['map'].OnOK(event = None)
+                if 'raster' in self.openDialogs:
+                    self.openDialogs['raster'].OnOK(event = None)
+                if 'vector' in self.openDialogs:
+                    self.openDialogs['vector'].OnOK(event = None)
+                    
+                if 'mapNotebook' not in self.openDialogs:
+                    dlg = MapDialog(parent = self, id  = id, settings = self.instruction,
+                                    notebook = notebook)
+                    self.openDialogs['mapNotebook'] = dlg
+                self.openDialogs['mapNotebook'].Show()
+            else:
+                if 'mapNotebook' in self.openDialogs:
+                    self.openDialogs['mapNotebook'].notebook.ChangeSelection(0)
+                else:
+                    if 'map' not in self.openDialogs:
+                        dlg = MapDialog(parent = self, id  = id, settings = self.instruction,
+                                        notebook = notebook)
+                        self.openDialogs['map'] = dlg
+                    self.openDialogs['map'].Show()
+                    
+
+        else:    # sofar no map
+            self.mouse["use"] = "addMap"
+            self.canvas.SetCursor(self.cursors["cross"])
+            if self.currentPage == 1:
+                self.book.SetSelection(0)
+                self.currentPage = 0
+                
+    def OnAddRaster(self, event):
+        """!Add raster map"""
+        if self.instruction.FindInstructionByType('raster'):
+            id = self.instruction.FindInstructionByType('raster').id
+        else: id = None
+        if self.instruction.FindInstructionByType('map'):
+            mapId = self.instruction.FindInstructionByType('map').id
+        else: mapId = None
+
+        if not id:
+            if not mapId:
+                GMessage(message = _("Please, create map frame first."))
+                return
+            
+##        dlg = RasterDialog(self, id = id, settings = self.instruction)
+##        dlg.ShowModal()
+        if 'mapNotebook' in self.openDialogs:
+            self.openDialogs['mapNotebook'].notebook.ChangeSelection(1)
+        else:
+            if 'raster' not in self.openDialogs:
+                dlg = RasterDialog(self, id = id, settings = self.instruction)
+                self.openDialogs['raster'] = dlg
+            self.openDialogs['raster'].Show()
+            
+    def OnAddVect(self, event):
+        """!Add vector map"""
+        if self.instruction.FindInstructionByType('vector'):
+            id = self.instruction.FindInstructionByType('vector').id
+        else: id = None
+        if self.instruction.FindInstructionByType('map'):
+            mapId = self.instruction.FindInstructionByType('map').id
+        else: mapId = None
+        if not id:
+            if not mapId:
+                GMessage(message = _("Please, create map frame first."))
+                return
+            
+##        dlg = MainVectorDialog(self, id = id, settings = self.instruction)
+##        dlg.ShowModal()
+        if 'mapNotebook' in self.openDialogs:
+            self.openDialogs['mapNotebook'].notebook.ChangeSelection(2)
+        else:
+            if 'vector' not in self.openDialogs:
+                dlg =  MainVectorDialog(self, id = id, settings = self.instruction)
+                self.openDialogs['vector'] = dlg
+            self.openDialogs['vector'].Show()
+       
+    def OnAddScalebar(self, event):
+        """!Add scalebar"""
+        if projInfo()['proj'] == 'll':
+            GMessage(message = _("Scalebar is not appropriate for this projection"))
+            return
+        if self.instruction.FindInstructionByType('scalebar'):
+            id = self.instruction.FindInstructionByType('scalebar').id
+        else: id = None
+        
+        if 'scalebar' not in self.openDialogs:
+            dlg = ScalebarDialog(self, id = id, settings = self.instruction)
+            self.openDialogs['scalebar'] = dlg
+        self.openDialogs['scalebar'].Show()
+        
+    def OnAddLegend(self, event, page = 0):
+        """!Add raster or vector legend"""
+        if self.instruction.FindInstructionByType('rasterLegend'):
+            idR = self.instruction.FindInstructionByType('rasterLegend').id
+        else: idR = None
+        if self.instruction.FindInstructionByType('vectorLegend'):
+            idV = self.instruction.FindInstructionByType('vectorLegend').id
+        else: idV = None
+
+        if 'rasterLegend' not in self.openDialogs:    
+            dlg = LegendDialog(self, id = [idR, idV], settings = self.instruction, page = page)
+            self.openDialogs['rasterLegend'] = dlg
+            self.openDialogs['vectorLegend'] = dlg
+        self.openDialogs['rasterLegend'].notebook.ChangeSelection(page)
+        self.openDialogs['rasterLegend'].Show()
+
+    def OnAddMapinfo(self, event):
+        if self.instruction.FindInstructionByType('mapinfo'):
+            id = self.instruction.FindInstructionByType('mapinfo').id
+        else: id = None
+        
+        if 'mapinfo' not in self.openDialogs:
+            dlg = MapinfoDialog(self, id = id, settings = self.instruction)
+            self.openDialogs['mapinfo'] = dlg
+        self.openDialogs['mapinfo'].Show()
+        
+    def OnAddImage(self, event, id = None):
+        """!Show dialog for image adding and editing"""
+        position = None
+        if 'image' in self.openDialogs:
+            position = self.openDialogs['image'].GetPosition()
+            self.openDialogs['image'].OnApply(event = None)
+            self.openDialogs['image'].Destroy()
+        dlg = ImageDialog(self, id = id, settings = self.instruction)
+        self.openDialogs['image'] = dlg 
+        if position: 
+            dlg.SetPosition(position)
+        dlg.Show()
+        
+    def OnAddNorthArrow(self, event, id = None):
+        """!Show dialog for north arrow adding and editing"""
+        if self.instruction.FindInstructionByType('northArrow'):
+            id = self.instruction.FindInstructionByType('northArrow').id
+        else: id = None
+        
+        if 'northArrow' not in self.openDialogs:
+            dlg = NorthArrowDialog(self, id = id, settings = self.instruction)
+            self.openDialogs['northArrow'] = dlg
+        self.openDialogs['northArrow'].Show()
+        
+    def OnAddText(self, event, id = None):
+        """!Show dialog for text adding and editing"""
+        position = None
+        if 'text' in self.openDialogs:
+            position = self.openDialogs['text'].GetPosition()
+            self.openDialogs['text'].OnApply(event = None)
+            self.openDialogs['text'].Destroy()
+        dlg = TextDialog(self, id = id, settings = self.instruction)
+        self.openDialogs['text'] = dlg 
+        if position: 
+            dlg.SetPosition(position)
+        dlg.Show()
+        
+    def OnAddPoint(self, event):
+        """!Add point action selected"""
+        self.mouse["use"] = "addPoint"
+        self.canvas.SetCursor(self.cursors["cross"])
+        
+    def AddPoint(self, id = None, coordinates = None):
+        """!Add point and open property dialog.
+
+        @param id id point id (None if creating new point)
+        @param coordinates coordinates of new point
+        """
+        position = None
+        if 'point' in self.openDialogs:
+            position = self.openDialogs['point'].GetPosition()
+            self.openDialogs['point'].OnApply(event = None)
+            self.openDialogs['point'].Destroy()
+        dlg = PointDialog(self, id = id, settings = self.instruction,
+                          coordinates = coordinates)
+        self.openDialogs['point'] = dlg
+        if position: 
+            dlg.SetPosition(position)
+        if coordinates:
+            dlg.OnApply(event = None)
+        dlg.Show()
+        
+    def OnAddLine(self, event):
+        """!Add line action selected"""
+        self.mouse["use"] = "addLine"
+        self.canvas.SetCursor(self.cursors["cross"])
+
+    def AddLine(self, id = None, coordinates = None):
+        """!Add line and open property dialog.
+        
+        @param id id line id (None if creating new line)
+        @param coordinates coordinates of new line
+        """
+        position = None
+        if 'line' in self.openDialogs:
+            position = self.openDialogs['line'].GetPosition()
+            self.openDialogs['line'].OnApply(event = None)
+            self.openDialogs['line'].Destroy()
+        dlg = RectangleDialog(self, id = id, settings = self.instruction,
+                              type = 'line', coordinates = coordinates)
+        self.openDialogs['line'] = dlg
+        if position: 
+            dlg.SetPosition(position)
+        if coordinates:
+            dlg.OnApply(event = None)
+        dlg.Show()
+
+    def OnAddRectangle(self, event):
+        """!Add rectangle action selected"""
+        self.mouse["use"] = "addRectangle"
+        self.canvas.SetCursor(self.cursors["cross"])
+
+    def AddRectangle(self, id = None, coordinates = None):
+        """!Add rectangle and open property dialog.
+        
+        @param id id rectangle id (None if creating new rectangle)
+        @param coordinates coordinates of new rectangle
+        """
+        position = None
+        if 'rectangle' in self.openDialogs:
+            position = self.openDialogs['rectangle'].GetPosition()
+            self.openDialogs['rectangle'].OnApply(event = None)
+            self.openDialogs['rectangle'].Destroy()
+        dlg = RectangleDialog(self, id = id, settings = self.instruction,
+                              type = 'rectangle', coordinates = coordinates)
+        self.openDialogs['rectangle'] = dlg
+        if position: 
+            dlg.SetPosition(position)
+        if coordinates:
+            dlg.OnApply(event = None)
+        dlg.Show()
+
+    def getModifiedTextBounds(self, x, y, textExtent, rotation):
+        """!computes bounding box of rotated text, not very precisely"""
+        w, h = textExtent
+        rotation = float(rotation)/180*pi
+        H = float(w) * sin(rotation)
+        W = float(w) * cos(rotation)
+        X, Y = x, y
+        if pi/2 < rotation <= 3*pi/2:
+            X = x + W 
+        if 0 < rotation < pi:
+            Y = y - H
+        if rotation == 0:
+            return wx.Rect(x,y, *textExtent)
+        else:
+            return wx.Rect(X, Y, abs(W), abs(H)).Inflate(h,h) 
+
+    def makePSFont(self, textDict):
+        """!creates a wx.Font object from selected postscript font. To be
+        used for estimating bounding rectangle of text"""
+        
+        fontsize = textDict['fontsize'] * self.canvas.currScale
+        fontface = textDict['font'].split('-')[0]
+        try:
+            fontstyle = textDict['font'].split('-')[1]
+        except IndexError:
+            fontstyle = ''
+        
+        if fontface == "Times":
+            family = wx.FONTFAMILY_ROMAN
+            face = "times"
+        elif fontface == "Helvetica":
+            family = wx.FONTFAMILY_SWISS
+            face = 'helvetica'
+        elif fontface == "Courier":
+            family = wx.FONTFAMILY_TELETYPE
+            face = 'courier'
+        else:
+            family = wx.FONTFAMILY_DEFAULT
+            face = ''
+            
+        style = wx.FONTSTYLE_NORMAL
+        weight = wx.FONTWEIGHT_NORMAL
+            
+        if 'Oblique' in fontstyle:
+            style =  wx.FONTSTYLE_SLANT
+            
+        if 'Italic' in fontstyle:
+            style =  wx.FONTSTYLE_ITALIC
+            
+        if 'Bold' in fontstyle:
+            weight = wx.FONTWEIGHT_BOLD
+        
+        try:
+            fn = wx.Font(pointSize = fontsize, family = family, style = style,
+                         weight = weight, face = face)
+        except:
+            fn = wx.Font(pointSize = fontsize, family = wx.FONTFAMILY_DEFAULT, 
+                         style = wx.FONTSTYLE_NORMAL, weight = wx.FONTWEIGHT_NORMAL)
+
+        return fn
+       
+       
+    def getTextExtent(self, textDict):
+        """!Estimates bounding rectangle of text"""
+        #fontsize = str(fontsize if fontsize >= 4 else 4)
+        dc = wx.ClientDC(self) # dc created because of method GetTextExtent, which pseudoDC lacks
+       
+        fn = self.makePSFont(textDict)
+
+        try:
+            dc.SetFont(fn)
+            w,h,lh = dc.GetMultiLineTextExtent(textDict['text'])
+            return (w,h)
+        except:
+            return (0,0)
+    
+    def getInitMap(self):
+        """!Create default map frame when no map is selected, needed for coordinates in map units"""
+        instrFile = grass.tempfile()
+        instrFileFd = open(instrFile, mode = 'w')
+        instrFileFd.write(self.InstructionFile())
+        instrFileFd.flush()
+        instrFileFd.close()
+        
+        page = self.instruction.FindInstructionByType('page')
+        mapInitRect = GetMapBounds(instrFile, portrait = (page['Orientation'] == 'Portrait'))
+        grass.try_remove(instrFile)
+        
+        region = grass.region()
+        units = UnitConversion(self)
+        realWidth = units.convert(value = abs(region['w'] - region['e']), fromUnit = 'meter', toUnit = 'inch')
+        scale = mapInitRect.Get()[2]/realWidth  
+        
+        initMap = self.instruction.FindInstructionByType('initMap')
+        if initMap:
+            id = initMap.id 
+        else:
+            id = None
+
+        
+        if not id:
+            id = wx.NewId()
+            initMap = InitMap(id)
+            self.instruction.AddInstruction(initMap)
+        self.instruction[id].SetInstruction(dict(rect = mapInitRect, scale = scale))
+
+    def OnDelete(self, event):
+        if self.canvas.dragId != -1 and self.currentPage == 0:
+            if self.instruction[self.canvas.dragId].type == 'map':
+                self.deleteObject(self.canvas.dragId)
+                self.getInitMap()
+                self.canvas.RecalculateEN()
+            else:
+                self.deleteObject(self.canvas.dragId)   
+    
+    def deleteObject(self, id):
+        """!Deletes object, his id and redraws"""
+        #delete from canvas
+        self.canvas.pdcObj.RemoveId(id)
+        if id == self.canvas.dragId:
+            self.canvas.pdcTmp.RemoveAll()
+            self.canvas.dragId = -1
+        self.canvas.Refresh()
+        
+        # delete from instructions
+        del self.instruction[id]
+
+    def DialogDataChanged(self, id):
+        ids = id
+        if type(id) == int:
+            ids = [id]
+        for id in ids:
+            itype = self.instruction[id].type
+            
+            if itype in ('scalebar', 'mapinfo', 'image'):
+                drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
+                self.canvas.UpdateLabel(itype = itype, id = id)
+                self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
+                                 pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
+                self.canvas.RedrawSelectBox(id)
+            if itype == 'northArrow':
+                self.canvas.UpdateLabel(itype = itype, id = id)
+                drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
+                self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
+                                 pdc = self.canvas.pdcObj, drawid = id, pdctype = 'bitmap', bb = drawRectangle)
+                self.canvas.RedrawSelectBox(id)
+
+            if itype in ('point', 'line', 'rectangle'):
+                drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
+                # coords only for line
+                coords = None
+                if itype == 'line':
+                    point1 = self.instruction[id]['where'][0]
+                    point2 = self.instruction[id]['where'][1]
+                    point1Coords = self.canvas.CanvasPaperCoordinates(rect = Rect2DPS(point1, (0, 0)), canvasToPaper = False)[:2]
+                    point2Coords = self.canvas.CanvasPaperCoordinates(rect = Rect2DPS(point2, (0, 0)), canvasToPaper = False)[:2]
+                    coords = (point1Coords, point2Coords)
+
+                # fill color is not in line
+                fcolor = None
+                if 'fcolor' in self.instruction[id].GetInstruction():
+                    fcolor = self.instruction[id]['fcolor']
+                # width is not in point
+                width = None
+                if 'width' in self.instruction[id].GetInstruction():
+                    width = self.instruction[id]['width']
+
+                self.canvas.DrawGraphics(drawid = id, color = self.instruction[id]['color'], shape = itype,
+                                       fcolor = fcolor, width = width, bb = drawRectangle, lineCoords = coords)
+
+                self.canvas.RedrawSelectBox(id)
+
+            if itype == 'text':
+                
+                if self.instruction[id]['rotate']:
+                    rot = float(self.instruction[id]['rotate']) 
+                else:
+                    rot = 0
+
+                extent = self.getTextExtent(textDict = self.instruction[id].GetInstruction())
+                rect = Rect2DPS(self.instruction[id]['where'], (0, 0))
+                self.instruction[id]['coords'] = list(self.canvas.CanvasPaperCoordinates(rect = rect, canvasToPaper = False)[:2])
+                
+                #computes text coordinates according to reference point, not precisely
+                if self.instruction[id]['ref'].split()[0] == 'lower':
+                    self.instruction[id]['coords'][1] -= extent[1]
+                elif self.instruction[id]['ref'].split()[0] == 'center':
+                    self.instruction[id]['coords'][1] -= extent[1]/2
+                if self.instruction[id]['ref'].split()[1] == 'right':
+                    self.instruction[id]['coords'][0] -= extent[0] * cos(rot/180*pi)
+                    self.instruction[id]['coords'][1] += extent[0] * sin(rot/180*pi)
+                elif self.instruction[id]['ref'].split()[1] == 'center':
+                    self.instruction[id]['coords'][0] -= extent[0]/2 * cos(rot/180*pi)
+                    self.instruction[id]['coords'][1] += extent[0]/2 * sin(rot/180*pi)
+                    
+                self.instruction[id]['coords'][0] += self.instruction[id]['xoffset']
+                self.instruction[id]['coords'][1] -= self.instruction[id]['yoffset']
+                coords = self.instruction[id]['coords']
+                self.instruction[id]['rect'] = bounds = self.getModifiedTextBounds(coords[0], coords[1], extent, rot)
+                self.canvas.DrawRotText(pdc = self.canvas.pdcObj, drawId = id,
+                                        textDict = self.instruction[id].GetInstruction(),
+                                        coords = coords, bounds = bounds)
+                self.canvas.RedrawSelectBox(id)
+                
+            if itype in ('map', 'vector', 'raster'):
+                
+                if itype == 'raster':#set resolution
+                    info = grass.raster_info(self.instruction[id]['raster'])
+                    RunCommand('g.region', nsres = info['nsres'], ewres = info['ewres'])
+                    # change current raster in raster legend
+                    
+                if 'rasterLegend' in self.openDialogs:
+                    self.openDialogs['rasterLegend'].updateDialog()
+                id = self.instruction.FindInstructionByType('map').id
+                
+                #check resolution
+                if itype == 'raster':
+                    SetResolution(dpi = self.instruction[id]['resolution'], 
+                                  width = self.instruction[id]['rect'].width,
+                                  height = self.instruction[id]['rect'].height)   
+                rectCanvas = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'],
+                                                                canvasToPaper = False)
+                self.canvas.RecalculateEN()
+                self.canvas.UpdateMapLabel()
+                
+                self.canvas.Draw(pen = self.pen['map'], brush = self.brush['map'],
+                                 pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = rectCanvas)
+                # redraw select box  
+                self.canvas.RedrawSelectBox(id)
+                self.canvas.pdcTmp.RemoveId(self.canvas.idZoomBoxTmp)
+                # redraw to get map to the bottom layer
+                #self.canvas.Zoom(zoomFactor = 1, view = (0, 0))
+                
+            if itype == 'rasterLegend':
+                if self.instruction[id]['rLegend']:
+                    self.canvas.UpdateLabel(itype = itype, id = id)
+                    drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
+                    self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
+                                     pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
+                    self.canvas.RedrawSelectBox(id)
+                else:
+                    self.deleteObject(id)
+                    
+            if itype == 'vectorLegend':
+                if not self.instruction.FindInstructionByType('vector'):
+                    self.deleteObject(id)
+                elif self.instruction[id]['vLegend']:
+                    self.canvas.UpdateLabel(itype = itype, id = id)
+                    drawRectangle = self.canvas.CanvasPaperCoordinates(rect = self.instruction[id]['rect'], canvasToPaper = False)
+                    self.canvas.Draw(pen = self.pen[itype], brush = self.brush[itype],
+                                     pdc = self.canvas.pdcObj, drawid = id, pdctype = 'rectText', bb = drawRectangle)
+                    self.canvas.RedrawSelectBox(id)
+
+                else:
+                    self.deleteObject(id)
+                
+    def OnPageChanged(self, event):
+        """!Flatnotebook page has changed"""
+        self.currentPage = self.book.GetPageIndex(self.book.GetCurrentPage())
+        
+        
+    def OnPageChanging(self, event):
+        """!Flatnotebook page is changing"""
+        if self.currentPage == 0 and self.mouse['use'] == 'addMap':
+            event.Veto()
+
+    def OnHelp(self, event):
+        """!Show help"""
+        if self.parent and self.parent.GetName() == 'LayerManager':
+            log = self.parent.GetLogWindow()
+            log.RunCmd(['g.manual',
+                        'entry=wxGUI.PsMap'])
+        else:
+            RunCommand('g.manual',
+                       quiet = True,
+                       entry = 'wxGUI.PsMap')
+        
+    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 Cartographic Composer'))
+        info.SetWebSite('http://grass.osgeo.org')
+        info.SetDescription(_('(C) 2011 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 OnCloseWindow(self, event):
+        """!Close window"""
+        try:
+            os.remove(self.imgName)
+        except OSError:
+            pass
+        grass.set_raise_on_error(False)
+        self.Destroy()
+
+
+
+class PsMapBufferedWindow(wx.Window):
+    """!A buffered window class.
+    
+    @param parent parent window
+    @param kwargs other wx.Window parameters
+    """
+    def __init__(self, parent, id =  wx.ID_ANY,
+                 style = wx.NO_FULL_REPAINT_ON_RESIZE,
+                 **kwargs):
+        wx.Window.__init__(self, parent, id = id, style = style)
+        self.parent = parent
+    
+        self.FitInside()
+        
+        # store an off screen empty bitmap for saving to file
+        self._buffer = None
+        # indicates whether or not a resize event has taken place
+        self.resize = False 
+        
+        self.mouse = kwargs['mouse']
+        self.cursors = kwargs['cursors']
+        self.preview = kwargs['preview']
+        self.pen = kwargs['pen']
+        self.brush = kwargs['brush']
+        
+        if kwargs.has_key('instruction'):
+            self.instruction = kwargs['instruction']
+        if kwargs.has_key('openDialogs'):
+            self.openDialogs = kwargs['openDialogs']
+        if kwargs.has_key('pageId'):
+            self.pageId = kwargs['pageId']
+        if kwargs.has_key('objectId'):
+            self.objectId = kwargs['objectId']
+        
+        
+        #labels
+        self.itemLabelsDict = { 'map': 'MAP FRAME',
+                                'rasterLegend': 'RASTER LEGEND',
+                                'vectorLegend': 'VECTOR LEGEND',
+                                'mapinfo': 'MAP INFO',
+                                'scalebar': 'SCALE BAR',
+                                'image': 'IMAGE',
+                                'northArrow': 'NORTH ARROW'}
+        self.itemLabels = {}
+        
+        # define PseudoDC
+        self.pdc = wx.PseudoDC()
+        self.pdcObj = wx.PseudoDC()
+        self.pdcPaper = wx.PseudoDC()
+        self.pdcTmp = wx.PseudoDC()
+        self.pdcImage = wx.PseudoDC()
+        dc = wx.ClientDC(self)
+        self.font = dc.GetFont()
+        
+        self.SetClientSize((700,510))#?
+        self._buffer = wx.EmptyBitmap(*self.GetClientSize())
+        
+        self.idBoxTmp = wx.NewId()
+        self.idZoomBoxTmp = wx.NewId()
+        self.idResizeBoxTmp = wx.NewId()
+        self.idLinePointsTmp = (wx.NewId(), wx.NewId()) # ids of marks for moving line vertices
+
+        self.resizeBoxSize = wx.Size(8, 8)
+        
+        
+
+        self.dragId = -1
+        
+        if self.preview:
+            self.image = None
+            self.imageId = 2000
+            self.imgName = self.parent.imgName
+            
+            
+            
+        self.currScale = None
+        
+        self.Clear()
+        
+        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None)
+        
+        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_MOUSE_EVENTS, self.OnMouse)
+        self.Bind(wx.EVT_MOUSE_EVENTS, self.MouseActions)
+
+
+    def Clear(self):
+        """!Clear canvas and set paper
+        """
+        bg = wx.LIGHT_GREY_BRUSH
+        self.pdcPaper.BeginDrawing()
+        self.pdcPaper.SetBackground(bg)
+        self.pdcPaper.Clear()
+        self.pdcPaper.EndDrawing()
+        
+        self.pdcObj.RemoveAll()
+        self.pdcTmp.RemoveAll()
+        
+
+
+        if not self.preview:
+            self.SetPage()
+
+    
+    def CanvasPaperCoordinates(self, rect, canvasToPaper = True):
+        """!Converts canvas (pixel) -> paper (inch) coordinates and size and vice versa"""
+        
+        units = UnitConversion(self)
+        
+        fromU = 'pixel'
+        toU = 'inch'
+        pRect = self.pdcPaper.GetIdBounds(self.pageId)
+        pRectx, pRecty = pRect.x, pRect.y 
+        scale = 1/self.currScale
+        if not canvasToPaper: # paper -> canvas
+            fromU = 'inch'
+            toU = 'pixel'
+            scale = self.currScale
+            pRectx = units.convert(value =  - pRect.x, fromUnit = 'pixel', toUnit = 'inch' ) /scale #inch, real, negative
+            pRecty = units.convert(value =  - pRect.y, fromUnit = 'pixel', toUnit = 'inch' ) /scale 
+        Width = units.convert(value = rect.GetWidth(), fromUnit = fromU, toUnit = toU) * scale
+        Height = units.convert(value = rect.GetHeight(), fromUnit = fromU, toUnit = toU) * scale
+        X = units.convert(value = (rect.GetX() - pRectx), fromUnit = fromU, toUnit = toU) * scale
+        Y = units.convert(value = (rect.GetY() - pRecty), fromUnit = fromU, toUnit = toU) * scale
+
+        return Rect2D(X, Y, Width, Height)
+
+    
+    
+    def SetPage(self):
+        """!Sets and changes page, redraws paper"""
+        
+        page = self.instruction[self.pageId]
+        if not page:
+            page = PageSetup(id = self.pageId)
+            self.instruction.AddInstruction(page)
+        
+        ppi = wx.ClientDC(self).GetPPI()
+        cW, cH = self.GetClientSize()
+        pW, pH = page['Width']*ppi[0], page['Height']*ppi[1]
+
+        if self.currScale is None:
+            self.currScale = min(cW/pW, cH/pH)
+        pW = pW * self.currScale
+        pH = pH * self.currScale
+        
+        x = cW/2 - pW/2
+        y = cH/2 - pH/2
+        self.DrawPaper(wx.Rect(x, y, pW, pH))
+
+
+    def modifyRectangle(self, r):
+        """! Recalculates rectangle not to have negative size"""
+        if r.GetWidth() < 0:
+            r.SetX(r.GetX() + r.GetWidth())
+        if r.GetHeight() < 0:
+            r.SetY(r.GetY() + r.GetHeight())
+        r.SetWidth(abs(r.GetWidth()))
+        r.SetHeight(abs(r.GetHeight()))
+        return r 
+    
+    def RecalculateEN(self):
+        """!Recalculate east and north for texts (eps, points) after their or map's movement"""
+        try:
+            mapId = self.instruction.FindInstructionByType('map').id
+        except AttributeError:
+            mapId = self.instruction.FindInstructionByType('initMap').id
+        
+        for itemType in ('text', 'image', 'northArrow', 'point', 'line', 'rectangle'):
+            items = self.instruction.FindInstructionByType(itemType, list = True)
+            for item in items:
+                instr = self.instruction[item.id]
+                if itemType in ('line', 'rectangle'):
+                    if itemType == 'line':
+                        e1, n1 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['where'][0][0],
+                                                     y = instr['where'][0][1], paperToMap = True)
+                        e2, n2 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['where'][1][0],
+                                                     y = instr['where'][1][1], paperToMap = True)
+                    else: 
+                        e1, n1 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['rect'].GetLeft(),
+                                                     y = instr['rect'].GetTop(), paperToMap = True)
+                        e2, n2 = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['rect'].GetRight(),
+                                                     y = instr['rect'].GetBottom(), paperToMap = True)
+                    instr['east1'] = e1
+                    instr['north1'] = n1
+                    instr['east2'] = e2
+                    instr['north2'] = n2
+                else:
+                    e, n = PaperMapCoordinates(mapInstr = self.instruction[mapId], x = instr['where'][0],
+                                               y = instr['where'][1], paperToMap = True)
+                    instr['east'], instr['north'] = e, n
+                
+    def OnPaint(self, event):
+        """!Draw pseudo DC to buffer
+        """
+        if not self._buffer:
+            return
+        dc = wx.BufferedPaintDC(self, self._buffer)
+        # use PrepareDC to set position correctly
+        self.PrepareDC(dc)
+        
+        dc.SetBackground(wx.LIGHT_GREY_BRUSH)
+        dc.Clear()
+        
+        # draw paper
+        if not self.preview:
+            self.pdcPaper.DrawToDC(dc)
+        # draw to the DC using the calculated clipping rect
+
+        rgn = self.GetUpdateRegion()
+        
+        if not self.preview:
+            self.pdcObj.DrawToDCClipped(dc, rgn.GetBox())
+        else: 
+            self.pdcImage.DrawToDCClipped(dc, rgn.GetBox())
+        self.pdcTmp.DrawToDCClipped(dc, rgn.GetBox())
+        
+    def MouseActions(self, event):
+        """!Mouse motion and button click notifier
+        """
+        # 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)
+        
+        elif event.Moving():
+            self.OnMouseMoving(event)
+                
+    def OnMouseWheel(self, event):
+        """!Mouse wheel scrolled.
+
+        Changes zoom."""
+        if not UserSettings.Get(group = 'display',
+                                key = 'mouseWheelZoom',
+                                subkey = 'enabled'):
+            event.Skip()
+            return
+
+        zoom = event.GetWheelRotation()
+        oldUse = self.mouse['use']
+        self.mouse['begin'] = event.GetPosition()
+        
+        if UserSettings.Get(group = 'display',
+                            key = 'mouseWheelZoom',
+                            subkey = 'selection'):
+            zoom *= -1
+            
+        if zoom > 0:
+            self.mouse['use'] = 'zoomin'
+        else:
+            self.mouse['use'] = 'zoomout'
+            
+        zoomFactor, view = self.ComputeZoom(wx.Rect(0, 0, 0, 0))
+        self.Zoom(zoomFactor, view)
+        self.mouse['use'] = oldUse
+
+    def OnMouseMoving(self, event):
+        """!Mouse cursor moving.
+
+        Change cursor when moving over resize marker.
+        """
+        if self.mouse['use'] in ('pointer', 'resize'):
+            pos = event.GetPosition()
+            foundResize = self.pdcTmp.FindObjects(pos[0], pos[1])
+            if foundResize and foundResize[0] in (self.idResizeBoxTmp,) + self.idLinePointsTmp:
+                self.SetCursor(self.cursors["sizenwse"])
+                self.parent.SetStatusText(_('Click and drag to resize object'), 0)
+            else:
+                self.parent.SetStatusText('', 0)
+                self.SetCursor(self.cursors["default"])
+                
+    def OnLeftDown(self, event):
+        """!Left mouse button pressed.
+
+        Select objects, redraw, prepare for moving/resizing.
+        """
+        self.mouse['begin'] = event.GetPosition()
+        self.begin = self.mouse['begin']
+        
+        # select
+        if self.mouse['use'] == 'pointer':
+            found = self.pdcObj.FindObjects(self.mouse['begin'][0], self.mouse['begin'][1])
+            foundResize = self.pdcTmp.FindObjects(self.mouse['begin'][0], self.mouse['begin'][1])
+
+            if foundResize and foundResize[0] in (self.idResizeBoxTmp,) + self.idLinePointsTmp:
+                self.mouse['use'] = 'resize'
+                
+                # when resizing, proportions match region
+                if self.instruction[self.dragId].type == 'map':
+                    self.constraint = False
+                    self.mapBounds = self.pdcObj.GetIdBounds(self.dragId)
+                    if self.instruction[self.dragId]['scaleType'] in (0, 1, 2):
+                        self.constraint = True
+                        self.mapBounds = self.pdcObj.GetIdBounds(self.dragId)
+
+                if self.instruction[self.dragId].type == 'line':
+                    self.currentLinePoint = self.idLinePointsTmp.index(foundResize[0])
+                
+            elif found:
+                self.dragId = found[0]
+                self.RedrawSelectBox(self.dragId)
+                if self.instruction[self.dragId].type not in ('map', 'rectangle'):
+                    self.pdcTmp.RemoveId(self.idResizeBoxTmp)
+                    self.Refresh()
+                if self.instruction[self.dragId].type != 'line':
+                    for id in self.idLinePointsTmp:
+                        self.pdcTmp.RemoveId(id)
+                    self.Refresh()
+                    
+            else:
+                self.dragId = -1
+                self.pdcTmp.RemoveId(self.idBoxTmp)
+                self.pdcTmp.RemoveId(self.idResizeBoxTmp)
+                for id in self.idLinePointsTmp:
+                    self.pdcTmp.RemoveId(id)
+                self.Refresh()
+
+    def OnLeftUp(self, event):
+        """!Left mouse button released.
+
+        Recalculate zooming/resizing/moving and redraw.
+        """
+        # zoom in, zoom out
+        if self.mouse['use'] in ('zoomin','zoomout'):
+            zoomR = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp)
+            self.pdcTmp.RemoveId(self.idZoomBoxTmp)
+            self.Refresh()
+            zoomFactor, view = self.ComputeZoom(zoomR)
+            self.Zoom(zoomFactor, view)
+
+        # draw map frame
+        if self.mouse['use'] == 'addMap':
+            rectTmp = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp)
+            # too small rectangle, it's usually some mistake
+            if rectTmp.GetWidth() < 20 or rectTmp.GetHeight() < 20:
+                self.pdcTmp.RemoveId(self.idZoomBoxTmp)
+                self.Refresh()
+                return
+            rectPaper = self.CanvasPaperCoordinates(rect = rectTmp, canvasToPaper = True)
+
+            dlg = MapDialog(parent = self.parent, id = [None, None, None], settings = self.instruction, 
+                            rect = rectPaper)
+            self.openDialogs['map'] = dlg
+            self.openDialogs['map'].Show()
+            
+            self.mouse['use'] = self.parent.mouseOld
+
+            self.SetCursor(self.parent.cursorOld)
+            self.parent.toolbar.ToggleTool(self.parent.actionOld, True)
+            self.parent.toolbar.ToggleTool(self.parent.toolbar.action['id'], False)
+            self.parent.toolbar.action['id'] = self.parent.actionOld
+            return
+
+        # resize resizable objects (map, line, rectangle)
+        if self.mouse['use'] == 'resize':
+            mapObj = self.instruction.FindInstructionByType('map')
+            if not mapObj:
+                mapObj = self.instruction.FindInstructionByType('initMap')
+            mapId = mapObj.id
+            
+            if self.dragId == mapId:
+                # necessary to change either map frame (scaleType 0,1,2) or region (scaletype 3)
+                newRectCanvas = self.pdcObj.GetIdBounds(mapId)
+                newRectPaper = self.CanvasPaperCoordinates(rect = newRectCanvas, canvasToPaper = True)
+                self.instruction[mapId]['rect'] = newRectPaper
+                
+                if self.instruction[mapId]['scaleType'] in (0, 1, 2):
+                    if self.instruction[mapId]['scaleType'] == 0:
+                        
+                        scale, foo, rect = AutoAdjust(self, scaleType = 0,
+                                                      map = self.instruction[mapId]['map'],
+                                                      mapType = self.instruction[mapId]['mapType'], 
+                                                      rect = self.instruction[mapId]['rect'])
+                        
+                    elif self.instruction[mapId]['scaleType'] == 1:
+                        scale, foo, rect = AutoAdjust(self, scaleType = 1,
+                                                      region = self.instruction[mapId]['region'],
+                                                      rect = self.instruction[mapId]['rect'])
+                    else:
+                        scale, foo, rect = AutoAdjust(self, scaleType = 2,
+                                                      rect = self.instruction[mapId]['rect'])
+                    self.instruction[mapId]['rect'] = rect
+                    self.instruction[mapId]['scale'] = scale
+                    
+                    rectCanvas = self.CanvasPaperCoordinates(rect = rect, canvasToPaper = False)
+                    self.Draw(pen = self.pen['map'], brush = self.brush['map'],
+                              pdc = self.pdcObj, drawid = mapId, pdctype = 'rectText', bb = rectCanvas)
+                    
+                elif self.instruction[mapId]['scaleType'] == 3:
+                    ComputeSetRegion(self, mapDict = self.instruction[mapId].GetInstruction())
+                #check resolution
+                SetResolution(dpi = self.instruction[mapId]['resolution'],
+                              width = self.instruction[mapId]['rect'].width,
+                              height = self.instruction[mapId]['rect'].height)
+                
+                self.RedrawSelectBox(mapId)
+                self.Zoom(zoomFactor = 1, view = (0, 0))
+
+            elif self.instruction[self.dragId].type == 'line':
+                points = self.instruction[self.dragId]['where']
+                self.instruction[self.dragId]['rect'] = Rect2DPP(points[0], points[1])
+                self.RecalculatePosition(ids = [self.dragId])
+
+            elif self.instruction[self.dragId].type == 'rectangle':
+                self.RecalculatePosition(ids = [self.dragId])
+
+            self.mouse['use'] = 'pointer'
+            
+        # recalculate the position of objects after dragging
+        if self.mouse['use'] in ('pointer', 'resize') and self.dragId != -1:
+            if self.mouse['begin'] != event.GetPosition(): #for double click
+                
+                self.RecalculatePosition(ids = [self.dragId])
+                if self.instruction[self.dragId].type in self.openDialogs:
+                    self.openDialogs[self.instruction[self.dragId].type].updateDialog()
+        
+        elif self.mouse['use'] in ('addPoint', 'addLine', 'addRectangle'):
+            endCoordinates = self.CanvasPaperCoordinates(rect = wx.Rect(event.GetX(), event.GetY(), 0, 0),
+                                                         canvasToPaper = True)[:2]
+
+            diffX = event.GetX() - self.mouse['begin'][0]
+            diffY = event.GetY() - self.mouse['begin'][1]
+
+            if self.mouse['use'] == 'addPoint':
+                self.parent.AddPoint(coordinates = endCoordinates)
+            elif self.mouse['use'] in ('addLine', 'addRectangle'):
+                # not too small lines/rectangles
+                if sqrt(diffX * diffX + diffY * diffY) < 5:
+                    self.pdcTmp.RemoveId(self.idZoomBoxTmp)
+                    self.Refresh()
+                    return
+
+                beginCoordinates = self.CanvasPaperCoordinates(rect = wx.Rect(self.mouse['begin'][0],
+                                                                              self.mouse['begin'][1], 0, 0),
+                                                               canvasToPaper = True)[:2]
+                if self.mouse['use'] == 'addLine':
+                    self.parent.AddLine(coordinates = [beginCoordinates, endCoordinates])
+                else:
+                    self.parent.AddRectangle(coordinates = [beginCoordinates, endCoordinates])
+                self.pdcTmp.RemoveId(self.idZoomBoxTmp)
+                self.Refresh()
+
+    def OnButtonDClick(self, event):
+        """!Open object dialog for editing."""
+        if self.mouse['use'] == 'pointer' and self.dragId != -1:
+            itemCall = {'text':self.parent.OnAddText,
+                        'mapinfo': self.parent.OnAddMapinfo,
+                        'scalebar': self.parent.OnAddScalebar,
+                        'image': self.parent.OnAddImage,
+                        'northArrow' : self.parent.OnAddNorthArrow,
+                        'point': self.parent.AddPoint,
+                        'line': self.parent.AddLine,
+                        'rectangle': self.parent.AddRectangle,
+                        'rasterLegend': self.parent.OnAddLegend,
+                        'vectorLegend': self.parent.OnAddLegend,
+                        'map': self.parent.OnAddMap}
+
+            itemArg = { 'text': dict(event = None, id = self.dragId),
+                        'mapinfo': dict(event = None),
+                        'scalebar': dict(event = None),
+                        'image': dict(event = None, id = self.dragId),
+                        'northArrow': dict(event = None, id = self.dragId),
+                        'point': dict(id = self.dragId),
+                        'line': dict(id = self.dragId),
+                        'rectangle': dict(id = self.dragId),
+                        'rasterLegend': dict(event = None),
+                        'vectorLegend': dict(event = None, page = 1),
+                        'map': dict(event = None, notebook = True)}
+
+            type = self.instruction[self.dragId].type
+            itemCall[type](**itemArg[type])
+
+    def OnDragging(self, event):
+        """!Process panning/resizing/drawing/moving."""
+        if event.MiddleIsDown():
+            # panning
+            self.mouse['end'] = event.GetPosition()
+            self.Pan(begin = self.mouse['begin'], end = self.mouse['end'])
+            self.mouse['begin'] = event.GetPosition()
+
+        elif event.LeftIsDown():
+            # draw box when zooming, creating map 
+            if self.mouse['use'] in ('zoomin', 'zoomout', 'addMap', 'addLine', 'addRectangle'):
+                self.mouse['end'] = event.GetPosition()
+                r = wx.Rect(self.mouse['begin'][0], self.mouse['begin'][1],
+                            self.mouse['end'][0]-self.mouse['begin'][0], self.mouse['end'][1]-self.mouse['begin'][1])
+                r = self.modifyRectangle(r)
+
+                if self.mouse['use'] in ('addLine', 'addRectangle'):
+                    if self.mouse['use'] == 'addLine':
+                        pdcType = 'line'
+                        lineCoords = (self.mouse['begin'], self.mouse['end'])
+                    else:
+                        pdcType = 'rect'
+                        lineCoords = None
+                        if r[2] < 2 or r[3] < 2:
+                            # to avoid strange behavoiur
+                            return
+
+                    self.Draw(pen = self.pen['line'], brush = self.brush['line'],
+                              pdc = self.pdcTmp, drawid = self.idZoomBoxTmp,
+                              pdctype = pdcType, bb = r, lineCoords = lineCoords)
+
+                else:
+                    self.Draw(pen = self.pen['box'], brush = self.brush['box'],
+                              pdc = self.pdcTmp, drawid = self.idZoomBoxTmp,
+                              pdctype = 'rect', bb = r)
+
+            # panning
+            if self.mouse["use"] == 'pan':
+                self.mouse['end'] = event.GetPosition()
+                self.Pan(begin = self.mouse['begin'], end = self.mouse['end'])
+                self.mouse['begin'] = event.GetPosition()
+
+            # move object
+            if self.mouse['use'] == 'pointer' and self.dragId != -1:
+                self.mouse['end'] = event.GetPosition()
+                dx, dy = self.mouse['end'][0] - self.begin[0], self.mouse['end'][1] - self.begin[1]
+                self.pdcObj.TranslateId(self.dragId, dx, dy)
+                self.pdcTmp.TranslateId(self.idBoxTmp, dx, dy)
+                self.pdcTmp.TranslateId(self.idResizeBoxTmp, dx, dy)
+                for id in self.idLinePointsTmp:
+                    self.pdcTmp.TranslateId(id, dx, dy)
+                if self.instruction[self.dragId].type == 'text': 
+                    self.instruction[self.dragId]['coords'] = self.instruction[self.dragId]['coords'][0] + dx,\
+                        self.instruction[self.dragId]['coords'][1] + dy
+                self.begin = event.GetPosition()
+                self.Refresh()
+
+            # resize object
+            if self.mouse['use'] == 'resize':
+                pos = event.GetPosition()
+                diffX = pos[0] - self.mouse['begin'][0]
+                diffY = pos[1] - self.mouse['begin'][1]
+                if self.instruction[self.dragId].type == 'map':
+                    x, y = self.mapBounds.GetX(), self.mapBounds.GetY()
+                    width, height = self.mapBounds.GetWidth(), self.mapBounds.GetHeight()
+                    # match given region
+                    if self.constraint:
+                        if width > height:
+                            newWidth = width + diffX
+                            newHeight = height + diffX * (float(height) / width)
+                        else:
+                            newWidth = width + diffY * (float(width) / height)
+                            newHeight = height + diffY
+                    else:
+                        newWidth = width + diffX
+                        newHeight = height + diffY
+                        
+                    if newWidth < 10 or newHeight < 10:
+                        return
+
+                    bounds = wx.Rect(x, y, newWidth, newHeight)
+                    self.Draw(pen = self.pen['map'], brush = self.brush['map'], pdc = self.pdcObj, drawid = self.dragId,
+                              pdctype = 'rectText', bb = bounds)
+
+                elif self.instruction[self.dragId].type == 'rectangle':
+                    instr = self.instruction[self.dragId]
+                    rect = self.CanvasPaperCoordinates(rect = instr['rect'], canvasToPaper = False)
+                    rect.SetWidth(rect.GetWidth() + diffX)
+                    rect.SetHeight(rect.GetHeight() + diffY)
+
+                    if rect.GetWidth() < 5 or rect.GetHeight() < 5:
+                        return
+
+                    self.DrawGraphics(drawid = self.dragId, shape = 'rectangle', color = instr['color'],
+                                      fcolor = instr['fcolor'], width = instr['width'], bb = rect)
+
+                elif self.instruction[self.dragId].type == 'line':
+                    instr = self.instruction[self.dragId]
+                    points = instr['where']
+                    # moving point
+                    if self.currentLinePoint == 0:
+                        pPaper = points[1]
+                    else:
+                        pPaper = points[0]
+                    pCanvas = self.CanvasPaperCoordinates(rect = Rect2DPS(pPaper, (0, 0)),
+                                                          canvasToPaper = False)[:2]
+                    bounds = wx.RectPP(pCanvas, pos)
+                    self.DrawGraphics(drawid = self.dragId, shape = 'line', color = instr['color'],
+                                      width = instr['width'], bb = bounds, lineCoords = (pos, pCanvas))
+
+                    # update paper coordinates
+                    points[self.currentLinePoint] = self.CanvasPaperCoordinates(rect = wx.RectPS(pos, (0, 0)),
+                                                                                canvasToPaper = True)[:2]
+
+                self.RedrawSelectBox(self.dragId)
+
+    def OnMiddleDown(self, event):
+        """!Middle mouse button pressed."""
+        self.mouse['begin'] = event.GetPosition()
+
+    def Pan(self, begin, end):
+        """!Move canvas while dragging.
+        
+        @param begin x,y coordinates of first point
+        @param end x,y coordinates of second point
+        """
+        view = begin[0] - end[0], begin[1] - end[1]
+        zoomFactor = 1
+        self.Zoom(zoomFactor, view)
+                
+    def RecalculatePosition(self, ids):
+        for id in ids:
+            itype = self.instruction[id].type
+            if itype in ('map', 'rectangle'):
+                self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
+                                                                           canvasToPaper = True)
+                self.RecalculateEN()
+                
+            elif itype in ('mapinfo' ,'rasterLegend', 'vectorLegend', 'image', 'northArrow'):
+                self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
+                                                                           canvasToPaper = True)
+                self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
+                                                                            canvasToPaper = True)[:2] 
+                if itype in ('image', 'northArrow'):
+                    self.RecalculateEN()
+
+            elif itype == 'point':
+                rect = self.pdcObj.GetIdBounds(id)
+                self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = rect,
+                                                                           canvasToPaper = True)
+                rect.OffsetXY(rect.GetWidth()/2, rect.GetHeight()/2)
+                self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = rect,
+                                                                            canvasToPaper = True)[:2]
+                self.RecalculateEN()
+
+            elif itype == 'line':
+                rect = self.pdcObj.GetIdBounds(id)
+                oldRect = self.instruction[id]['rect']
+                newRect = self.CanvasPaperCoordinates(rect = rect, canvasToPaper = True)
+                xDiff = newRect[0] - oldRect[0]
+                yDiff = newRect[1] - oldRect[1]
+                self.instruction[id]['rect'] = newRect
+
+                point1 = wx.Point2D(xDiff, yDiff) + self.instruction[id]['where'][0]
+                point2 = wx.Point2D(xDiff, yDiff) + self.instruction[id]['where'][1]
+                self.instruction[id]['where'] = [point1, point2]
+                
+                self.RecalculateEN()
+
+            elif  itype == 'scalebar':
+                self.instruction[id]['rect'] = self.CanvasPaperCoordinates(rect = self.pdcObj.GetIdBounds(id),
+                                                                           canvasToPaper = True)
+
+                
+                self.instruction[id]['where'] = self.instruction[id]['rect'].GetCentre()
+                
+            elif  itype == 'text':
+                x, y = self.instruction[id]['coords'][0] - self.instruction[id]['xoffset'],\
+                    self.instruction[id]['coords'][1] + self.instruction[id]['yoffset']
+                extent = self.parent.getTextExtent(textDict = self.instruction[id])
+                if self.instruction[id]['rotate'] is not None:
+                    rot = float(self.instruction[id]['rotate'])/180*pi 
+                else:
+                    rot = 0
+
+                if self.instruction[id]['ref'].split()[0] == 'lower':
+                    y += extent[1]
+                elif self.instruction[id]['ref'].split()[0] == 'center':
+                    y += extent[1]/2
+                if self.instruction[id]['ref'].split()[1] == 'right':
+                    x += extent[0] * cos(rot)
+                    y -= extent[0] * sin(rot)
+                elif self.instruction[id]['ref'].split()[1] == 'center':
+                    x += extent[0]/2 * cos(rot)
+                    y -= extent[0]/2 * sin(rot)
+                
+                self.instruction[id]['where'] = self.CanvasPaperCoordinates(rect = Rect2D(x, y, 0, 0),
+                                                                            canvasToPaper = True)[:2]
+                self.RecalculateEN()
+        
+    def ComputeZoom(self, rect):
+        """!Computes zoom factor and scroll view"""
+        zoomFactor = 1
+        cW, cH = self.GetClientSize()
+        cW = float(cW)
+        if rect.IsEmpty(): # clicked on canvas
+            zoomFactor = 1.5
+            if self.mouse['use'] == 'zoomout':
+                zoomFactor = 1./zoomFactor
+            x,y = self.mouse['begin']
+            xView = x - x/zoomFactor#x - cW/(zoomFactor * 2)
+            yView = y - y/zoomFactor#y - cH/(zoomFactor * 2)
+
+        else:   #dragging    
+            rW, rH = float(rect.GetWidth()), float(rect.GetHeight())
+            try:
+                zoomFactor = 1/max(rW/cW, rH/cH)
+            except ZeroDivisionError:
+                zoomFactor = 1
+            # when zooming to full extent, in some cases, there was zoom 1.01..., which causes problem
+            if abs(zoomFactor - 1) > 0.01:
+                zoomFactor = zoomFactor 
+            else:
+                zoomFactor = 1.
+
+
+            if self.mouse['use'] == 'zoomout':
+                zoomFactor = min(rW/cW, rH/cH) 
+            try:
+                if rW/rH > cW/cH:
+                    yView = rect.GetY() - (rW*(cH/cW) - rH)/2
+                    xView = rect.GetX()
+                    
+                    if self.mouse['use'] == 'zoomout':
+                        x,y = rect.GetX() + (rW-(cW/cH)*rH)/2, rect.GetY()
+                        xView, yView = -x, -y
+                else:
+                    xView = rect.GetX() - (rH*(cW/cH) - rW)/2
+                    yView = rect.GetY()
+                    if self.mouse['use'] == 'zoomout':
+                        x,y = rect.GetX(), rect.GetY() + (rH-(cH/cW)*rW)/2
+                        xView, yView = -x, -y
+            except ZeroDivisionError:
+                xView, yView = rect.GetX(), rect.GetY()
+        return zoomFactor, (int(xView), int(yView))
+    
+    
+    def Zoom(self, zoomFactor, view):
+        """! Zoom to specified region, scroll view, redraw"""
+        if not self.currScale:
+            return
+        self.currScale = self.currScale*zoomFactor
+        
+        if self.currScale > 10 or self.currScale < 0.1:
+            self.currScale = self.currScale/zoomFactor
+            return 
+        if not self.preview:
+            # redraw paper
+            pRect = self.pdcPaper.GetIdBounds(self.pageId)
+            pRect.OffsetXY(-view[0], -view[1])
+            pRect = self.ScaleRect(rect = pRect, scale = zoomFactor)
+            self.DrawPaper(pRect)
+            
+            #redraw objects
+            for id in self.objectId:
+                oRect = self.CanvasPaperCoordinates(
+                    rect = self.instruction[id]['rect'], canvasToPaper = False)
+                
+                type = self.instruction[id].type
+                if type == 'text':
+                    coords = self.instruction[id]['coords']# recalculate coordinates, they are not equal to BB
+                    self.instruction[id]['coords'] = coords = [(int(coord) - view[i]) * zoomFactor
+                                                               for i, coord in enumerate(coords)]
+                    self.DrawRotText(pdc = self.pdcObj, drawId = id, textDict = self.instruction[id],
+                                     coords = coords, bounds = oRect )
+                    extent = self.parent.getTextExtent(textDict = self.instruction[id])
+                    if self.instruction[id]['rotate']:
+                        rot = float(self.instruction[id]['rotate']) 
+                    else:
+                        rot = 0
+
+                    self.instruction[id]['rect'] = bounds = self.parent.getModifiedTextBounds(coords[0], coords[1], extent, rot)
+                    self.pdcObj.SetIdBounds(id, bounds)
+                elif type == 'northArrow':
+                    self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcObj,
+                              drawid = id, pdctype = 'bitmap', bb = oRect)
+
+                elif type in ('point', 'line', 'rectangle'):
+                    instr = self.instruction[id]
+                    color = self.instruction[id]['color']
+                    width = fcolor = coords = None
+
+                    if type in ('point', 'rectangle'):
+                        fcolor = self.instruction[id]['fcolor']
+                    if type in ('line', 'rectangle'):
+                        width = self.instruction[id]['width']
+                    if type in ('line'):
+                        point1, point2 = instr['where'][0], instr['where'][1]
+                        point1 = self.CanvasPaperCoordinates(rect = Rect2DPS(point1, (0, 0)),
+                                                             canvasToPaper = False)[:2]
+                        point2 = self.CanvasPaperCoordinates(rect = Rect2DPS(point2, (0, 0)),
+                                                             canvasToPaper = False)[:2]
+                        coords = (point1, point2)
+
+                    self.DrawGraphics(drawid = id, shape = type, bb = oRect, lineCoords = coords,
+                                    color = color, fcolor = fcolor, width = width)
+
+                else:
+                    self.Draw(pen = self.pen[type], brush = self.brush[type], pdc = self.pdcObj,
+                              drawid = id, pdctype = 'rectText', bb = oRect)
+            #redraw tmp objects
+            if self.dragId != -1:
+                self.RedrawSelectBox(self.dragId)
+                
+        #redraw preview
+        else: # preview mode    
+            imageRect = self.pdcImage.GetIdBounds(self.imageId)
+            imageRect.OffsetXY(-view[0], -view[1])
+            imageRect = self.ScaleRect(rect = imageRect, scale = zoomFactor)
+            self.DrawImage(imageRect)
+        
+    def ZoomAll(self):
+        """! Zoom to full extent"""  
+        if not self.preview:
+            bounds = self.pdcPaper.GetIdBounds(self.pageId)
+        else:
+            bounds = self.pdcImage.GetIdBounds(self.imageId)
+        zoomP = bounds.Inflate(bounds.width/20, bounds.height/20)
+        zoomFactor, view = self.ComputeZoom(zoomP)
+        self.Zoom(zoomFactor, view)
+        
+    def Draw(self, pen, brush, pdc, drawid = None, pdctype = 'rect', bb = wx.Rect(0,0,0,0), lineCoords = None): 
+        """! Draw object with given pen and brush.
+
+        @param pdc PseudoDC
+        @param pdctype 'bitmap'/'rectText'/'rect'/'point'/'line'
+        @param bb bounding box
+        @param lineCoords coordinates of line start, end points (wx.Point, wx.Point)
+        """    
+        if drawid is None:
+            drawid = wx.NewId()
+        bb = bb.Get()
+        pdc.BeginDrawing()
+        pdc.RemoveId(drawid)
+        pdc.SetId(drawid)
+        pdc.SetPen(pen)
+        pdc.SetBrush(brush)
+        
+        if pdctype == 'bitmap':
+            if havePILImage:
+                file = self.instruction[drawid]['epsfile']
+                rotation = self.instruction[drawid]['rotate']
+                self.DrawBitmap(pdc = pdc, filePath = file, rotation = rotation, bbox = bb)
+            else: # draw only rectangle with label
+                pdctype = 'rectText'
+                
+        if pdctype in ('rect', 'rectText'):
+            pdc.DrawRectangle(*bb)
+            
+        if pdctype == 'rectText':
+            dc = wx.ClientDC(self) # dc created because of method GetTextExtent, which pseudoDC lacks
+            font = self.font
+            size = 10
+            font.SetPointSize(size)
+            font.SetStyle(wx.ITALIC)
+            dc.SetFont(font)
+            pdc.SetFont(font)
+            text = '\n'.join(self.itemLabels[drawid])
+            w,h,lh = dc.GetMultiLineTextExtent(text)
+            textExtent = (w,h)
+            textRect = wx.Rect(0, 0, *textExtent).CenterIn(bb)
+            r = map(int, bb)
+            while not wx.Rect(*r).ContainsRect(textRect) and size >= 8:
+                size -= 2
+                font.SetPointSize(size)
+                dc.SetFont(font)
+                pdc.SetFont(font)
+                textExtent = dc.GetTextExtent(text)
+                textRect = wx.Rect(0, 0, *textExtent).CenterIn(bb)
+            pdc.SetTextForeground(wx.Color(100,100,100,200)) 
+            pdc.SetBackgroundMode(wx.TRANSPARENT)
+            pdc.DrawText(text = text, x = textRect.x, y = textRect.y)
+                
+        elif pdctype == 'point':
+            pdc.DrawCircle(x = bb[0] + bb[2] / 2,
+                           y = bb[1] + bb[3] / 2,
+                           radius = bb[2] / 2)
+                           
+        elif pdctype == 'line':
+            pdc.DrawLinePoint(lineCoords[0], lineCoords[1])
+
+        pdc.SetIdBounds(drawid, bb)
+        pdc.EndDrawing()
+        self.Refresh()
+
+        return drawid
+    
+    def DrawGraphics(self, drawid, shape, color, bb, width = None, fcolor = None, lineCoords = None):
+        """!Draw point/line/rectangle with given color and width
+
+        @param drawid id of drawn object
+        @param shape drawn shape: 'point'/'line'/'rectangle'
+        @param color pen outline color ('RRR:GGG:BBB')
+        @param fcolor brush fill color, if meaningful ('RRR:GGG:BBB')
+        @param width pen width
+        @param bb bounding box
+        @param lineCoords line coordinates (for line only)
+        """
+        pdctype = {'point'     : 'point',
+                   'line'      : 'line',
+                   'rectangle' : 'rect'}
+
+        if color == 'none':
+            pen = wx.TRANSPARENT_PEN
+        else:
+            if width is not None:
+                units = UnitConversion(self)
+                width = int(units.convert(value = width, fromUnit = 'point', toUnit = 'pixel') * self.currScale)
+            else:
+                width = 2
+            pen = wx.Pen(colour = convertRGB(color), width = width)
+            pen.SetCap(wx.CAP_BUTT) # this is how ps.map draws
+
+        brush = wx.TRANSPARENT_BRUSH
+        if fcolor and fcolor != 'none':
+            brush = wx.Brush(colour = convertRGB(fcolor))
+        
+        self.Draw(pen = pen, brush = brush, pdc = self.pdcObj, pdctype = pdctype[shape],
+                  drawid = drawid, bb = bb, lineCoords = lineCoords)
+
+    def DrawBitmap(self, pdc, filePath, rotation, bbox):
+        """!Draw bitmap using PIL"""
+        pImg = PILImage.open(filePath)
+        if rotation:
+            # get rid of black background
+            pImg = pImg.convert("RGBA")
+            rot = pImg.rotate(rotation, expand = 1)
+            new = PILImage.new('RGBA', rot.size, (255,) * 4)
+            pImg = PILImage.composite(rot, new, rot)
+        pImg = pImg.resize((int(bbox[2]), int(bbox[3])), resample = PILImage.BICUBIC)
+        img = PilImageToWxImage(pImg)
+        bitmap = img.ConvertToBitmap()
+        mask = wx.Mask(bitmap, wx.WHITE)
+        bitmap.SetMask(mask)
+        pdc.DrawBitmap(bitmap, bbox[0], bbox[1], useMask = True)
+        
+    def DrawRotText(self, pdc, drawId, textDict, coords, bounds):
+        if textDict['rotate']:
+            rot = float(textDict['rotate']) 
+        else:
+            rot = 0
+
+        fontsize = textDict['fontsize'] * self.currScale
+        if textDict['background'] != 'none':
+            background = textDict['background'] 
+        else:
+            background = None
+
+        pdc.RemoveId(drawId)
+        pdc.SetId(drawId)
+        pdc.BeginDrawing()
+        
+        # border is not redrawn when zoom changes, why?
+##        if textDict['border'] != 'none' and not rot:
+##            units = UnitConversion(self)
+##            borderWidth = units.convert(value = textDict['width'],
+##                                        fromUnit = 'point', toUnit = 'pixel' ) * self.currScale
+##            pdc.SetPen(wx.Pen(colour = convertRGB(textDict['border']), width = borderWidth))
+##            pdc.DrawRectangle(*bounds)
+            
+        if background:
+            pdc.SetTextBackground(convertRGB(background))
+            pdc.SetBackgroundMode(wx.SOLID)
+        else:
+            pdc.SetBackgroundMode(wx.TRANSPARENT)
+        
+        fn = self.parent.makePSFont(textDict)
+
+        pdc.SetFont(fn)
+        pdc.SetTextForeground(convertRGB(textDict['color']))        
+        pdc.DrawRotatedText(textDict['text'], coords[0], coords[1], rot)
+        
+        pdc.SetIdBounds(drawId, wx.Rect(*bounds))
+        self.Refresh()
+        pdc.EndDrawing()
+        
+    def DrawImage(self, rect):
+        """!Draw preview image to pseudoDC"""
+        self.pdcImage.ClearId(self.imageId)
+        self.pdcImage.SetId(self.imageId)
+        img = self.image
+        
+
+        if img.GetWidth() != rect.width or img.GetHeight() != rect.height:
+            img = img.Scale(rect.width, rect.height)
+        bitmap = img.ConvertToBitmap()
+        
+        self.pdcImage.BeginDrawing()
+        self.pdcImage.DrawBitmap(bitmap, rect.x, rect.y)
+        self.pdcImage.SetIdBounds(self.imageId, rect)
+        self.pdcImage.EndDrawing()
+        self.Refresh()
+        
+    def DrawPaper(self, rect):
+        """!Draw paper and margins"""
+        page = self.instruction[self.pageId]
+        scale = page['Width'] / rect.GetWidth()
+        w = (page['Width'] - page['Right'] - page['Left']) / scale
+        h = (page['Height'] - page['Top'] - page['Bottom']) / scale
+        x = page['Left'] / scale + rect.GetX()
+        y = page['Top'] / scale + rect.GetY()
+        
+        self.pdcPaper.BeginDrawing()
+        self.pdcPaper.RemoveId(self.pageId)
+        self.pdcPaper.SetId(self.pageId)
+        self.pdcPaper.SetPen(self.pen['paper'])
+        self.pdcPaper.SetBrush(self.brush['paper'])
+        self.pdcPaper.DrawRectangleRect(rect)
+        
+        self.pdcPaper.SetPen(self.pen['margins'])
+        self.pdcPaper.SetBrush(self.brush['margins'])
+        self.pdcPaper.DrawRectangle(x, y, w, h)
+        
+        self.pdcPaper.SetIdBounds(self.pageId, rect)
+        self.pdcPaper.EndDrawing()
+        self.Refresh()
+
+        
+    def ImageRect(self):
+        """!Returns image centered in canvas, computes scale"""
+        img = wx.Image(self.imgName, wx.BITMAP_TYPE_PNG)
+        cW, cH = self.GetClientSize()
+        iW, iH = img.GetWidth(), img.GetHeight()
+
+        self.currScale = min(float(cW)/iW, float(cH)/iH)
+        iW = iW * self.currScale
+        iH = iH * self.currScale
+        x = cW/2 - iW/2
+        y = cH/2 - iH/2
+        imageRect = wx.Rect(x, y, iW, iH)
+
+        return imageRect 
+    
+    def RedrawSelectBox(self, id):
+        """!Redraws select box when selected object changes its size"""
+        if self.dragId == id:
+            rect = self.pdcObj.GetIdBounds(id)
+            if self.instruction[id].type != 'line':
+                rect = rect.Inflate(3,3)
+            # draw select box around object
+            self.Draw(pen = self.pen['select'], brush = self.brush['select'], pdc = self.pdcTmp,
+                      drawid = self.idBoxTmp, pdctype = 'rect', bb = rect)
+            
+            # draw small marks signalizing resizing
+            if self.instruction[id].type in ('map', 'rectangle'):
+                controlP = self.pdcObj.GetIdBounds(id).GetBottomRight()
+                rect  = wx.RectPS(controlP, self.resizeBoxSize)
+                self.Draw(pen = self.pen['resize'], brush = self.brush['resize'], pdc = self.pdcTmp,
+                          drawid = self.idResizeBoxTmp, pdctype = 'rect', bb = rect)
+
+            elif self.instruction[id].type == 'line':
+                p1Paper = self.instruction[id]['where'][0]
+                p2Paper = self.instruction[id]['where'][1]
+                p1Canvas = self.CanvasPaperCoordinates(rect = Rect2DPS(p1Paper, (0, 0)), canvasToPaper = False)[:2]
+                p2Canvas = self.CanvasPaperCoordinates(rect = Rect2DPS(p2Paper, (0, 0)), canvasToPaper = False)[:2]
+                rect = []
+                box = wx.RectS(self.resizeBoxSize)
+                rect.append(box.CenterIn(wx.RectPS(p1Canvas, wx.Size())))
+                rect.append(box.CenterIn(wx.RectPS(p2Canvas, wx.Size())))
+                for i, point in enumerate((p1Canvas, p2Canvas)):
+                    self.Draw(pen = self.pen['resize'], brush = self.brush['resize'], pdc = self.pdcTmp,
+                              drawid = self.idLinePointsTmp[i], pdctype = 'rect', bb = rect[i])
+        
+    def UpdateMapLabel(self):
+        """!Updates map frame label"""
+
+        vector = self.instruction.FindInstructionByType('vector')
+        if vector:
+            vectorId = vector.id 
+        else:
+            vectorId = None
+
+        raster = self.instruction.FindInstructionByType('raster')
+        if raster:
+            rasterId = raster.id 
+        else:
+            rasterId = None
+
+        rasterName = 'None'
+        if rasterId:
+            rasterName = self.instruction[rasterId]['raster'].split('@')[0]
+            
+        mapId = self.instruction.FindInstructionByType('map').id
+        self.itemLabels[mapId] = []
+        self.itemLabels[mapId].append(self.itemLabelsDict['map'])
+        self.itemLabels[mapId].append("raster: " + rasterName)
+        if vectorId: 
+            for map in self.instruction[vectorId]['list']:
+                self.itemLabels[mapId].append('vector: ' + map[0].split('@')[0])
+            
+    def UpdateLabel(self, itype, id):
+        self.itemLabels[id] = []
+        self.itemLabels[id].append(self.itemLabelsDict[itype])
+        if itype == 'image':
+            file = os.path.basename(self.instruction[id]['epsfile'])
+            self.itemLabels[id].append(file)
+        
+    def OnSize(self, event):
+        """!Init image size to match window size
+        """
+        # not zoom all when notebook page is changed
+        if self.preview and self.parent.currentPage == 1 or not self.preview and self.parent.currentPage == 0:
+            self.ZoomAll()
+        self.OnIdle(None)
+        event.Skip()
+        
+    def OnIdle(self, event):
+        """!Only re-render a image during idle time instead of
+        multiple times during resizing.
+        """ 
+        
+        width, 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(width, height)
+        # re-render image on idle
+        self.resize = True
+        
+    def ScaleRect(self, rect, scale):
+        """! Scale rectangle"""
+        return wx.Rect(rect.GetLeft()*scale, rect.GetTop()*scale,
+                       rect.GetSize()[0]*scale, rect.GetSize()[1]*scale)   
+    
+def main():
+    import gettext
+    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+    
+    app = wx.PySimpleApp()
+    wx.InitAllImageHandlers()
+    frame = PsMapFrame()
+    frame.Show()
+    
+    app.MainLoop()
+
+if __name__ == "__main__":
+    main()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/psmap/instructions.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/psmap/instructions.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/psmap/instructions.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1811 @@
+"""!
+ at package psmap.instructions
+
+ at brief Map feature objects
+
+Classes:
+ - dialogs::Instruction
+ - dialogs::InstructionObject
+ - dialogs::InitMap
+ - dialogs::MapFrame
+ - dialogs::PageSetup
+ - dialogs::Mapinfo
+ - dialogs::Text
+ - dialogs::Image
+ - dialogs::NorthArrow
+ - dialogs::Point
+ - dialogs::Line
+ - dialogs::Rectangle
+ - dialogs::Scalebar
+ - dialogs::RasterLegend
+ - dialogs::VectorLegend
+ - dialogs::Raster
+ - dialogs::Vector
+ - dialogs::VProperties
+
+(C) 2011-2012 by Anna Kratochvilova, and 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 Anna Kratochvilova <kratochanna gmail.com> (bachelor's project)
+ at author Martin Landa <landa.martin gmail.com> (mentor)
+"""
+
+import os
+import string
+from math import ceil
+from time import strftime, localtime
+
+import wx
+import grass.script as grass
+
+from core.gcmd          import RunCommand, GError, GMessage, GWarning
+from core.utils         import CmdToTuple, GetCmdString
+from dbmgr.vinfo        import VectorDBInfo
+from psmap.utils        import *
+
+class Instruction:
+    """!Class which represents instruction file"""
+    def __init__(self, parent, objectsToDraw):
+        
+        self.parent = parent
+        self.objectsToDraw = objectsToDraw
+        #here are kept objects like mapinfo, rasterlegend, etc.
+        self.instruction = list()
+        
+    def __str__(self):
+        """!Returns text for instruction file"""
+        comment = "# timestamp: " + strftime("%Y-%m-%d %H:%M", localtime()) + '\n'
+        env = grass.gisenv()
+        comment += "# location: %s\n" % env['LOCATION_NAME']
+        comment += "# mapset: %s\n" % env['MAPSET']
+        comment += "# page orientation: %s\n" % self.FindInstructionByType('page')['Orientation']
+        border = ''
+        if not self.FindInstructionByType('map'):
+            border = 'border n\n'
+        text = [str(each) for each in self.instruction]
+        return comment + border + '\n'.join(text) + '\nend'
+    
+    def __getitem__(self, id):
+        for each in self.instruction:
+            if each.id == id:
+                return each
+        return None
+
+    def __contains__(self, id):
+        """!Test if instruction is included"""
+        for each in self.instruction:
+            if each.id == id:
+                return True
+        return False
+    
+    def __delitem__(self, id):
+        """!Delete instruction"""
+        for each in self.instruction:
+            if each.id == id:
+                if each.type == 'map':
+                    #must remove raster, vector layers too
+                    vektor = self.FindInstructionByType('vector', list = True)
+                    vProperties = self.FindInstructionByType('vProperties', list = True)
+                    raster = self.FindInstructionByType('raster', list = True)
+                    for item in vektor + vProperties + raster:
+                        if item in self.instruction:
+                            self.instruction.remove(item)
+                            
+                self.instruction.remove(each)
+                if id in self.objectsToDraw:
+                    self.objectsToDraw.remove(id)
+                return
+            
+    def AddInstruction(self, instruction):
+        """!Add instruction"""
+        # add to instructions
+        if instruction.type == 'map':
+            self.instruction.insert(0, instruction)
+        else:
+            self.instruction.append(instruction)
+        # add to drawable objects
+        if instruction.type not in ('page', 'raster', 'vector', 'vProperties', 'initMap'):
+            if instruction.type == 'map':
+                self.objectsToDraw.insert(0, instruction.id) 
+            else:
+                self.objectsToDraw.append(instruction.id) 
+                
+            
+    def FindInstructionByType(self, type, list = False):
+        """!Find instruction(s) with the given type"""
+        inst = []
+        for each in self.instruction:
+            if each.type == type:
+                inst.append(each)
+        if len(inst) == 1 and not list:
+            return inst[0]
+        return inst
+    
+    def Read(self, filename):
+        """!Reads instruction file and creates instruction objects"""
+        self.filename = filename
+        # open file
+        try:
+            file = open(filename, 'r')
+        except IOError:
+            GError(message = _("Unable to open file\n%s") % filename)
+            return
+        # first read file to get information about region and scaletype
+        isRegionComment = False
+        orientation = 'Portrait'
+        for line in file:
+            if '# g.region' in line:
+                self.SetRegion(regionInstruction = line)
+                isRegionComment = True
+                break
+            if '# page orientation' in line:
+                orientation = line.split(':')[-1].strip()
+                
+        if not isRegionComment:
+            self.SetRegion(regionInstruction = None)
+        # then run ps.map -b to get information for maploc
+        # compute scale and center 
+        map = self.FindInstructionByType('map')
+        region = grass.region()
+        map['center'] = (region['n'] + region['s']) / 2, (region['w'] + region['e']) / 2
+        mapRect = GetMapBounds(self.filename, portrait = (orientation == 'Portrait'))
+        map['rect'] = mapRect
+        proj = projInfo()
+        toM = 1.0
+        if proj['units']:
+            toM = float(proj['meters'])
+        units = UnitConversion(self.parent)
+        w = units.convert(value = mapRect.Get()[2], fromUnit = 'inch', toUnit = 'meter') / toM
+        map['scale'] = w / abs((region['w'] - region['e']))
+        
+        SetResolution(dpi = 300, width = map['rect'].width, height = map['rect'].height)
+        
+        # read file again, now with information about map bounds
+        isBuffer = False
+        buffer = []
+        instruction = None
+        vectorMapNumber = 1
+        file.seek(0)
+        for line in file:
+            if not line.strip(): 
+                continue
+            line = line.strip()
+            if isBuffer:
+                buffer.append(line)
+                if 'end' in line:
+                    isBuffer = False
+                    kwargs = {}
+                    if instruction == 'scalebar':
+                        kwargs['scale'] = map['scale']
+                    elif instruction in ('text', 'eps', 'point', 'line', 'rectangle'):
+                        kwargs['mapInstruction'] = map
+                    elif instruction in ('vpoints', 'vlines', 'vareas'):
+                        kwargs['id'] = wx.NewId()
+                        kwargs['vectorMapNumber'] = vectorMapNumber
+                        vectorMapNumber += 1
+                    elif instruction == 'paper':
+                        kwargs['Orientation'] = orientation
+                        
+                    ok = self.SendToRead(instruction, buffer, **kwargs)
+                    if not ok: return False
+                    buffer = []
+                continue 
+            
+            elif line.startswith('paper'):
+                instruction = 'paper'
+                isBuffer = True
+                buffer.append(line)
+            
+            elif line.startswith('border'):
+                if line.split()[1].lower() in ('n', 'no', 'none'):
+                    ok = self.SendToRead('border', [line])
+                    if not ok: return False
+                elif line.split()[1].lower() in ('y', 'yes'):
+                    instruction = 'border'
+                    isBuffer = True
+                    buffer.append(line)
+            
+            elif line.startswith('scale '):
+                if isBuffer:
+                    continue
+                ok = self.SendToRead('scale', line, isRegionComment = isRegionComment)
+                if not ok: return False
+            
+            elif line.startswith('maploc'):
+                ok = self.SendToRead(instruction = 'maploc', text = line)
+                if not ok: return False
+                
+            elif line.startswith('raster'):
+                ok = self.SendToRead(instruction = 'raster', text = line)
+                if not ok: return False
+            
+            elif line.startswith('mapinfo'):
+                instruction = 'mapinfo'
+                isBuffer = True
+                buffer.append(line)
+
+
+            elif line.startswith('scalebar'):
+                instruction = 'scalebar'
+                isBuffer = True
+                buffer.append(line) 
+            
+            elif line.startswith('text'):
+                instruction = 'text'
+                isBuffer = True
+                buffer.append(line)
+                
+            elif line.startswith('eps'):
+                instruction = 'eps'
+                isBuffer = True
+                buffer.append(line)
+
+            elif line.startswith('point'):
+                instruction = 'point'
+                isBuffer = True
+                buffer.append(line)
+
+            elif line.startswith('line'):
+                instruction = 'line'
+                isBuffer = True
+                buffer.append(line)
+
+            elif line.startswith('rectangle'):
+                instruction = 'rectangle'
+                isBuffer = True
+                buffer.append(line) 
+            
+            elif line.startswith('colortable'):
+                if len(line.split()) == 2 and line.split()[1].lower() in ('n', 'no', 'none'):
+                    break
+                instruction = 'colortable'
+                isBuffer = True
+                buffer.append(line) 
+        
+            elif line.startswith('vlegend'):
+                instruction = 'vlegend'
+                isBuffer = True
+                buffer.append(line) 
+                
+            elif line.startswith('vpoints'):
+                instruction = 'vpoints'
+                isBuffer = True
+                buffer.append(line) 
+                
+            elif line.startswith('vlines'):
+                instruction = 'vlines'
+                isBuffer = True
+                buffer.append(line)
+                
+            elif line.startswith('vareas'):
+                instruction = 'vareas'
+                isBuffer = True
+                buffer.append(line)
+
+
+        
+        rasterLegend = self.FindInstructionByType('rasterLegend')
+        raster = self.FindInstructionByType('raster')
+        page = self.FindInstructionByType('page')
+        vector = self.FindInstructionByType('vector')
+        vectorLegend = self.FindInstructionByType('vectorLegend')
+        vectorMaps = self.FindInstructionByType('vProperties', list = True)
+
+        # check (in case of scaletype 0) if map is drawn also
+        map['drawMap'] = False
+        if map['scaleType'] == 0:
+            mapForRegion = map['map']
+            if map['mapType'] == 'raster' and raster:
+                if mapForRegion == raster['raster']:
+                    map['drawMap'] = True
+            elif map['mapType'] == 'vector' and vector:
+                for vmap in vector['list']:
+                    if mapForRegion == vmap[0]:
+                        map['drawMap'] = True
+
+        # rasterLegend
+        if rasterLegend:
+            if rasterLegend['rasterDefault'] and raster:
+                rasterLegend['raster'] = raster['raster']
+                if not rasterLegend['discrete']:
+                    rasterType = getRasterType(map = rasterLegend['raster'])
+                    if rasterType == 'CELL':
+                        rasterLegend['discrete'] = 'y'
+                    else:
+                        rasterLegend['discrete'] = 'n'
+            
+            #estimate size
+            height = rasterLegend.EstimateHeight(raster = rasterLegend['raster'], discrete = rasterLegend['discrete'], 
+                                                 fontsize = rasterLegend['fontsize'],
+                                                 cols = rasterLegend['cols'], 
+                                                 height = rasterLegend['height'])
+            width = rasterLegend.EstimateWidth(raster = rasterLegend['raster'], discrete = rasterLegend['discrete'], 
+                                               fontsize = rasterLegend['fontsize'],
+                                               cols = rasterLegend['cols'] , 
+                                               width = rasterLegend['width'],
+                                               paperInstr = page)
+            rasterLegend['rect'] = Rect2D(x = float(rasterLegend['where'][0]), y = float(rasterLegend['where'][1]),
+                                          width = width, height = height)
+            
+        # vectors, vlegend        
+        
+        if vector:
+            for vmap in vectorMaps:
+                for i, each in enumerate(vector['list']):
+                    if each[2] == vmap.id:
+                        
+                        vector['list'][i][4] = vmap['label']
+                        vector['list'][i][3] = vmap['lpos']
+            if vectorLegend:
+                size = vectorLegend.EstimateSize(vectorInstr = vector, fontsize = vectorLegend['fontsize'],
+                                                 width = vectorLegend['width'], cols = vectorLegend['cols'])                            
+                vectorLegend['rect'] = Rect2D(x = float(vectorLegend['where'][0]), y = float(vectorLegend['where'][1]),
+                                              width = size[0], height = size[1])
+        
+        
+        page = self.FindInstructionByType('page')
+        if not page:
+            page = PageSetup(wx.NewId())
+            self.AddInstruction(page)
+        else:
+            page['Orientation'] = orientation
+
+
+        #
+        return True
+    
+    def SendToRead(self, instruction, text, **kwargs):
+        psmapInstrDict = dict(paper = ['page'],
+                              maploc = ['map'],
+                              scale = ['map'],
+                              border = ['map'],
+                              raster = ['raster'],
+                              mapinfo = ['mapinfo'],
+                              scalebar = ['scalebar'],
+                              text = ['text'],
+                              eps = ['image', 'northArrow'],
+                              point = ['point'],
+                              line = ['line'],
+                              rectangle = ['rectangle'],
+                              vpoints = ['vector', 'vProperties'],
+                              vlines = ['vector', 'vProperties'],
+                              vareas = ['vector', 'vProperties'],
+                              colortable = ['rasterLegend'],
+                              vlegend = ['vectorLegend']
+                              )
+        
+        myInstrDict = dict(page = PageSetup,
+                           map = MapFrame,
+                           raster = Raster,
+                           mapinfo = Mapinfo,
+                           scalebar = Scalebar,
+                           text = Text,
+                           image = Image,
+                           northArrow = NorthArrow,
+                           point = Point,
+                           line = Line,
+                           rectangle = Rectangle,
+                           rasterLegend = RasterLegend,
+                           vectorLegend = VectorLegend,
+                           vector = Vector,
+                           vProperties = VProperties
+                           )
+        
+        myInstruction = psmapInstrDict[instruction]
+        
+        for i in myInstruction:
+            instr = self.FindInstructionByType(i)
+            if i in ('text', 'vProperties', 'image', 'northArrow', 'point', 'line', 'rectangle') or not instr:
+                
+                id = wx.NewId() #!vProperties expect subtype
+                if i == 'vProperties':
+                    id = kwargs['id']
+                    newInstr = myInstrDict[i](id, subType = instruction[1:])
+                elif i in ('image', 'northArrow'):
+                    commentFound = False
+                    for line in text:
+                        if line.find("# north arrow") >= 0:
+                            commentFound = True
+                    if i == 'image' and commentFound or \
+                       i == 'northArrow' and not commentFound:
+                        continue
+                    newInstr = myInstrDict[i](id, settings = self)
+                else:
+                    newInstr = myInstrDict[i](id)
+                ok = newInstr.Read(instruction, text, **kwargs)
+                if ok:
+                    self.AddInstruction(newInstr)
+                else:
+                    return False
+
+            else:
+                ok = instr.Read(instruction, text, **kwargs)
+
+                if not ok:
+                    return False
+        return True
+    
+    def SetRegion(self, regionInstruction):
+        """!Sets region from file comment or sets current region in case of no comment"""
+        map = MapFrame(wx.NewId())
+        self.AddInstruction(map)
+        if regionInstruction:
+            cmd = CmdToTuple(regionInstruction.strip('# ').split())
+            
+            # define scaleType
+            if len(cmd[1]) <= 3:
+                if 'rast' in cmd[1]:
+                    map['scaleType'] = 0
+                    map['mapType'] = 'raster'   
+                    map['map'] = cmd[1]['rast']  
+                elif 'vect' in cmd[1]:
+                    map['scaleType'] = 0
+                    map['mapType'] = 'vector' 
+                    map['map'] = cmd[1]['vect']  
+                elif 'region' in cmd[1]:
+                    map['scaleType'] = 1  
+                    map['region'] = cmd[1]['region']
+                    
+            else:
+                map['scaleType'] = 2  
+        else:
+            map['scaleType'] = 2
+            grass.del_temp_region()
+            region = grass.region()
+            grass.use_temp_region()    
+            cmd = ['g.region', region]
+        cmdString = GetCmdString(cmd).replace('g.region', '')
+        GMessage(_("Instruction file will be loaded with following region: %s\n") % cmdString)
+        try:
+            RunCommand(cmd[0], **cmd[1])
+            
+        except grass.ScriptError, e:
+            GError(_("Region cannot be set\n%s") % e)
+            return False
+        
+
+class InstructionObject:
+    """!Abtract class representing single instruction"""
+    def __init__(self, id): 
+        self.id = id
+        
+        # default values
+        self.defaultInstruction = dict()
+        # current values
+        self.instruction = self.defaultInstruction   
+        # converting units
+        self.unitConv = UnitConversion() 
+    
+    def __str__(self):
+        """!Returns particular part of text instruction"""
+        return ''
+    
+    def __getitem__(self, key):
+        for each in self.instruction.keys():
+            if each == key:
+                return self.instruction[key]
+        return None
+    
+    def __setitem__(self, key, value):
+        self.instruction[key] = value
+    
+    def GetInstruction(self):
+        """!Get current values"""
+        return self.instruction
+    
+    def SetInstruction(self, instruction):
+        """!Set default values"""
+        self.instruction = instruction
+        
+    def Read(self, instruction, text, **kwargs):
+        """!Read instruction and save them"""
+        pass
+        
+    def PercentToReal(self, e, n):
+        """!Converts text coordinates from percent of region to map coordinates"""
+        e, n = float(e.strip('%')), float(n.strip('%'))
+        region = grass.region()
+        N = region['s'] + (region['n'] - region['s']) / 100 * n
+        E = region['w'] + (region['e'] - region['w']) / 100 * e
+        return E, N
+
+class InitMap(InstructionObject):
+    """!Class representing virtual map"""
+    def __init__(self, id):
+        InstructionObject.__init__(self, id = id)
+        self.type = 'initMap'
+        
+        # default values
+        self.defaultInstruction = dict(rect = None, scale =  None)
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+        
+    
+class MapFrame(InstructionObject):
+    """!Class representing map (instructions maploc, scale, border)"""
+    def __init__(self, id):
+        InstructionObject.__init__(self, id = id)
+        self.type = 'map'
+        # default values
+        self.defaultInstruction = dict(map = None, mapType = None, drawMap = True, region = None,
+                                       rect = Rect2D(), scaleType = 0, scale = None, center = None,
+                                       resolution = 300, border = 'y', width = 1, color = '0:0:0') 
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+        
+    def __str__(self):
+        instr = ''
+        comment = ''
+        
+        #region settings
+        region = grass.region()
+        if self.instruction['scaleType'] == 0: #match map
+            map = self.instruction['map']
+            if self.instruction['mapType'] == 'raster':
+                comment = "# g.region rast=%s nsres=%s ewres=%s\n" % (map, region['nsres'], region['ewres'])
+            else:
+                comment = "# g.region vect=%s\n" % (map)
+        elif self.instruction['scaleType'] == 1:# saved region
+            region = self.instruction['region']
+            comment = "# g.region region=%s\n" % region
+        elif self.instruction['scaleType'] in (2, 3): #current region, fixed scale
+            comment = string.Template("# g.region n=$n s=$s e=$e w=$w rows=$rows cols=$cols \n").substitute(**region)
+        
+        instr += comment
+        instr += '\n'
+        # maploc
+        maplocInstruction = "maploc %.3f %.3f" % (self.instruction['rect'].x, self.instruction['rect'].y)
+        if self.instruction['scaleType'] != 3:
+            maplocInstruction += "  %.3f %.3f"% (self.instruction['rect'].width, self.instruction['rect'].height)
+        instr += maplocInstruction
+        instr += '\n'
+        
+        # scale
+        if self.instruction['scaleType'] == 3: #fixed scale
+            scaleInstruction = "scale 1:%.0f" % (1/self.instruction['scale'])
+            instr += scaleInstruction
+            instr += '\n'
+        # border
+        borderInstruction = ''
+        if self.instruction['border'] == 'n':
+            borderInstruction = "border n"
+        else:
+            borderInstruction = "border y\n"
+            borderInstruction += string.Template("    width $width\n    color $color\n").substitute(self.instruction)
+            borderInstruction += "    end"
+        instr += borderInstruction
+        instr += '\n'
+
+        return instr  
+    
+    def Read(self, instruction, text, **kwargs):
+        """!Read instruction and save information"""
+        if 'isRegionComment' in kwargs:
+            isRegionComment = kwargs['isRegionComment']
+        instr = {}
+        
+        if instruction == 'border':
+            for line in text:
+                if line.startswith('end'):
+                    break
+                try:
+                    if line.split()[1].lower() in ('n', 'no', 'none'):
+                        instr['border'] = 'n'
+                        break
+                    elif line.split()[1].lower() in ('y', 'yes'):
+                        instr['border'] = 'y'
+                    elif line.startswith('width'):
+                        instr['width'] = line.split()[1]
+                    elif line.startswith('color'):
+                        instr['color'] = line.split()[1]
+                except IndexError:
+                    GError(_("Failed to read instruction %s") % instruction)
+                    return False
+                
+        elif instruction == 'scale':
+            try:
+                scaleText = text.strip('scale ').split(':')[1]
+                # when scale instruction given and region comment also, then scaletype is fixed scale
+                if not isRegionComment:
+                    instr['scaleType'] = 2 
+                else:
+                    instr['scaleType'] = 3
+
+                scale = 1/float(scaleText)
+                if abs(scale - self.instruction['scale']) > (0.01 * scale):
+                    GWarning(_("Scale has changed, old value: %(old)s\nnew value: %(new)s") % \
+                                 { 'old' : scale, 'new' : self.instruction['scale'] })
+            except (ValueError, IndexError):
+                GError(_("Failed to read instruction %s.\nUse 1:25000 notation.") % instruction)
+                return False
+        
+        elif instruction == 'maploc':
+            maploc = text.strip('maploc ').split()
+            if len(maploc) >= 2:
+                if  abs(self.instruction['rect'].Get()[0] - float(maploc[0])) > 0.5 or \
+                        abs(self.instruction['rect'].Get()[1] - float(maploc[1])) > 0.5:
+                    GWarning(_("Map frame position changed, old value: %(old1)s %(old2)s\nnew value: %(new1)s %(new2)s") % \
+                                 { 'old1' : maploc[0], 'old2' : maploc[1],
+                                   'new1' : self.instruction['rect'].Get()[0], 'new2' : self.instruction['rect'].Get()[1] })
+                    
+                #instr['rect'] = wx.Rect2D(float(maploc[0]), float(maploc[1]), self.instruction['rect'][2], self.instruction['rect'][3])
+            if len(maploc) == 4:
+                if  abs(self.instruction['rect'].Get()[2] - float(maploc[2])) > 0.5 or \
+                        abs(self.instruction['rect'].Get()[3] - float(maploc[3])) > 0.5:
+                    GWarning(_("Map frame size changed, old value: %(old1)s %(old2)s\nnew value: %(new1)s %(new2)s") % \
+                                 { 'old1' : maploc[2], 'old2' : maploc[3],
+                                   'new1' : self.instruction['rect'].Get()[2], 'new2' : self.instruction['rect'].Get()[3] })
+                #instr['rect'] = wx.Rect2D(*map(float, maploc))
+        self.instruction.update(instr)   
+        return True 
+    
+class PageSetup(InstructionObject):
+    """!Class representing page instruction"""
+    def __init__(self, id):
+        InstructionObject.__init__(self, id = id)
+        self.type = 'page'
+        # default values
+        self.defaultInstruction = dict(Units = 'inch', Format = 'a4', Orientation = 'Portrait',
+                                       Width = 8.268, Height = 11.693, Left = 0.5, Right = 0.5, Top = 1, Bottom = 1)
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+        
+    def __str__(self):
+        if self.instruction['Format'] == 'custom':
+            instr = string.Template("paper\n    width $Width\n    height $Height\n").substitute(self.instruction)
+        else:
+            instr = string.Template("paper $Format\n").substitute(self.instruction)
+        instr += string.Template("    left $Left\n    right $Right\n    bottom $Bottom\n    top $Top\n    end").substitute(self.instruction)
+
+        return instr
+    
+    def Read(self, instruction, text, **kwargs):
+        """!Read instruction and save information"""
+        instr = {}
+        self.cats = ['Width', 'Height', 'Left', 'Right', 'Top', 'Bottom']
+        self.subInstr = dict(zip(['width', 'height', 'left', 'right', 'top', 'bottom'], self.cats))
+        
+        if instruction == 'paper': # just for sure
+            for line in text:
+                if line.startswith('paper'): 
+                    if len(line.split()) > 1:
+                        pformat = line.split()[1]
+                        availableFormats = self._toDict(grass.read_command('ps.map', flags = 'p',
+                                                                           quiet = True))
+                        # e.g. paper a3 
+                        try:
+                            instr['Format'] = pformat
+                            for key, value in availableFormats[pformat].iteritems():
+                                instr[key] = float(value)
+                            break
+                        except KeyError:
+                            GError(_("Failed to read instruction %(file)s.\nUnknown format %(for)s") % \
+                                       { 'file' : instruction, 'for' : format })
+                            return False
+                    else:
+                        # paper
+                        # width ...
+                        instr['Format'] = 'custom'
+                # read subinstructions
+                elif instr['Format'] == 'custom' and not line.startswith('end'):
+                    text = line.split()
+                    try:
+                        instr[self.subInstr[text[0]]] = float(text[1])
+                    except  (IndexError, KeyError):
+                        GError(_("Failed to read instruction %s.") % instruction)
+                        return False
+                    
+            if 'Orientation' in kwargs and kwargs['Orientation'] == 'Landscape':
+                instr['Width'], instr['Height'] = instr['Height'], instr['Width']
+                
+            self.instruction.update(instr)
+        return True  
+    
+    def _toDict(self, paperStr):    
+        sizeDict = dict()
+#     cats = self.subInstr[ 'Width', 'Height', 'Left', 'Right', 'Top', 'Bottom']
+        for line in paperStr.strip().split('\n'):
+            d = dict(zip(self.cats, line.split()[1:]))
+            sizeDict[line.split()[0]] = d
+            
+        return sizeDict    
+    
+class Mapinfo(InstructionObject):
+    """!Class representing mapinfo instruction"""
+    def __init__(self, id):
+        InstructionObject.__init__(self, id = id)
+        self.type = 'mapinfo'
+        # default values
+        self.defaultInstruction = dict(unit = 'inch', where = (0, 0),
+                                       font = 'Helvetica', fontsize = 10, color = '0:0:0', background = 'none', 
+                                       border = 'none', rect = None)
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+        
+    def __str__(self):
+        instr = "mapinfo\n"
+        instr += "    where %.3f %.3f\n" % (self.instruction['where'][0], self.instruction['where'][1])
+        instr += string.Template("    font $font\n    fontsize $fontsize\n    color $color\n").substitute(self.instruction)            
+        instr += string.Template("    background $background\n    border $border\n").substitute(self.instruction)  
+        instr += "    end"
+        return instr
+    
+    def Read(self, instruction, text):
+        """!Read instruction and save information"""
+        instr = {}
+        try:
+            for line in text:
+                sub = line.split(None,1)
+                if sub[0] == 'font':
+                    instr['font'] = sub[1]
+                elif sub[0] == 'fontsize':
+                    instr['fontsize'] = int(sub[1])
+                elif sub[0] == 'color':
+                    instr['color'] = sub[1]
+                elif sub[0] == 'background':
+                    instr['background'] = sub[1]
+                elif sub[0] == 'border':
+                    instr['border'] = sub[1]
+                elif sub[0] == 'where':
+                    instr['where'] = float(sub[1].split()[0]), float(sub[1].split()[1])
+        except (ValueError, IndexError):
+            GError(_("Failed to read instruction %s") % instruction)
+            return False
+        self.instruction.update(instr)
+        self.instruction['rect'] = self.EstimateRect(mapinfoDict = self.instruction)
+        return True
+    
+    def EstimateRect(self, mapinfoDict):
+        """!Estimate size to draw mapinfo"""
+        w = mapinfoDict['fontsize'] * 20 # any better estimation? 
+        h = mapinfoDict['fontsize'] * 7
+        width = self.unitConv.convert(value = w, fromUnit = 'point', toUnit = 'inch')
+        height = self.unitConv.convert(value = h, fromUnit = 'point', toUnit = 'inch')
+        return Rect2D(x = float(mapinfoDict['where'][0]), y = float(mapinfoDict['where'][1]),
+                      width = width, height = height)
+    
+class Text(InstructionObject):
+    """!Class representing text instruction"""
+    def __init__(self, id):
+        InstructionObject.__init__(self, id = id)
+        self.type = 'text'
+        # default values
+        self.defaultInstruction = dict(text = "", font = "Helvetica", fontsize = 10, color = 'black', background = 'none',
+                                       hcolor = 'none', hwidth = 1, border = 'none', width = '1', XY = True,
+                                       where = (0,0), unit = 'inch', rotate = None, 
+                                       ref = "center center", xoffset = 0, yoffset = 0, east = None, north = None)
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+        
+    def __str__(self):
+        text = self.instruction['text'].replace('\n','\\n')
+        instr = u"text %s %s" % (self.instruction['east'], self.instruction['north'])
+        instr += " %s\n" % text
+        instr += (string.Template("    font $font\n    fontsize $fontsize\n    color $color\n").
+                                                                   substitute(self.instruction))
+        instr += string.Template("    hcolor $hcolor\n").substitute(self.instruction)
+        if self.instruction['hcolor'] != 'none':
+            instr += string.Template("    hwidth $hwidth\n").substitute(self.instruction)
+        instr += string.Template("    border $border\n").substitute(self.instruction)
+        if self.instruction['border'] != 'none':
+            instr += string.Template("    width $width\n").substitute(self.instruction)
+        instr += string.Template("    background $background\n").substitute(self.instruction)
+        if self.instruction["ref"] != '0':
+            instr += string.Template("    ref $ref\n").substitute(self.instruction)
+        if self.instruction["rotate"]:
+            instr += string.Template("    rotate $rotate\n").substitute(self.instruction)
+        if float(self.instruction["xoffset"]) or float(self.instruction["yoffset"]):
+            instr += (string.Template("    xoffset $xoffset\n    yoffset $yoffset\n").
+                                                            substitute(self.instruction))
+        instr += "    end"
+        try:
+            instr = instr.encode('latin1')
+        except UnicodeEncodeError, err:
+            try:
+                pos = str(err).split('position')[1].split(':')[0].strip()
+            except IndexError:
+                pos = ''
+            if pos:
+                message = _("Characters on position %s are not supported "
+                            "by ISO-8859-1 (Latin 1) encoding "
+                            "which is required by module ps.map.") % pos
+            else:
+                message = _("Not all characters are supported "
+                            "by ISO-8859-1 (Latin 1) encoding "
+                            "which is required by module ps.map.")
+            GMessage(message = message)
+            return ''
+        
+        return instr
+    
+    def Read(self, instruction, text, **kwargs):
+        """!Read instruction and save information"""
+        map = kwargs['mapInstruction']
+        instr = {}
+        for line in text:
+            try:
+                sub = line.split(None, 1)[0]
+                if sub == 'text':
+                    e, n = line.split(None, 3)[1:3]
+                    if '%' in e and '%' in n:
+                        instr['XY'] = True
+                        instr['east'], instr['north'] = self.PercentToReal(e, n)
+                    else:
+                        instr['XY'] = False
+                        instr['east'], instr['north'] = float(e), float(n)
+                        
+                    instr['text'] = line.split(None, 3)[3].decode('latin_1')
+                
+                elif sub == 'font':
+                    instr['font'] = line.split(None, 1)[1]
+                elif sub == 'fontsize':
+                    instr['fontsize'] = float(line.split(None, 1)[1])
+                elif sub == 'color':
+                    instr['color'] = line.split(None, 1)[1]
+                elif sub == 'width':
+                    instr['width'] = line.split(None, 1)[1]
+                elif sub == 'hcolor':
+                    instr['hcolor'] = line.split(None, 1)[1]
+                elif sub == 'hwidth':
+                    instr['hwidth'] = line.split(None, 1)[1]
+                elif sub == 'background':
+                    instr['background'] = line.split(None, 1)[1]
+                elif sub == 'border':
+                    instr['border'] = line.split(None, 1)[1]
+                elif sub == 'ref':
+                    instr['ref'] = line.split(None, 1)[1]
+                elif sub == 'rotate':
+                    instr['rotate'] = float(line.split(None, 1)[1])
+                elif sub == 'xoffset':
+                    instr['xoffset'] = int(line.split(None, 1)[1])
+                elif sub == 'yoffset':
+                    instr['yoffset'] = int(line.split(None, 1)[1])
+                elif sub == 'opaque':
+                    if line.split(None, 1)[1].lower() in ('n', 'none'):
+                        instr['background'] = 'none'
+                        
+            except(IndexError, ValueError):
+                GError(_("Failed to read instruction %s") % instruction)
+                return False
+        instr['where'] = PaperMapCoordinates(mapInstr = map, x = instr['east'], y = instr['north'], paperToMap = False)       
+        self.instruction.update(instr)
+
+        return True 
+        
+class Image(InstructionObject):
+    """!Class representing eps instruction - image"""
+    def __init__(self, id, settings):
+        InstructionObject.__init__(self, id = id)
+        self.settings = settings
+        self.type = 'image'
+        # default values
+        self.defaultInstruction = dict(epsfile = "", XY = True, where = (0,0), unit = 'inch',
+                                       east = None, north = None,
+                                       rotate = None, scale = 1)
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+        
+    def __str__(self):
+        self.ChangeRefPoint(toCenter = True)
+        
+        instr = "eps %s %s\n" % (self.instruction['east'], self.instruction['north'])
+        instr += string.Template("    epsfile $epsfile\n").substitute(self.instruction)
+        if self.instruction["rotate"]:
+            instr += string.Template("    rotate $rotate\n").substitute(self.instruction)
+        if self.instruction["scale"]:
+            instr += string.Template("    scale $scale\n").substitute(self.instruction)
+        instr += "    end"
+        return instr
+    
+    def Read(self, instruction, text, **kwargs):
+        """!Read instruction and save information"""
+        mapInstr = kwargs['mapInstruction']
+        instr = {}
+        for line in text:
+            try:
+                sub = line.split(None, 1)[0]
+                if sub == 'eps':
+                    e, n = line.split(None, 3)[1:3]
+                    if '%' in e and '%' in n:
+                        instr['XY'] = True
+                        instr['east'], instr['north'] = self.PercentToReal(e, n)
+                    else:
+                        instr['XY'] = False
+                        instr['east'], instr['north'] = float(e), float(n)
+                
+                elif sub == 'epsfile':
+                    instr['epsfile'] = line.split(None, 1)[1]
+                elif sub == 'rotate':
+                    instr['rotate'] = float(line.split(None, 1)[1])
+                elif sub == 'scale':
+                    instr['scale'] = float(line.split(None, 1)[1])
+                        
+            except(IndexError, ValueError):
+                GError(_("Failed to read instruction %s") % instruction)
+                return False
+        if not os.path.exists(instr['epsfile']):
+            GError(_("Failed to read instruction %(inst)s: "
+                     "file %(file)s not found.") % { 'inst' : instruction,
+                                                     'file' : instr['epsfile'] })
+            return False
+        
+        instr['epsfile'] = os.path.abspath(instr['epsfile'])
+        instr['size'] = self.GetImageOrigSize(instr['epsfile'])
+        if 'rotate' in instr:
+            instr['size'] = BBoxAfterRotation(instr['size'][0], instr['size'][1], instr['rotate'])
+        self.instruction.update(instr)
+        self.ChangeRefPoint(toCenter = False)
+        instr['where'] = PaperMapCoordinates(mapInstr = mapInstr, x = self.instruction['east'],
+                                             y = self.instruction['north'], paperToMap = False)       
+        w = self.unitConv.convert(value = instr['size'][0], fromUnit = 'point', toUnit = 'inch')
+        h = self.unitConv.convert(value = instr['size'][1], fromUnit = 'point', toUnit = 'inch')
+        instr['rect'] = Rect2D(x = float(instr['where'][0]), y = float(instr['where'][1]),
+                               width = w * self.instruction['scale'], height = h * self.instruction['scale'])
+        self.instruction.update(instr)
+
+        return True 
+        
+    def ChangeRefPoint(self, toCenter):
+        """!Change reference point (left top x center)"""
+        mapInstr = self.settings.FindInstructionByType('map')
+        if not mapInstr:
+            mapInstr = self.settings.FindInstructionByType('initMap')
+        mapId = mapInstr.id
+        if toCenter:
+            center = self.instruction['rect'].GetCentre()
+            ENCenter = PaperMapCoordinates(mapInstr = self.settings[mapId],
+                                           x = center[0], y = center[1], paperToMap = True)
+                                           
+            self.instruction['east'], self.instruction['north'] = ENCenter
+        else:
+            x, y = PaperMapCoordinates(mapInstr = self.settings[mapId], x = self.instruction['east'],
+                                       y = self.instruction['north'], paperToMap = False)
+            w = self.unitConv.convert(value = self.instruction['size'][0], fromUnit = 'point', toUnit = 'inch')
+            h = self.unitConv.convert(value = self.instruction['size'][1], fromUnit = 'point', toUnit = 'inch')
+            x -= w * self.instruction['scale'] / 2
+            y -= h * self.instruction['scale'] / 2
+            e, n = PaperMapCoordinates(mapInstr = self.settings[mapId], x = x, y = y, paperToMap = True)
+            self.instruction['east'], self.instruction['north'] = e, n
+
+    def GetImageOrigSize(self, imagePath):
+        """!Get image size.
+        
+        If eps, size is read from image header.
+        """
+        fileName = os.path.split(imagePath)[1]
+        # if eps, read info from header
+        if os.path.splitext(fileName)[1].lower() == '.eps':
+            bbInfo = "%%BoundingBox"
+            file = open(imagePath,"r")
+            w = h = 0
+            while file:
+                line = file.readline()
+                if line.find(bbInfo) == 0:
+                    w, h = line.split()[3:5]
+                    break
+            file.close()
+            return float(w), float(h)
+        else: # we can use wx.Image
+            img = wx.Image(fileName, type=wx.BITMAP_TYPE_ANY)
+            return img.GetWidth(), img.GetHeight()
+            
+class NorthArrow(Image):
+    """!Class representing eps instruction -- North Arrow"""
+    def __init__(self, id, settings):
+        Image.__init__(self, id = id, settings = settings)
+        self.type = 'northArrow'
+        
+    def __str__(self):
+        self.ChangeRefPoint(toCenter = True)
+        
+        instr = "eps %s %s\n" % (self.instruction['east'], self.instruction['north'])
+        instr += "# north arrow\n"
+        instr += string.Template("    epsfile $epsfile\n").substitute(self.instruction)
+        if self.instruction["rotate"]:
+            instr += string.Template("    rotate $rotate\n").substitute(self.instruction)
+        if self.instruction["scale"]:
+            instr += string.Template("    scale $scale\n").substitute(self.instruction)
+        instr += "    end"
+        return instr
+        
+class Point(InstructionObject):
+    """!Class representing point instruction"""
+    def __init__(self, id):
+        InstructionObject.__init__(self, id = id)
+        self.type = 'point'
+        # default values
+        self.defaultInstruction = dict(symbol = os.path.join('basic', 'x'),
+                                       color = '0:0:0', fcolor = '200:200:200',
+                                       rotate = 0, size = 10,
+                                       XY = True, where = (0,0), unit = 'inch',
+                                       east = None, north = None)
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+        
+    def __str__(self):
+        instr = string.Template("point $east $north\n").substitute(self.instruction)
+        instr += string.Template("    symbol $symbol\n").substitute(self.instruction)
+        instr += string.Template("    color $color\n").substitute(self.instruction)
+        instr += string.Template("    fcolor $fcolor\n").substitute(self.instruction)
+        instr += string.Template("    rotate $rotate\n").substitute(self.instruction)
+        instr += string.Template("    size $size\n").substitute(self.instruction)
+        instr += "    end"
+        return instr
+    
+    def Read(self, instruction, text, **kwargs):
+        """!Read instruction and save information"""
+        mapInstr = kwargs['mapInstruction']
+        instr = {}
+        for line in text:
+            try:
+                sub = line.split(None, 1)[0]
+                if sub == 'point':
+                    e, n = line.split(None, 3)[1:3]
+                    if '%' in e and '%' in n:
+                        instr['XY'] = True
+                        instr['east'], instr['north'] = self.PercentToReal(e, n)
+                    else:
+                        instr['XY'] = False
+                        instr['east'], instr['north'] = float(e), float(n)
+                
+                elif sub == 'symbol':
+                    instr['symbol'] = line.split(None, 1)[1]
+                elif sub == 'rotate':
+                    instr['rotate'] = float(line.split(None, 1)[1])
+                elif sub == 'size':
+                    instr['size'] = float(line.split(None, 1)[1])
+                elif sub == 'color':
+                    instr['color'] = line.split(None, 1)[1]
+                elif sub == 'fcolor':
+                    instr['fcolor'] = line.split(None, 1)[1]
+
+                        
+            except(IndexError, ValueError):
+                GError(_("Failed to read instruction %s") % instruction)
+                return False
+        
+        self.instruction.update(instr)
+        instr['where'] = PaperMapCoordinates(mapInstr = mapInstr, x = self.instruction['east'],
+                                             y = self.instruction['north'], paperToMap = False)
+        w = h = self.unitConv.convert(value = instr['size'], fromUnit = 'point', toUnit = 'inch')
+        instr['rect'] = Rect2D(x = float(instr['where'][0]) - w / 2, y = float(instr['where'][1] - h / 2),
+                               width = w, height = h)
+        self.instruction.update(instr)
+
+        return True
+
+class Line(InstructionObject):
+    """!Class representing line instruction"""
+    def __init__(self, id):
+        InstructionObject.__init__(self, id = id)
+        self.type = 'line'
+        # default values
+        self.defaultInstruction = dict(color = '0:0:0', width = 2,
+                                       where = [wx.Point2D(), wx.Point2D()],
+                                       east1 = None, north1 = None,
+                                       east2 = None, north2 = None)
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+        
+    def __str__(self):
+        instr = string.Template("line $east1 $north1 $east2 $north2\n").substitute(self.instruction)
+        instr += string.Template("    color $color\n").substitute(self.instruction)
+        instr += string.Template("    width $width\n").substitute(self.instruction)
+        instr += "    end\n"
+        return instr
+    
+    def Read(self, instruction, text, **kwargs):
+        """!Read instruction and save information"""
+        mapInstr = kwargs['mapInstruction']
+        instr = {}
+        for line in text:
+            try:
+                sub = line.split(None, 1)[0]
+                if sub == 'line':
+                    e1, n1, e2, n2 = line.split(None, 5)[1:5]
+                    if '%' in e1 and '%' in n1 and '%' in e2 and '%' in n2:
+                        instr['east1'], instr['north1'] = self.PercentToReal(e1, n1)
+                        instr['east2'], instr['north2'] = self.PercentToReal(e2, n2)
+                    else:
+                        instr['east1'], instr['north1'] = float(e1), float(n1)
+                        instr['east2'], instr['north2'] = float(e2), float(n2)
+                
+                elif sub == 'width':
+                    instr['width'] = float(line.split(None, 1)[1])
+                elif sub == 'color':
+                    instr['color'] = line.split(None, 1)[1]
+                        
+            except(IndexError, ValueError):
+                GError(_("Failed to read instruction %s") % instruction)
+                return False
+        
+        self.instruction.update(instr)
+        e1, n1 = PaperMapCoordinates(mapInstr = mapInstr, x = self.instruction['east1'],
+                                     y = self.instruction['north1'], paperToMap = False)
+        e2, n2 = PaperMapCoordinates(mapInstr = mapInstr, x = self.instruction['east2'],
+                                     y = self.instruction['north2'], paperToMap = False)
+        instr['where'] = [wx.Point2D(e1, n1), wx.Point2D(e2, n2)]
+        instr['rect'] = Rect2DPP(instr['where'][0], instr['where'][1])
+        self.instruction.update(instr)
+
+        return True
+
+class Rectangle(InstructionObject):
+    """!Class representing rectangle instruction"""
+    def __init__(self, id):
+        InstructionObject.__init__(self, id = id)
+        self.type = 'rectangle'
+        # default values
+        self.defaultInstruction = dict(color = '0:0:0', fcolor = 'none', width = 2,
+                                       east1 = None, north1 = None,
+                                       east2 = None, north2 = None)
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+        
+    def __str__(self):
+        instr = string.Template("rectangle $east1 $north1 $east2 $north2\n").substitute(self.instruction)
+        instr += string.Template("    color $color\n").substitute(self.instruction)
+        instr += string.Template("    fcolor $fcolor\n").substitute(self.instruction)
+        instr += string.Template("    width $width\n").substitute(self.instruction)
+        instr += "    end\n"
+        return instr
+    
+    def Read(self, instruction, text, **kwargs):
+        """!Read instruction and save information"""
+        mapInstr = kwargs['mapInstruction']
+        instr = {}
+        for line in text:
+            try:
+                sub = line.split(None, 1)[0]
+                if sub == 'rectangle':
+                    e1, n1, e2, n2 = line.split(None, 5)[1:5]
+                    if '%' in e1 and '%' in n1 and '%' in e2 and '%' in n2:
+                        instr['east1'], instr['north1'] = self.PercentToReal(e1, n1)
+                        instr['east2'], instr['north2'] = self.PercentToReal(e2, n2)
+                    else:
+                        instr['east1'], instr['north1'] = float(e1), float(n1)
+                        instr['east2'], instr['north2'] = float(e2), float(n2)
+                
+                elif sub == 'width':
+                    instr['width'] = float(line.split(None, 1)[1])
+                elif sub == 'color':
+                    instr['color'] = line.split(None, 1)[1]
+                elif sub == 'fcolor':
+                    instr['fcolor'] = line.split(None, 1)[1]
+
+                        
+            except(IndexError, ValueError):
+                GError(_("Failed to read instruction %s") % instruction)
+                return False
+        
+        self.instruction.update(instr)
+        e1, n1 = PaperMapCoordinates(mapInstr = mapInstr, x = self.instruction['east1'],
+                                       y = self.instruction['north1'], paperToMap = False)
+        e2, n2 = PaperMapCoordinates(mapInstr = mapInstr, x = self.instruction['east2'],
+                                       y = self.instruction['north2'], paperToMap = False)
+        instr['rect'] = Rect2DPP(wx.Point2D(e1, n1), wx.Point2D(e2, n2))
+        self.instruction.update(instr)
+
+        return True
+        
+class Scalebar(InstructionObject):
+    """!Class representing scalebar instruction"""
+    def __init__(self, id):
+        InstructionObject.__init__(self, id = id)
+        self.type = 'scalebar'
+        # default values
+        self.defaultInstruction = dict(unit = 'inch', where = (1,1),
+                                       unitsLength = 'auto', unitsHeight = 'inch',
+                                       length = None, height = 0.1, rect = None,
+                                       fontsize = 10, background = 'y',
+                                       scalebar = 'f', segment = 4, numbers = 1)
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+        
+    def __str__(self):
+        instr = string.Template("scalebar $scalebar\n").substitute(self.instruction)
+        instr += "    where %.3f %.3f\n" % (self.instruction['where'][0], self.instruction['where'][1])
+        instr += string.Template("    length $length\n    units $unitsLength\n").substitute(self.instruction)
+        instr += string.Template("    height $height\n").substitute(self.instruction)
+        instr += string.Template("    segment $segment\n    numbers $numbers\n").substitute(self.instruction)
+        instr += string.Template("    fontsize $fontsize\n    background $background\n").substitute(self.instruction)
+        instr += "    end"
+        return instr
+    
+    def Read(self, instruction, text, **kwargs):
+        """!Read instruction and save information"""
+        scale = kwargs['scale']
+        instr = {}
+        for line in text:
+            try:
+                if line.startswith('scalebar'):
+                    if 'scalebar s' in line:
+                        instr['scalebar'] = 's'
+                    else:
+                        instr['scalebar'] = 'f'
+                elif line.startswith('where'):
+                    instr['where'] = map(float, line.split()[1:3])
+                elif line.startswith('length'):
+                    instr['length'] = float(line.split()[1])
+                elif line.startswith('units'):
+                    if line.split()[1] in ['auto', 'meters', 'kilometers', 'feet', 'miles', 'nautmiles']:
+                        instr['unitsLength'] = line.split()[1]
+                elif line.startswith('height'):
+                    instr['height'] = float(line.split()[1])
+                elif line.startswith('fontsize'):
+                    instr['fontsize'] = float(line.split()[1])
+                elif line.startswith('numbers'):
+                    instr['numbers'] = int(line.split()[1])
+                elif line.startswith('segment'):
+                    instr['segment'] = int(line.split()[1])
+                elif line.startswith('background'):
+                    if line.split()[1].strip().lower() in ('y','yes'):
+                        instr['background'] = 'y'
+                    elif line.split()[1].strip().lower() in ('n','no', 'none'):
+                        instr['background'] = 'n'
+            except(IndexError, ValueError):
+                GError(_("Failed to read instruction %s") % instruction)
+                return False
+            
+        self.instruction.update(instr)
+        w, h = self.EstimateSize(scalebarDict = self.instruction, scale = scale)
+        x = self.instruction['where'][0] - w / 2 
+        y = self.instruction['where'][1] - h / 2
+        self.instruction['rect'] = Rect2D(x, y, w, h)
+        return True 
+    
+    def EstimateSize(self, scalebarDict, scale):
+        """!Estimate size to draw scalebar"""
+        units = projInfo()['units']
+        if not units or units not in self.unitConv.getAllUnits():
+            units = 'meters'
+        if scalebarDict['unitsLength'] != 'auto':
+            length = self.unitConv.convert(value = scalebarDict['length'], fromUnit = scalebarDict['unitsLength'], toUnit = 'inch')
+        else:
+            length = self.unitConv.convert(value = scalebarDict['length'], fromUnit = units, toUnit = 'inch')
+            
+        length *= scale
+        length *= 1.1 #for numbers on the edge
+        height = scalebarDict['height'] + 2 * self.unitConv.convert(value = scalebarDict['fontsize'], fromUnit = 'point', toUnit = 'inch')     
+        return (length, height)
+    
+class RasterLegend(InstructionObject):
+    """!Class representing colortable instruction"""
+    def __init__(self, id):
+        InstructionObject.__init__(self, id = id)
+        self.type = 'rasterLegend'
+        # default values
+        self.defaultInstruction = dict(rLegend = False, unit = 'inch', rasterDefault = True, raster = None,
+                                       discrete = None, type = None,
+                                       where = (0, 0),
+                                       width = None, height = None, cols = 1, font = "Helvetica", fontsize = 10,
+                                       #color = '0:0:0', tickbar = False, range = False, min = 0, max = 0,
+                                       color = 'black', tickbar = 'n', range = False, min = 0, max = 0,
+                                       nodata = 'n')
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+        
+    def __str__(self):
+        instr = "colortable y\n"
+        instr += string.Template("    raster $raster\n").substitute(self.instruction)
+        instr += "    where %.3f %.3f\n" % (self.instruction['where'][0], self.instruction['where'][1])
+        if self.instruction['width']:
+            instr += string.Template("    width $width\n").substitute(self.instruction)
+        instr += string.Template("    discrete $discrete\n").substitute(self.instruction)
+        if self.instruction['discrete'] == 'n':
+            if self.instruction['height']:
+                instr += string.Template("    height $height\n").substitute(self.instruction)
+            instr += string.Template("    tickbar $tickbar\n").substitute(self.instruction)
+            if self.instruction['range']:
+                instr += string.Template("    range $min $max\n").substitute(self.instruction)
+        else:
+            instr += string.Template("    cols $cols\n").substitute(self.instruction)
+            instr += string.Template("    nodata $nodata\n").substitute(self.instruction)
+        instr += string.Template("    font $font\n    fontsize $fontsize\n    color $color\n")\
+            .substitute(self.instruction)
+        instr += "    end"
+        return instr    
+    
+    
+    def Read(self, instruction, text, **kwargs):
+        """!Read instruction and save information"""
+        instr = {}
+        instr['rLegend'] = True
+        for line in text:
+            try:
+                if line.startswith('where'):
+                    instr['where'] = map(float, line.split()[1:3])
+                elif line.startswith('font '):
+                    instr['font'] = line.split()[1]
+                elif line.startswith('fontsize'):
+                    instr['fontsize'] = float(line.split()[1])
+                elif line.startswith('color '):
+                    instr['color'] = line.split()[1]
+                elif line.startswith('raster'):
+                    instr['raster'] = line.split()[1]
+                elif line.startswith('width'):
+                    instr['width'] = float(line.split()[1])
+                elif line.startswith('height'):
+                    instr['height'] = float(line.split()[1])
+                elif line.startswith('cols'):
+                    instr['cols'] = int(line.split()[1])                    
+                elif line.startswith('range'):
+                    instr['range'] = True
+                    instr['min'] = float(line.split()[1])
+                    instr['max'] = float(line.split()[2])
+                elif line.startswith('nodata'):
+                    if line.split()[1].strip().lower() in ('y','yes'):
+                        instr['nodata'] = 'y'
+                    elif line.split()[1].strip().lower() in ('n','no', 'none'):
+                        instr['nodata'] = 'n'
+                elif line.startswith('tickbar'):
+                    if line.split()[1].strip().lower() in ('y','yes'):
+                        instr['tickbar'] = 'y'
+                    elif line.split()[1].strip().lower() in ('n','no', 'none'):
+                        instr['tickbar'] = 'n'
+                elif line.startswith('discrete'):
+                    if line.split()[1].strip().lower() in ('y','yes'):
+                        instr['discrete'] = 'y'
+                    elif line.split()[1].strip().lower() in ('n','no', 'none'):
+                        instr['discrete'] = 'n'            
+
+            except(IndexError, ValueError):
+                GError(_("Failed to read instruction %s") % instruction)
+                return False
+            
+        if 'raster' in instr:
+            instr['rasterDefault'] = False
+            if 'discrete' not in instr:
+                rasterType = getRasterType(map = instr['raster'])
+                instr['type'] = rasterType
+                if rasterType == 'CELL':
+                    instr['discrete'] = 'y'
+                else:
+                    instr['discrete'] = 'n'
+            
+        else:
+            instr['rasterDefault'] = True
+        self.instruction.update(instr)
+        # add 'rect' in the end
+            
+        return True 
+    
+    def EstimateHeight(self, raster, discrete, fontsize, cols = None,  height = None):
+        """!Estimate height to draw raster legend"""
+        if discrete == 'n':
+            if height:
+                height = height
+            else:
+                height = self.unitConv.convert(value = fontsize * 10,
+                                                    fromUnit = 'point', toUnit = 'inch')
+                                                    
+        if discrete == 'y':
+            if cols:
+                cols = cols 
+            else:
+                cols = 1 
+
+            rinfo = grass.raster_info(raster)
+            if rinfo['datatype'] in ('DCELL', 'FCELL'):
+                minim, maxim = rinfo['min'], rinfo['max']
+                rows = ceil(maxim / cols )
+            else:
+                cat = grass.read_command('r.category', map = raster,
+                                    fs = ':').strip().split('\n')
+                rows = ceil(float(len(cat)) / cols )
+                            
+                
+            height = self.unitConv.convert(value =  1.5 * rows * fontsize, fromUnit = 'point', toUnit = 'inch')
+            
+        return height
+        
+    def EstimateWidth(self, raster, discrete, fontsize, cols = None, width = None, paperInstr = None):
+        """!Estimate size to draw raster legend"""
+        
+        if discrete == 'n':
+            rinfo = grass.raster_info(raster)
+            minim, maxim = rinfo['min'], rinfo['max']
+            if width:
+                width = width
+            else:
+                width = self.unitConv.convert(value = fontsize * 2,
+                                                    fromUnit = 'point', toUnit = 'inch')
+            text = len(max(str(minim), str(maxim), key = len))
+            textPart = self.unitConv.convert(value = text * fontsize / 2,
+                                                    fromUnit = 'point', toUnit = 'inch')
+            width += textPart
+                                                    
+        elif discrete == 'y':
+            if cols:
+                cols = cols 
+            else:
+                cols = 1    
+
+            if width:
+                width = width
+            else:
+                paperWidth = paperInstr['Width'] - paperInstr['Right'] - paperInstr['Left']
+                width = (paperWidth / cols) * (cols - 1) + 1
+                
+        return width    
+             
+class VectorLegend(InstructionObject):
+    """!Class representing colortable instruction"""
+    def __init__(self, id):
+        InstructionObject.__init__(self, id = id)
+        self.type = 'vectorLegend'
+        # default values
+        self.defaultInstruction = dict(vLegend = False, unit = 'inch', where = (0, 0),
+                                                defaultSize = True, width = 0.4, cols = 1, span = None,
+                                                font = "Helvetica", fontsize = 10,
+                                                border = 'none')
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+        
+    def __str__(self):
+        instr = "vlegend\n"
+        instr += "    where %.3f %.3f\n" % (self.instruction['where'][0], self.instruction['where'][1])
+        instr += string.Template("    font $font\n    fontsize $fontsize\n").substitute(self.instruction)
+        instr += string.Template("    width $width\n    cols $cols\n").substitute(self.instruction)
+        if self.instruction['span']:
+            instr += string.Template("    span $span\n").substitute(self.instruction)
+        instr += string.Template("    border $border\n").substitute(self.instruction)  
+        instr += "    end"  
+        return instr
+
+    def Read(self, instruction, text, **kwargs):
+        """!Read instruction and save information"""
+        instr = {}
+        instr['vLegend'] = True
+        for line in text:
+            try:
+                if line.startswith('where'):
+                    instr['where'] = map(float, line.split()[1:3])
+                elif line.startswith('font '):
+                    instr['font'] = line.split()[1]
+                elif line.startswith('fontsize'):
+                    instr['fontsize'] = float(line.split()[1])
+                elif line.startswith('width'):
+                    instr['width'] = float(line.split()[1])
+                elif line.startswith('cols'):
+                    instr['cols'] = int(line.split()[1]) 
+                elif line.startswith('span'):
+                    instr['span'] = float(line.split()[1])
+                elif line.startswith('border'):
+                    instr['border'] = line.split()[1]
+                    
+            except(IndexError, ValueError):
+                GError(_("Failed to read instruction %s") % instruction)
+                return False
+            
+        self.instruction.update(instr)
+            
+        return True 
+    
+    def EstimateSize(self, vectorInstr, fontsize, width = None, cols = None):
+        """!Estimate size to draw vector legend"""
+        if width:
+            width = width 
+        else:
+            width = fontsize/24.0
+
+        if cols:
+            cols = cols 
+        else:
+            cols = 1
+
+        vectors = vectorInstr['list']
+        labels = [vector[4] for vector in vectors if vector[3] != 0]
+        extent = (len(max(labels, key = len)) * fontsize / 2, fontsize)
+        wExtent = self.unitConv.convert(value = extent[0], fromUnit = 'point', toUnit = 'inch')
+        hExtent = self.unitConv.convert(value = extent[1], fromUnit = 'point', toUnit = 'inch')
+        w = (width + wExtent) * cols
+        h = len(labels) * hExtent / cols
+        h *= 1.1
+        return (w, h)
+            
+   
+class Raster(InstructionObject):
+    """!Class representing raster instruction"""
+    def __init__(self, id):
+        InstructionObject.__init__(self, id = id)
+        self.type = 'raster'
+        # default values
+        self.defaultInstruction = dict(isRaster = False, raster = None)
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+        
+    def __str__(self):
+        instr = string.Template("raster $raster").substitute(self.instruction)
+        return instr
+    
+    def Read(self, instruction, text):
+        """!Read instruction and save information"""
+        instr = {}
+        instr['isRaster'] = True
+        try:
+            map = text.split()[1]
+        except IndexError:
+            GError(_("Failed to read instruction %s") % instruction)
+            return False
+        try:
+            info = grass.find_file(map, element = 'cell')
+        except grass.ScriptError, e:
+            GError(message = e.value)
+            return False
+        instr['raster'] = info['fullname']
+
+        
+        self.instruction.update(instr)
+        return True
+    
+class Vector(InstructionObject):
+    """!Class keeps vector layers"""
+    def __init__(self, id):
+        InstructionObject.__init__(self, id = id)
+        self.type = 'vector'
+        # default values
+        self.defaultInstruction = dict(list = None)# [vmap, type, id, lpos, label] 
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+    def __str__(self):
+        return ''
+    
+    def Read(self, instruction, text, **kwargs):
+        """!Read instruction and save information"""
+        instr = {}
+        
+        for line in text:
+            if line.startswith('vpoints') or line.startswith('vlines') or line.startswith('vareas'):
+                # subtype
+                if line.startswith('vpoints'):
+                    subType = 'points'
+                elif line.startswith('vlines'):
+                    subType = 'lines'
+                elif line.startswith('vareas'):
+                    subType = 'areas'
+                # name of vector map
+                vmap = line.split()[1]
+                try:
+                    info = grass.find_file(vmap, element = 'vector')
+                except grass.ScriptError, e:
+                    GError(message = e.value)
+                    return False
+                vmap = info['fullname']
+                # id
+                id = kwargs['id']
+                # lpos
+                lpos = kwargs['vectorMapNumber']
+                #label
+                label = '('.join(vmap.split('@')) + ')'
+                break
+        instr = [vmap, subType, id, lpos, label] 
+        if not self.instruction['list']:
+            self.instruction['list'] = []
+        self.instruction['list'].append(instr)
+        
+        return True    
+    
+class VProperties(InstructionObject):
+    """!Class represents instructions vareas, vlines, vpoints"""
+    def __init__(self, id, subType):
+        InstructionObject.__init__(self, id = id)
+        self.type = 'vProperties'
+        self.subType = subType
+        # default values
+        if self.subType == 'points':
+            dd = dict(subType  = 'points', name = None, type = 'point or centroid', connection = False, layer = '1',
+                        masked = 'n', color = '0:0:0', width = 1,
+                        fcolor = '255:0:0', rgbcolumn = None, symbol = os.path.join('basic', 'x'), eps = None,
+                        size = 5, sizecolumn = None, scale = None,
+                        rotation = False, rotate = 0, rotatecolumn = None, label = None, lpos = None)
+        elif self.subType == 'lines':
+            dd = dict(subType = 'lines', name = None, type = 'line or boundary', connection = False, layer = '1',
+                        masked = 'n', color = '0:0:0', hwidth = 1,
+                        hcolor = 'none', rgbcolumn = None,
+                        width = 1, cwidth = None,
+                        style = 'solid', linecap = 'butt', label = None, lpos = None)
+        else: # areas
+            dd = dict(subType = 'areas', name = None, connection = False, layer = '1',    
+                        masked = 'n', color = '0:0:0', width = 1,
+                        fcolor = 'none', rgbcolumn = None,
+                        pat = None, pwidth = 1, scale = 1, label = None, lpos = None)
+        self.defaultInstruction = dd
+        # current values
+        self.instruction = dict(self.defaultInstruction)
+        
+    def __str__(self):
+        dic = self.instruction
+        vInstruction = string.Template("v$subType $name\n").substitute(dic)
+        #data selection
+        if self.subType in ('points', 'lines'):
+           vInstruction += string.Template("    type $type\n").substitute(dic) 
+        if dic['connection']:
+            vInstruction += string.Template("    layer $layer\n").substitute(dic)
+            if dic.has_key('cats'):
+                vInstruction += string.Template("    cats $cats\n").substitute(dic)
+            elif dic.has_key('where'):
+                    vInstruction += string.Template("    where $where\n").substitute(dic)
+        vInstruction += string.Template("    masked $masked\n").substitute(dic)
+        #colors
+        vInstruction += string.Template("    color $color\n").substitute(dic)
+        if self.subType in ('points', 'areas'):
+            if dic['color'] != 'none':
+                vInstruction += string.Template("    width $width\n").substitute(dic)
+            if dic['rgbcolumn']:
+                vInstruction += string.Template("    rgbcolumn $rgbcolumn\n").substitute(dic)
+            vInstruction += string.Template("    fcolor $fcolor\n").substitute(dic)
+        else:
+            if dic['rgbcolumn']:
+                vInstruction += string.Template("    rgbcolumn $rgbcolumn\n").substitute(dic)
+            elif dic['hcolor'] != 'none':
+                vInstruction += string.Template("    hwidth $hwidth\n").substitute(dic)
+                vInstruction += string.Template("    hcolor $hcolor\n").substitute(dic)
+        
+        # size and style
+        if self.subType == 'points':
+            if not dic['eps']:
+                vInstruction += string.Template("    symbol $symbol\n").substitute(dic)
+            else: #eps
+                vInstruction += string.Template("    eps $eps\n").substitute(dic)
+            if dic['size']:
+                vInstruction += string.Template("    size $size\n").substitute(dic)            
+            else: # sizecolumn
+                vInstruction += string.Template("    sizecolumn $sizecolumn\n").substitute(dic)
+                vInstruction += string.Template("    scale $scale\n").substitute(dic)
+            if dic['rotation']:
+                if dic['rotate'] is not None:
+                    vInstruction += string.Template("    rotate $rotate\n").substitute(dic)
+                else:
+                    vInstruction += string.Template("    rotatecolumn $rotatecolumn\n").substitute(dic)
+                    
+        if self.subType == 'areas':
+            if dic['pat'] is not None:
+                vInstruction += string.Template("    pat $pat\n").substitute(dic)
+                vInstruction += string.Template("    pwidth $pwidth\n").substitute(dic)
+                vInstruction += string.Template("    scale $scale\n").substitute(dic)
+                
+        if self.subType == 'lines':
+            if dic['width'] is not None:
+                vInstruction += string.Template("    width $width\n").substitute(dic)
+            else:
+                vInstruction += string.Template("    cwidth $cwidth\n").substitute(dic)
+            vInstruction += string.Template("    style $style\n").substitute(dic)
+            vInstruction += string.Template("    linecap $linecap\n").substitute(dic)
+        #position and label in vlegend
+        vInstruction += string.Template("    label $label\n    lpos $lpos\n").substitute(dic)
+        
+        vInstruction += "    end"
+        try:
+            vInstruction = vInstruction.encode('Latin_1')
+        except UnicodeEncodeError, err:
+            try:
+                pos = str(err).split('position')[1].split(':')[0].strip()
+            except IndexError:
+                pos = ''
+            if pos:
+                message = _("Characters on position %s are not supported "
+                            "by ISO-8859-1 (Latin 1) encoding "
+                            "which is required by module ps.map.") % pos
+            else:
+                message = _("Not all characters are supported "
+                            "by ISO-8859-1 (Latin 1) encoding "
+                            "which is required by module ps.map.")
+            GMessage(message = message)
+            return ''
+        return vInstruction
+    
+    def Read(self, instruction, text, **kwargs):
+        """!Read instruction and save information"""
+        instr = {}
+        try:
+            info = grass.find_file(name = text[0].split()[1], element = 'vector')
+        except grass.ScriptError, e:
+            GError(message = e.value)
+            return False
+        instr['name'] = info['fullname']
+        #connection
+        instr['connection'] = True
+        self.mapDBInfo = VectorDBInfo(instr['name'])
+        self.layers = self.mapDBInfo.layers.keys()
+        if not self.layers:
+            instr['connection'] = False
+            
+        # points
+        if text[0].startswith('vpoints'):
+            for line in text[1:]:
+                if line.startswith('type'):
+                    tp = []
+                    if line.find('point') != -1:
+                        tp.append('point')
+                    if line.find('centroid') != -1:
+                        tp.append('centroid')
+                    instr['type'] = ' or '.join(tp)
+                elif line.startswith('fcolor'):
+                    instr['fcolor'] = line.split()[1]
+                elif line.startswith('rgbcolumn'):
+                    instr['rgbcolumn'] = line.split()[1]
+                elif line.startswith('symbol'):
+                    instr['symbol'] = line.split()[1]
+                elif line.startswith('eps'):
+                    instr['eps'] = line.split()[1]
+                elif line.startswith('size '):
+                    instr['size'] = line.split()[1]
+                elif line.startswith('sizecolumn'):
+                    instr['size'] = None
+                    instr['sizecolumn'] = line.split()[1]
+                elif line.startswith('scale '):
+                    instr['scale'] = float(line.split()[1])
+                elif line.startswith('rotate '):
+                    instr['rotation'] = True
+                    instr['rotate'] = line.split()[1]
+                elif line.startswith('rotatecolumn'):
+                    instr['rotatecolumn'] = line.split()[1]
+                    instr['rotation'] = True
+                    instr['rotate'] = None
+                    
+        # lines            
+        elif text[0].startswith('vlines'):
+            for line in text[1:]:
+                if line.startswith('type'):
+                    tp = []
+                    if line.find('line') != -1:
+                        tp.append('line')
+                    if line.find('boundary') != -1:
+                        tp.append('boundary')
+                    instr['type'] = ' or '.join(tp)
+                elif line.startswith('hwidth'):
+                    instr['hwidth'] = float(line.split()[1])
+                elif line.startswith('hcolor'):
+                    instr['hcolor'] = line.split()[1]
+                elif line.startswith('rgbcolumn'):
+                    instr['rgbcolumn'] = line.split()[1]                    
+                elif line.startswith('cwidth'):
+                    instr['cwidth'] = float(line.split()[1])
+                    instr['width'] = None
+                elif line.startswith('style'):
+                    instr['style'] = line.split()[1]       
+                elif line.startswith('linecap'):
+                    instr['linecap'] = line.split()[1]
+         
+        elif text[0].startswith('vareas'):
+            for line in text[1:]:
+                if line.startswith('fcolor'):
+                    instr['fcolor'] = line.split()[1]    
+                elif line.startswith('pat'):
+                    instr['pat'] = line.split()[1]
+                elif line.startswith('pwidth'):
+                    instr['pwidth'] = float(line.split()[1])
+                elif line.startswith('scale'):
+                    instr['scale'] = float(line.split()[1])
+            
+            
+        # same properties for all    
+        for line in text[1:]:
+            if line.startswith('lpos'):
+                instr['lpos'] = int(line.split()[1])
+            elif line.startswith('label'):
+                instr['label'] = line.split(None, 1)[1].decode('latin_1')
+            elif line.startswith('layer'):
+                instr['layer'] = line.split()[1]
+            elif line.startswith('masked'):
+                if line.split()[1].lower() in ('y', 'yes'):
+                    instr['masked'] = 'y'
+                else:
+                    instr['masked'] = 'n'
+            elif line.startswith('color'):
+                instr['color'] = line.split()[1]
+            elif line.startswith('rgbcolumn'):
+                instr['rgbcolumn'] = line.split()[1] 
+            elif line.startswith('width'):
+                instr['width'] = float(line.split()[1])
+                
+        if 'label' not in instr:
+            instr['label'] = '('.join(instr['name'].split('@')) + ')'
+        if 'lpos' not in instr:
+            instr['lpos'] = kwargs['vectorMapNumber']
+        self.instruction.update(instr)
+        
+        return True


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/psmap/menudata.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/psmap/menudata.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/psmap/menudata.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,33 @@
+"""!
+ at package ps.menudata
+
+ at brief wxPsMap - menu entries
+
+Classes:
+ - menudata::PsMapData
+
+(C) 2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import os
+
+from core                 import globalvar
+from core.menudata        import MenuData
+
+class PsMapData(MenuData):
+    def __init__(self, path = None):
+        """!Menu for Cartographic Composer (psmap.py)
+        
+        @path path to XML to be read (None for menudata_psmap.xml)
+        """
+        if not path:
+            gisbase = os.getenv('GISBASE')
+            global etcwxdir
+        path = os.path.join(globalvar.ETCWXDIR, 'xml', 'menudata_psmap.xml')
+        
+        MenuData.__init__(self, path)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/psmap/toolbars.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/psmap/toolbars.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/psmap/toolbars.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,162 @@
+"""!
+ at package psmap.toolbars
+
+ at brief wxPsMap toolbars classes
+
+Classes:
+ - toolbars::PsMapToolbar
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import os
+import sys
+
+import wx
+
+from core              import globalvar
+from gui_core.toolbars import BaseToolbar, BaseIcons
+from icons.icon        import MetaIcon
+
+class PsMapToolbar(BaseToolbar):
+    def __init__(self, parent):
+        """!Toolbar Cartographic Composer (psmap.py)
+        
+        @param parent parent window
+        """
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        self.Realize()
+        
+        self.action = { 'id' : self.pointer }
+        self.defaultAction = { 'id' : self.pointer,
+                               'bind' : self.parent.OnPointer }
+        self.OnTool(None)
+        
+        from psmap.frame import havePILImage
+        if not havePILImage:
+            self.EnableTool(self.preview, False)
+        
+    def _toolbarData(self):
+        """!Toolbar data
+        """
+        icons = {
+            'scriptSave' : MetaIcon(img = 'script-save',
+                                    label = _('Generate text file with mapping instructions')),
+            'scriptLoad' : MetaIcon(img = 'script-load',
+                                    label = _('Load text file with mapping instructions')),                           
+            'psExport'   : MetaIcon(img = 'ps-export',
+                                    label = _('Generate PostScript output')),
+            'pdfExport'  : MetaIcon(img = 'pdf-export',
+                                    label = _('Generate PDF output')),
+            'pageSetup'  : MetaIcon(img = 'page-settings',
+                                    label = _('Page setup'),
+                                    desc = _('Specify paper size, margins and orientation')),
+            'fullExtent' : MetaIcon(img = 'zoom-extent',
+                                    label = _("Full extent"),
+                                    desc = _("Zoom to full extent")),
+            'addMap'     : MetaIcon(img = 'layer-add',
+                                    label = _("Map frame"),
+                                    desc = _("Click and drag to place map frame")),
+            'deleteObj'  : MetaIcon(img = 'layer-remove',
+                                    label = _("Delete selected object")),
+            'preview'    : MetaIcon(img = 'execute',
+                                    label = _("Show preview")),
+            'quit'       : MetaIcon(img = 'quit',
+                                    label = _('Quit Cartographic Composer')),
+            'addText'    : MetaIcon(img = 'text-add',
+                                    label = _('Text')),
+            'addMapinfo' : MetaIcon(img = 'map-info',
+                                    label = _('Map info')),
+            'addLegend'  : MetaIcon(img = 'legend-add',
+                                    label = _('Legend')),
+            'addScalebar' : MetaIcon(img = 'scalebar-add',
+                                     label = _('Scale bar')),
+            'addImage'   : MetaIcon(img = 'image-add',
+                                    label = _('Image')),
+            'addNorthArrow': MetaIcon(img = 'north-arrow-add',
+                                      label = _('North Arrow')),
+            'drawGraphics': MetaIcon(img = 'edit',
+                                     label = _('Add simple graphics')),
+            'pointAdd'    : MetaIcon(img = '',
+                                     label = _('Point')),
+            'lineAdd'     : MetaIcon(img = '',
+                                     label = _('Line')),
+            'rectangleAdd': MetaIcon(img = '',
+                                     label = _('Rectangle')),
+            }
+        self.icons = icons
+        
+        return self._getToolbarData((('loadFile', icons['scriptLoad'],
+                                      self.parent.OnLoadFile),                                    
+                                     ('instructionFile', icons['scriptSave'],
+                                      self.parent.OnInstructionFile),
+                                     (None, ),
+                                     ('pagesetup', icons['pageSetup'],
+                                      self.parent.OnPageSetup),
+                                     (None, ),
+                                     ("pointer", BaseIcons["pointer"],
+                                      self.parent.OnPointer, wx.ITEM_CHECK),
+                                     ('pan', BaseIcons['pan'],
+                                      self.parent.OnPan, wx.ITEM_CHECK),
+                                     ("zoomin", BaseIcons["zoomIn"],
+                                      self.parent.OnZoomIn, wx.ITEM_CHECK),
+                                     ("zoomout", BaseIcons["zoomOut"],
+                                      self.parent.OnZoomOut, wx.ITEM_CHECK),
+                                     ('zoomAll', icons['fullExtent'],
+                                      self.parent.OnZoomAll),
+                                     (None, ),
+                                     ('addMap', icons['addMap'],
+                                      self.parent.OnAddMap, wx.ITEM_CHECK),
+                                     ('addRaster', BaseIcons['addRast'],
+                                      self.parent.OnAddRaster),
+                                     ('addVector', BaseIcons['addVect'],
+                                      self.parent.OnAddVect),
+                                     ("dec", BaseIcons["overlay"],
+                                      self.OnDecoration),
+                                     ("drawGraphics", icons["drawGraphics"],
+                                      self.OnDrawGraphics, wx.ITEM_CHECK),
+                                     ("delete", icons["deleteObj"],
+                                      self.parent.OnDelete),
+                                     (None, ),
+                                     ("preview", icons["preview"],
+                                      self.parent.OnPreview),
+                                     ('generatePS', icons['psExport'],
+                                      self.parent.OnPSFile),
+                                     ('generatePDF', icons['pdfExport'],
+                                      self.parent.OnPDFFile),
+                                     (None, ),
+                                     ("help", BaseIcons['help'],
+                                      self.parent.OnHelp),
+                                     ('quit', icons['quit'],
+                                      self.parent.OnCloseWindow))
+                                    )
+
+    def OnDecoration(self, event):
+        """!Decorations overlay menu
+        """
+        self._onMenu(((self.icons["addLegend"],     self.parent.OnAddLegend),
+                      (self.icons["addMapinfo"],    self.parent.OnAddMapinfo),
+                      (self.icons["addScalebar"],   self.parent.OnAddScalebar),
+                      (self.icons["addText"],       self.parent.OnAddText),
+                      (self.icons["addImage"],      self.parent.OnAddImage),
+                      (self.icons["addNorthArrow"], self.parent.OnAddNorthArrow)))
+
+    def OnDrawGraphics(self, event):
+        """!Simple geometry features (point, line, rectangle) overlay menu
+        """
+        # we need the previous id
+        self.actionOld = self.action['id']
+        self.OnTool(event)
+        self.action['id'] = self.actionOld
+        self._onMenu(((self.icons["pointAdd"],      self.parent.OnAddPoint),
+                      (self.icons["lineAdd"],       self.parent.OnAddLine),
+                      (self.icons["rectangleAdd"],  self.parent.OnAddRectangle),
+                    ))


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/psmap/utils.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/psmap/utils.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/psmap/utils.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,422 @@
+"""!
+ at package psmap.utils
+
+ at brief utilities for wxpsmap (classes, functions)
+
+Classes:
+ - utils::Rect2D
+ - utils::Rect2DPP
+ - utils::Rect2DPS
+ - utils::UnitConversion
+
+(C) 2012 by Anna Kratochvilova, and 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 Anna Kratochvilova <kratochanna gmail.com>
+"""
+
+import wx
+from math import ceil, floor, sin, cos, pi
+
+try:
+    import Image as PILImage
+    havePILImage = True
+except ImportError:
+    havePILImage = False
+
+import grass.script as grass
+from core.gcmd          import RunCommand
+
+class Rect2D(wx.Rect2D):
+    """!Class representing rectangle with floating point values.
+
+    Overrides wx.Rect2D to unify Rect access methods, which are
+    different (e.g. wx.Rect.GetTopLeft() x wx.Rect2D.GetLeftTop()).
+    More methods can be added depending on needs.
+    """
+    def __init__(self, x = 0, y = 0, width = 0, height = 0):
+        wx.Rect2D.__init__(self, x = x, y = y, w = width, h = height)
+
+    def GetX(self):
+        return self.x
+        
+    def GetY(self):
+        return self.y
+
+    def GetWidth(self):
+        return self.width
+
+    def SetWidth(self, width):
+        self.width = width
+        
+    def GetHeight(self):
+        return self.height
+
+    def SetHeight(self, height):
+        self.height = height
+
+class Rect2DPP(Rect2D):
+    """!Rectangle specified by 2 points (with floating point values).
+
+    @see Rect2D, Rect2DPS
+    """
+    def __init__(self, topLeft = wx.Point2D(), bottomRight = wx.Point2D()):
+        Rect2D.__init__(self, x = 0, y = 0, width = 0, height = 0)
+
+        x1, y1 = topLeft[0], topLeft[1]
+        x2, y2 = bottomRight[0], bottomRight[1]
+
+        self.SetLeft(min(x1, x2))
+        self.SetTop(min(y1, y2))
+        self.SetRight(max(x1, x2))
+        self.SetBottom(max(y1, y2))
+
+class Rect2DPS(Rect2D):
+    """!Rectangle specified by point and size (with floating point values).
+
+    @see Rect2D, Rect2DPP
+    """
+    def __init__(self, pos = wx.Point2D(), size = (0, 0)):
+        Rect2D.__init__(self, x = pos[0], y = pos[1], width = size[0], height = size[1])
+
+class UnitConversion:
+    """! Class for converting units"""
+    def __init__(self, parent = None):
+        self.parent = parent
+        if self.parent:
+            ppi = wx.ClientDC(self.parent).GetPPI()
+        else: 
+            ppi = (72, 72)
+        self._unitsPage = { 'inch'          : {'val': 1.0, 'tr' : _("inch")},
+                            'point'         : {'val': 72.0, 'tr' : _("point")},
+                            'centimeter'    : {'val': 2.54, 'tr' : _("centimeter")},
+                            'millimeter'    : {'val': 25.4, 'tr' : _("millimeter")}}
+        self._unitsMap = {  'meters'        : {'val': 0.0254, 'tr' : _("meters")},
+                            'kilometers'    : {'val': 2.54e-5, 'tr' : _("kilometers")},
+                            'feet'          : {'val': 1./12, 'tr' : _("feet")},
+                            'miles'         : {'val': 1./63360, 'tr' : _("miles")},
+                            'nautical miles': {'val': 1/72913.386, 'tr' : _("nautical miles")}}
+
+        self._units = { 'pixel'     : {'val': ppi[0], 'tr' : _("pixel")},
+                        'meter'     : {'val': 0.0254, 'tr' : _("meter")},
+                        'nautmiles' : {'val': 1/72913.386, 'tr' :_("nautical miles")},
+                        'degrees'   : {'val': 0.0254 , 'tr' : _("degree")} #like 1 meter, incorrect
+                        }
+        self._units.update(self._unitsPage)
+        self._units.update(self._unitsMap)
+
+    def getPageUnitsNames(self):
+        return sorted(self._unitsPage[unit]['tr'] for unit in self._unitsPage.keys())
+    
+    def getMapUnitsNames(self):
+        return sorted(self._unitsMap[unit]['tr'] for unit in self._unitsMap.keys())
+    
+    def getAllUnits(self):
+        return sorted(self._units.keys())
+    
+    def findUnit(self, name):
+        """!Returns unit by its tr. string"""
+        for unit in self._units.keys():
+            if self._units[unit]['tr'] == name:
+                return unit
+        return None
+    
+    def findName(self, unit):
+        """!Returns tr. string of a unit"""
+        try:
+            return self._units[unit]['tr']
+        except KeyError:
+            return None
+    
+    def convert(self, value, fromUnit = None, toUnit = None):
+        return float(value)/self._units[fromUnit]['val']*self._units[toUnit]['val']
+
+def convertRGB(rgb):
+    """!Converts wx.Colour(r,g,b,a) to string 'r:g:b' or named color,
+            or named color/r:g:b string to wx.Colour, depending on input""" 
+    # transform a wx.Colour tuple into an r:g:b string    
+    if type(rgb) == wx.Colour:
+        for name, color in grass.named_colors.items(): 
+            if  rgb.Red() == int(color[0] * 255) and\
+                rgb.Green() == int(color[1] * 255) and\
+                rgb.Blue() == int(color[2] * 255):
+                return name
+        return str(rgb.Red()) + ':' + str(rgb.Green()) + ':' + str(rgb.Blue())
+    # transform a GRASS named color or an r:g:b string into a wx.Colour tuple
+    else:
+        color = (grass.parse_color(rgb)[0]*255,
+                 grass.parse_color(rgb)[1]*255,
+                 grass.parse_color(rgb)[2]*255)
+        color = wx.Color(*color)
+        if color.IsOk():
+            return color
+        else:  
+            return None
+        
+        
+def PaperMapCoordinates(mapInstr, x, y, paperToMap = True):
+    """!Converts paper (inch) coordinates <-> map coordinates.
+
+    @param mapInstr map frame instruction
+    @param x,y paper coords in inches or mapcoords in map units
+    @param paperToMap specify conversion direction
+    """
+    region = grass.region()
+    mapWidthPaper = mapInstr['rect'].GetWidth()
+    mapHeightPaper = mapInstr['rect'].GetHeight()
+    mapWidthEN = region['e'] - region['w']
+    mapHeightEN = region['n'] - region['s']
+
+    if paperToMap:
+        diffX = x - mapInstr['rect'].GetX()
+        diffY = y - mapInstr['rect'].GetY()
+        diffEW = diffX * mapWidthEN / mapWidthPaper
+        diffNS = diffY * mapHeightEN / mapHeightPaper
+        e = region['w'] + diffEW
+        n = region['n'] - diffNS
+
+        if projInfo()['proj'] == 'll':
+            return e, n
+        else:
+            return int(e), int(n)
+
+    else:
+        diffEW = x - region['w']
+        diffNS = region['n'] - y
+        diffX = mapWidthPaper * diffEW / mapWidthEN
+        diffY = mapHeightPaper * diffNS / mapHeightEN
+        xPaper = mapInstr['rect'].GetX() + diffX
+        yPaper = mapInstr['rect'].GetY() + diffY
+
+        return xPaper, yPaper
+
+
+def AutoAdjust(self, scaleType,  rect, map = None, mapType = None, region = None):
+    """!Computes map scale, center and map frame rectangle to fit region (scale is not fixed)"""
+    currRegionDict = {}
+    if scaleType == 0 and map:# automatic, region from raster or vector
+        res = ''
+        if mapType == 'raster': 
+            try:
+                res = grass.read_command("g.region", flags = 'gu', rast = map)
+            except grass.ScriptError:
+                pass
+        elif mapType == 'vector':
+            res = grass.read_command("g.region", flags = 'gu', vect = map)
+        currRegionDict = grass.parse_key_val(res, val_type = float)
+    elif scaleType == 1 and region: # saved region
+        res = grass.read_command("g.region", flags = 'gu', region = region)
+        currRegionDict = grass.parse_key_val(res, val_type = float)
+    elif scaleType == 2: # current region
+        env = grass.gisenv()
+        windFilePath = os.path.join(env['GISDBASE'], env['LOCATION_NAME'], env['MAPSET'], 'WIND')
+        try:
+            windFile = open(windFilePath, 'r').read()
+        except IOError:
+            currRegionDict = grass.region()
+        regionDict = grass.parse_key_val(windFile, sep = ':', val_type = float)
+        region = grass.read_command("g.region", flags = 'gu', n = regionDict['north'], s = regionDict['south'],
+                                    e = regionDict['east'], w = regionDict['west'])
+        currRegionDict = grass.parse_key_val(region, val_type = float)
+                                                                
+    else:
+        return None, None, None
+    
+    if not currRegionDict:
+        return None, None, None
+    rX = rect.x
+    rY = rect.y
+    rW = rect.width
+    rH = rect.height
+    if not hasattr(self, 'unitConv'):
+        self.unitConv = UnitConversion(self)
+    toM = 1
+    if projInfo()['proj'] != 'xy':
+        toM = float(projInfo()['meters'])
+
+    mW = self.unitConv.convert(value = (currRegionDict['e'] - currRegionDict['w']) * toM, fromUnit = 'meter', toUnit = 'inch')
+    mH = self.unitConv.convert(value = (currRegionDict['n'] - currRegionDict['s']) * toM, fromUnit = 'meter', toUnit = 'inch')
+    scale = min(rW/mW, rH/mH)
+    
+    if rW/rH > mW/mH:
+        x = rX - (rH*(mW/mH) - rW)/2
+        y = rY
+        rWNew = rH*(mW/mH)
+        rHNew = rH
+    else:
+        x = rX
+        y = rY - (rW*(mH/mW) - rH)/2
+        rHNew = rW*(mH/mW)
+        rWNew = rW
+
+    # center
+    cE = (currRegionDict['w'] + currRegionDict['e'])/2
+    cN = (currRegionDict['n'] + currRegionDict['s'])/2
+    return scale, (cE, cN), Rect2D(x, y, rWNew, rHNew) #inch
+
+def SetResolution(dpi, width, height):
+    """!If resolution is too high, lower it
+    
+    @param dpi max DPI
+    @param width map frame width
+    @param height map frame height
+    """
+    region = grass.region()
+    if region['cols'] > width * dpi or region['rows'] > height * dpi:
+        rows = height * dpi
+        cols = width * dpi
+        RunCommand('g.region', rows = rows, cols = cols)
+               
+def ComputeSetRegion(self, mapDict):
+    """!Computes and sets region from current scale, map center coordinates and map rectangle"""
+
+    if mapDict['scaleType'] == 3: # fixed scale
+        scale = mapDict['scale']
+            
+        if not hasattr(self, 'unitConv'):
+            self.unitConv = UnitConversion(self)
+        
+        fromM = 1
+        if projInfo()['proj'] != 'xy':
+            fromM = float(projInfo()['meters'])
+        rectHalfInch = (mapDict['rect'].width/2, mapDict['rect'].height/2)
+        rectHalfMeter = (self.unitConv.convert(value = rectHalfInch[0], fromUnit = 'inch', toUnit = 'meter')/ fromM /scale,
+                         self.unitConv.convert(value = rectHalfInch[1], fromUnit = 'inch', toUnit = 'meter')/ fromM /scale) 
+        
+        centerE = mapDict['center'][0]
+        centerN = mapDict['center'][1]
+        
+        raster = self.instruction.FindInstructionByType('raster')
+        if raster:
+            rasterId = raster.id 
+        else:
+            rasterId = None
+
+        if rasterId:
+            RunCommand('g.region', n = ceil(centerN + rectHalfMeter[1]),
+                       s = floor(centerN - rectHalfMeter[1]),
+                       e = ceil(centerE + rectHalfMeter[0]),
+                       w = floor(centerE - rectHalfMeter[0]),
+                       rast = self.instruction[rasterId]['raster'])
+        else:
+            RunCommand('g.region', n = ceil(centerN + rectHalfMeter[1]),
+                       s = floor(centerN - rectHalfMeter[1]),
+                       e = ceil(centerE + rectHalfMeter[0]),
+                       w = floor(centerE - rectHalfMeter[0]))
+                    
+def projInfo():
+    """!Return region projection and map units information,
+    taken from render.py"""
+    
+    projinfo = dict()
+    
+    ret = RunCommand('g.proj', read = True, flags = 'p')
+    
+    if not ret:
+        return projinfo
+    
+    for line in ret.splitlines():
+        if ':' in line:
+            key, val = line.split(':')
+            projinfo[key.strip()] = val.strip()
+        elif "XY location (unprojected)" in line:
+            projinfo['proj'] = 'xy'
+            projinfo['units'] = ''
+            break
+    
+    return projinfo
+
+def GetMapBounds(filename, portrait = True):
+    """!Run ps.map -b to get information about map bounding box
+    
+        @param filename psmap input file
+        @param portrait page orientation"""
+    orient = ''
+    if not portrait:
+        orient = 'r'
+    try:
+        bb = map(float, grass.read_command('ps.map',
+                                           flags = 'b' + orient,
+                                           quiet = True,
+                                           input = filename).strip().split('=')[1].split(','))
+    except (grass.ScriptError, IndexError):
+        GError(message = _("Unable to run `ps.map -b`"))
+        return None
+    return Rect2D(bb[0], bb[3], bb[2] - bb[0], bb[1] - bb[3])
+
+def getRasterType(map):
+    """!Returns type of raster map (CELL, FCELL, DCELL)"""
+    if map is None:
+        map = ''
+    file = grass.find_file(name = map, element = 'cell')
+    if file['file']:
+        rasterType = grass.raster_info(map)['datatype']
+        return rasterType
+    else:
+        return None
+   
+def PilImageToWxImage(pilImage, copyAlpha = True):
+    """!Convert PIL image to wx.Image
+    
+    Based on http://wiki.wxpython.org/WorkingWithImages
+    """
+    hasAlpha = pilImage.mode[-1] == 'A'
+    if copyAlpha and hasAlpha :  # Make sure there is an alpha layer copy.
+        wxImage = wx.EmptyImage( *pilImage.size )
+        pilImageCopyRGBA = pilImage.copy()
+        pilImageCopyRGB = pilImageCopyRGBA.convert('RGB')    # RGBA --> RGB
+        pilImageRgbData = pilImageCopyRGB.tostring()
+        wxImage.SetData(pilImageRgbData)
+        wxImage.SetAlphaData(pilImageCopyRGBA.tostring()[3::4])  # Create layer and insert alpha values.
+
+    else :    # The resulting image will not have alpha.
+        wxImage = wx.EmptyImage(*pilImage.size)
+        pilImageCopy = pilImage.copy()
+        pilImageCopyRGB = pilImageCopy.convert('RGB')    # Discard any alpha from the PIL image.
+        pilImageRgbData = pilImageCopyRGB.tostring()
+        wxImage.SetData(pilImageRgbData)
+
+    return wxImage
+
+def BBoxAfterRotation(w, h, angle):
+    """!Compute bounding box or rotated rectangle
+    
+    @param w rectangle width
+    @param h rectangle height
+    @param angle angle (0, 360) in degrees
+    """
+    angleRad = angle / 180. * pi
+    ct = cos(angleRad)
+    st = sin(angleRad)
+    
+    hct = h * ct
+    wct = w * ct
+    hst = h * st
+    wst = w * st
+    y = x = 0
+    
+    if 0 < angle <= 90:
+        y_min = y
+        y_max = y + hct + wst
+        x_min = x - hst
+        x_max = x + wct
+    elif 90 < angle <= 180:
+        y_min = y + hct
+        y_max = y + wst
+        x_min = x - hst + wct
+        x_max = x
+    elif 180 < angle <= 270:
+        y_min = y + wst + hct
+        y_max = y
+        x_min = x + wct
+        x_max = x - hst
+    elif 270 < angle <= 360:
+        y_min = y + wst
+        y_max = y + hct
+        x_min = x
+        x_max = x + wct - hst
+        
+    width = int(ceil(abs(x_max) + abs(x_min)))
+    height = int(ceil(abs(y_max) + abs(y_min)))
+    return width, height


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/states.txt
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/states.txt	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/states.txt	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,185 @@
+Afghanistan; 59.9,28.66 75.65,39.11
+Africa; -20.2,-37.6 53.4,35.75
+Albania; 19.36,39.42 21.39,42.71
+Algeria; -9.47,17.94 13.19,38.14
+Angola; 11.31,-18.68 24.97,-3.84
+Antarctic; -180,-90 180,-66
+Antarctica; -180,-90 180,-62.83
+Arctic; -180,66 180,90
+Argentina; -74.97,-56.71 -51.76,-20.25
+Armenia; 43.53,38.68 47.07,41.48
+Asia; 40,-10 180,83.5
+Australia; 111.22,-45.73 155.72,-8.88
+Austria; 9.27,45.99 17.93,49.38
+Azerbaijan; 44.58,38.04 50.96,42.2
+Bangladesh; 87.95,20.75 93.07,26.62
+Belgium; 2.54,49.31 6.69,51.69
+Belize; -89.18,15.78 -87.78,18.64
+Benin; 0.74,5.97 4.34,12.66
+Bhutan; 88.8,26.54 92.37,28.46
+Bolivia; -70.05,-23.63 -56.72,-9.13
+Bosnia and Herzegovina; 15.76,42.38 20.02,45.45
+Botswana; 19.57,-27.41 29.94,-17.32
+Brazil; -75.64,-35.81 -32.74,7.12
+Brunei; 114.22,3.96 115.42,5.09
+Bulgaria; 22.19,40.86 29.02,44.59
+Burkina Faso; -5.72,9.19 2.98,15.54
+Burma; 91.41,9.22 102.13,29.34
+Burundi; 28.98,-4.85 31.17,-2.35
+Byelarus; 22.91,50.82 33.38,56.65
+Cambodia; 102.28,10.07 107.98,14.86
+Cameroon; 8.22,1.06 16.85,13.65
+Canada; -145.27,37.3 -48.11,87.61
+Caribbean; -91.4,27.36 -55.4,6.48
+Central African Republic; 13.96,1.5 28.11,11.67
+Central America; -94.1,21.8 -75.8,6.61
+Chad; 12.88,6.67 24.97,24.19
+Chile; -77.16,-56.79 -64.9,-15.72
+China; 70.83,15.06 137.97,56.58
+Colombia; -79.69,-5 -66.15,13.28
+Congo; 10.93,-5.41 19.19,3.98
+Costa Rica; -85.83,7.9 -82.18,11.38
+Croatia; 13.47,42.09 19.92,46.84
+Cuba; -85.03,19.36 -73.44,23.68
+Cyprus; 32.23,34.44 34.78,35.78
+Czech Republic; 12.13,48.23 19.38,51.42
+Denmark; 8.02,54.68 12.89,58
+Djibouti; 41.89,10.78 43.77,12.81
+Dominican Republic; -71.87,17.54 -67.99,20.12
+East Pacific Ocean; -180,64.8 -72.7,-75.6
+Ecuador; -81.08,-5.35 -74.68,1.72
+Egypt; 24.29,21.29 37.61,32.14
+El Salvador; -90.05,13.07 -87.41,14.6
+Equatorial Guinea; 8.39,0.76 11.59,3.82
+Eritrea; 36.31,12 43.58,18.41
+Estonia; 23.3,57.29 28.59,59.75
+Ethiopia; 32.49,2.63 48.85,15.56
+Europe; -25.1,71.3 35,34.9
+Finland; 20.46,59.3 32.14,70.44
+France; -5.29,40.65 10.4,51.82
+French Guiana; -54.37,1.84 -51.23,5.89
+Gabon; 8.71,-4.23 15.01,2.6
+Gambia; -16.71,13.02 -13.66,13.96
+Germany; 5.68,46.86 15.68,55.41
+Ghana; -3.31,4.39 1.7,11.47
+Greece; 19.99,34.62 27.19,42.01
+Greenland; -75.34,56.78 -9.36,86.6
+Guatemala; -92.24,13.59 -87.87,18.06
+Guinea; -15.19,6.77 -6.87,13.02
+Guinea-Bissau; -16.51,10.97 -13.34,12.8
+Guyana; -61.41,0.81 -56.12,8.79
+Haiti; -74.38,17.88 -71.34,20.1
+Honduras; -89.47,12.75 -82.92,16.31
+Hungary; 16.12,45.44 23.57,48.95
+Iceland; -24.55,62.81 -12.79,67.01
+India; 66.79,6.58 99.01,36.96
+Indian Ocean; 22.3,-55.4 119.5,25.2
+Indonesia; 93.11,-12.65 143.45,7.88
+Iran; 43.31,24.08 64.42,40.73
+Iraq; 38.47,28.5 49.25,37.84
+Ireland; -10.52,51.23 -5.62,55.49
+Israel; 34.17,29.25 36.09,33.31
+Italy; 6.11,36.15 19.33,47.71
+Ivory Coast; -8.64,4.03 -2.01,10.96
+Jamaica; -78.22,17.72 -76,18.63
+Japan; 128.74,30.1 146.46,46.26
+Jordan; 34.97,28.87 39.75,33.44
+Kazakhstan; 44.73,38.62 89.65,57.49
+Kenya; 33,-5.3 42.44,5.07
+Democratic People's Republic of Korea; 124.02,43.29 37.55,130.95
+Republic of Korea; 125.95,38.76 33.06,129.88
+Kuwait; 46.62,28.34 48.74,30
+Kyrgyzstan; 69.01,38.7 81.03,43.77
+Laos; 99.77,13.47 108.1,22.98
+Latvia; 20.76,55.32 28.76,58.44
+Lebanon; 35.09,32.84 36.79,34.63
+Lesotho; 27.16,-30.89 29.76,-28.59
+Liberia; -11.47,4.16 -6.95,8.66
+Libya; 8.79,18.7 26.1,33.95
+Lithuania; 20.86,53.6 27.25,56.73
+Luxembourg; 5.9,49.42 6.77,50.21
+Macedonia; 20.62,40.62 23.27,42.48
+Madagascar; 42.83,-26.31 51.38,-11.58
+Malawi; 32.55,-17.51 36.46,-9.26
+Malaysia; 99.4,-0.2 120.19,7.86
+Mali; -12.77,9.25 5.27,25.83
+Mauritania; -17.47,14.21 -4.04,27.81
+Mexico; -118.48,13.05 -85.18,34.17
+Middle East; 25,10.7 59.7,36.1
+Moldova; 26.64,45.31 30.47,48.64
+Mongolia; 86.47,40 121.62,53.65
+Montenegro; 18.56,41.77 20.67,43.64
+Morocco; -13.52,26.96 -0.28,36.48
+Mozambique; 29.67,-27.82 41.89,-9.63
+Namibia; 11.32,-29.61 25.86,-16.31
+Nepal; 79.9,26 88.84,30.88
+Netherlands; 3.54,50.56 7.62,53.59
+New Hampshire; -72.68,42.57 -70.58,45.43
+New Jersey; -75.69,38.8 -73.78,41.47
+New Mexico; -109.35,31.04 -102.7,37.3
+New Zealand; 166.05,-47.31 179.41,-33.89
+Nicaragua; -87.7,10.55 -82.87,15.24
+Niger; -0.39,10.95 16.95,24.28
+Nigeria; 2.33,3.72 15.34,14.4
+North America; -168.5,18 -50.4,85.7
+North Atlantic Ocean; -82,0 12,80
+Northern Temperate; -180,23 180,60
+Norway; 3.88,56.69 32.56,81.95
+Oman; 51.53,16.19 60.52,26.73
+Pakistan; 60.18,22.94 78.66,37.86
+Panama; -83.06,6.9 -76.63,9.95
+Papua New Guinea; 140.37,-11.3 153.05,-2.2
+Paraguay; -62.83,-27.85 -53.6,-18.87
+Peru; -82.13,-19.35 -67.52,0.79
+Philippines; 116.68,4.85 127.23,19.22
+Poland; 13.77,48.57 24.85,55.24
+Portugal; -9.6,36.75 -5.65,42.36
+Qatar; 50.97,24.33 51.89,26.17
+Romania; 20.05,43.29 30.38,48.76
+Russia; 25,23.21 180,71
+Rwanda; 28.9,-3.01 31.2,-1.03
+Saudi Arabia; 33.9,14.01 57.3,33.22
+Senegal; -17.53,12.02 -10.89,17.14
+Serbia; 18.8,41.66 23.35,46.39
+Sierra Leone; -13.16,6.71 -10.02,10.09
+Slovakia; 16.84,47.61 23.06,49.93
+Slovenia; 13.39,45.28 16.87,47.06
+Somalia; 40.53,-2.55 52.14,12.66
+South Africa; 13.68,-35.9 33.98,-21.27
+South America; -84.9,-57.6 -32.4,13.7
+South Atlantic Ocean; -67,-55.4 23,0
+Southern Ocean; -180,-77 180,-32
+Southern Temperate; -180,-60 180,-23
+Spain; -9.69,35.4 3.98,44.38
+Sri Lanka; 79.69,5.76 82.26,9.89
+Sudan; 21.06,2.6 39.77,22.86
+Suriname; -58.01,1.53 -53.42,6.23
+Swaziland; 30.93,-27.52 32.45,-25.72
+Sweden; 10.56,54.63 24.84,69.68
+Switzerland; 5.92,45.66 10.84,48.02
+Syria; 35.36,31.84 43.11,37.69
+Taiwan; 119.99,21.78 122.14,25.31
+Tajikistan; 67.34,36.34 75.59,41.46
+Tanzania United Republic of; 0,-0.54 28.96,41.23
+Thailand; 96.83,4.8 106.42,21.22
+Togo; -0.09,5.85 2.21,11.33
+Trinidad; -61.88,10.01 -60.86,10.89
+Tropics; -180,-23 180,23
+Tunisia; 7.38,29.87 12.03,37.65
+Turkey; 25.29,34.91 45.94,43
+Turkmenistan; 52.05,34.56 67.66,43.46
+Uganda; 29.45,-1.82 35.52,4.32
+Ukraine; 21.4,43.61 41.24,53.31
+United Arab Emirates; 51.06,21.82 56.87,26.25
+United Kingdom; -8.41,49.49 2.39,59.07
+United States; -180,13.71 -61.48,76.63
+Uruguay; -58.46,-35.26 -52.77,-29.97
+Uzbekistan; 55.44,36.08 74.31,46.46
+Venezuela; -73.81,-0.11 -58.91,12.92
+Vietnam; 101.43,7.75 110.25,24.05
+Virginia; -84.1,36.12 -74.82,39.88
+Western Sahara; -17.23,20.87 -8.01,28
+Yemen; 42.45,12.12 53.74,19.51
+Zaire; 11.45,-14.4 32.4,6.28
+Zambia; 21.55,-18.7 34.45,-7.69
+Zimbabwe; 25.11,-22.93 33.65,15.22


Property changes on: grass/branches/releasebranch_6_4/gui/wxpython/states.txt
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Author Date Id
Added: svn:eol-style
   + native

Modified: grass/branches/releasebranch_6_4/gui/wxpython/tools/update_menudata.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/tools/update_menudata.py	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/tools/update_menudata.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -30,23 +30,24 @@
 from grass.script import core as grass
 from grass.script import task as gtask
 
-sys.path.append('gui_modules')
-import menudata
+if __name__ == "__main__":
+    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'wxpython'))
+from lmgr.menudata  import ManagerData
+from core.globalvar import grassCmd
 
 def parseModules():
     """!Parse modules' interface"""
     modules = dict()
     
     # list of modules to be ignored
-    ignore =  [ 'mkftcap',
+    ignore =  [ 'g.mapsets_picker.py',
+                'v.type_wrapper.py',
                 'g.parser',
-                'r.mapcalc',
-                'r3.mapcalc',
                 'vcolors' ]
     
-    count = len(globalvar.grassCmd['all'])
+    count = len(grassCmd)
     i = 0
-    for module in globalvar.grassCmd['all']:
+    for module in grassCmd:
         i += 1
         if i % 10 == 0:
             grass.info('* %d/%d' % (i, count))
@@ -54,8 +55,8 @@
             continue
         try:
             interface = gtask.parse_interface(module)
-        except:
-            grass.error(module)
+        except (StandardError, grass.ScriptError), e:
+            grass.error(module + ': ' + str(e))
             continue
         modules[interface.name] = { 'label'   : interface.label,
                                     'desc'    : interface.description,
@@ -69,7 +70,7 @@
     ignore = ['v.type_wrapper.py',
               'vcolors']
     
-    menu_modules = list()    
+    menu_modules = list()
     for node in data.tree.getiterator():
         if node.tag != 'menuitem':
             continue
@@ -105,7 +106,7 @@
             node.find('keywords').text = ','.join(modules[module]['keywords'])
         
         menu_modules.append(item['command'])
-    
+
     for module in modules.keys():
         if module not in menu_modules:
             grass.warning("'%s' not available from the menu" % module)
@@ -150,7 +151,7 @@
     modules = dict()
     modules = parseModules()
     grass.info("Step 3: reading menu data...")
-    data = menudata.ManagerData()
+    data = ManagerData()
     grass.info("Step 4: updating menu data...")
     updateData(data, modules)
     
@@ -172,9 +173,4 @@
     if os.getenv("GISBASE") is None:
         sys.exit("You must be in GRASS GIS to run this program.")
     
-    sys.path.append(os.path.join(os.getenv("GISBASE"), 'etc', 'wxpython', 'gui_modules'))
-    import menudata
-    import menuform
-    import globalvar
-    
     sys.exit(main())

Added: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/dialogs.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/dialogs.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/dialogs.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,754 @@
+"""!
+ at package vdigit.dialogs
+
+ at brief wxGUI vector digitizer dialogs
+
+Classes:
+ - dialogs::VDigitCategoryDialog
+ - dialogs::CategoryListCtrl
+ - dialogs::VDigitZBulkDialog
+ - dialogs::VDigitDuplicatesDialog
+ - dialogs::CheckListFeature
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import sys
+import copy
+
+import wx
+import wx.lib.mixins.listctrl as listmix
+
+from core.gcmd        import RunCommand, GError
+from core.debug       import Debug
+from core.settings    import UserSettings
+
+class VDigitCategoryDialog(wx.Dialog, listmix.ColumnSorterMixin):
+    def __init__(self, parent, title,
+                 vectorName, query = None, cats = None,
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        """!Dialog used to display/modify categories of vector objects
+        
+        @param parent
+        @param title dialog title
+        @param query {coordinates, qdist} - used by v.edit/v.what
+        @param cats  directory of lines (layer/categories) - used by vdigit
+        @param style dialog style
+        """
+        self.parent = parent       # mapdisplay.BufferedWindow class instance
+        self.digit = parent.digit
+        
+        # map name
+        self.vectorName = vectorName
+        
+        # line : {layer: [categories]}
+        self.cats = {}
+        
+        # do not display dialog if no line is found (-> self.cats)
+        if cats is None:
+            if self._getCategories(query[0], query[1]) == 0 or not self.line:
+                Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
+        else:
+            self.cats = cats
+            for line in cats.keys():
+                for layer in cats[line].keys():
+                    self.cats[line][layer] = list(cats[line][layer])
+            
+            layers = []
+            for layer in self.digit.GetLayers():
+                layers.append(str(layer))
+        
+        # make copy of cats (used for 'reload')
+        self.cats_orig = copy.deepcopy(self.cats)
+        
+        wx.Dialog.__init__(self, parent = self.parent, id = wx.ID_ANY, title = title,
+                           style = style, **kwargs)
+        
+        # list of categories
+        box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                           label = " %s " % _("List of categories - right-click to delete"))
+        listSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        self.list = CategoryListCtrl(parent = self, id = wx.ID_ANY,
+                                     style = wx.LC_REPORT |
+                                     wx.BORDER_NONE |
+                                     wx.LC_SORT_ASCENDING |
+                                     wx.LC_HRULES |
+                                     wx.LC_VRULES)
+        # sorter
+        self.fid = self.cats.keys()[0]
+        self.itemDataMap = self.list.Populate(self.cats[self.fid])
+        listmix.ColumnSorterMixin.__init__(self, 2)
+        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)
+        if len(self.cats.keys()) == 1:
+            self.fidMulti.Show(False)
+            self.fidText.SetLabel(str(self.fid))
+        else:
+            self.fidText.Show(False)
+            choices = []
+            for fid in self.cats.keys():
+                choices.append(str(fid))
+            self.fidMulti.SetItems(choices)
+            self.fidMulti.SetSelection(0)
+        
+        listSizer.Add(item = self.list, proportion = 1, flag = wx.EXPAND)
+
+        # add new category
+        box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                           label = " %s " % _("Add new category"))
+        addSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        flexSizer = wx.FlexGridSizer (cols = 5, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(3)
+
+        layerNewTxt = wx.StaticText(parent = self, id = wx.ID_ANY,
+                                 label = "%s:" % _("Layer"))
+        self.layerNew = wx.Choice(parent = self, id = wx.ID_ANY, size = (75, -1),
+                                  choices = layers)
+        if len(layers) > 0:
+            self.layerNew.SetSelection(0)
+        
+        catNewTxt = wx.StaticText(parent = self, id = wx.ID_ANY,
+                               label = "%s:" % _("Category"))
+
+        try:
+            newCat = max(self.cats[self.fid][1]) + 1
+        except KeyError:
+            newCat = 1
+        self.catNew = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (75, -1),
+                                  initial = newCat, min = 0, max = 1e9)
+        btnAddCat = wx.Button(self, wx.ID_ADD)
+        flexSizer.Add(item = layerNewTxt, proportion = 0,
+                      flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(item = self.layerNew, proportion = 0,
+                      flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(item = catNewTxt, proportion = 0,
+                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT,
+                      border = 10)
+        flexSizer.Add(item = self.catNew, proportion = 0,
+                      flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(item = btnAddCat, proportion = 0,
+                      flag = wx.EXPAND | wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
+        addSizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5)
+
+        # buttons
+        btnApply = wx.Button(self, wx.ID_APPLY)
+        btnApply.SetToolTipString(_("Apply changes"))
+        btnCancel = wx.Button(self, wx.ID_CANCEL)
+        btnCancel.SetToolTipString(_("Ignore changes and close dialog"))
+        btnOk = wx.Button(self, wx.ID_OK)
+        btnOk.SetToolTipString(_("Apply changes and close dialog"))
+        btnOk.SetDefault()
+
+        # sizers
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(btnCancel)
+        #btnSizer.AddButton(btnReload)
+        #btnSizer.SetNegativeButton(btnReload)
+        btnSizer.AddButton(btnApply)
+        btnSizer.AddButton(btnOk)
+        btnSizer.Realize()
+        
+        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)
+        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.ALL, border = 5)
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+        self.SetAutoLayout(True)
+
+        # set min size for dialog
+        self.SetMinSize(self.GetBestSize())
+
+        # bindings
+        btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+        btnOk.Bind(wx.EVT_BUTTON, self.OnOK)
+        btnAddCat.Bind(wx.EVT_BUTTON, self.OnAddCat)
+        btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+                                     
+        # list
+        self.list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) #wxMSW
+        self.list.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) #wxGTK
+        self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit, self.list)
+        self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndEdit, self.list)
+        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)
+
+    def GetListCtrl(self):
+        """!Used by ColumnSorterMixin
+        """
+        return self.list
+
+    def OnColClick(self, event):
+        """!Click on column header (order by)
+        """
+        event.Skip()
+        
+    def OnBeginEdit(self, event):
+        """!Editing of item started
+        """
+        event.Allow()
+
+    def OnEndEdit(self, event):
+        """!Finish editing of item
+        """
+        itemIndex = event.GetIndex()
+        layerOld = int (self.list.GetItem(itemIndex, 0).GetText())
+        catOld = int (self.list.GetItem(itemIndex, 1).GetText())
+
+        if event.GetColumn() == 0:
+            layerNew = int(event.GetLabel())
+            catNew = catOld
+        else:
+            layerNew = layerOld
+            catNew = int(event.GetLabel())
+        
+        try:
+            if layerNew not in self.cats[self.fid].keys():
+                self.cats[self.fid][layerNew] = []
+            self.cats[self.fid][layerNew].append(catNew)
+            self.cats[self.fid][layerOld].remove(catOld)
+        except:
+            event.Veto()
+            self.list.SetStringItem(itemIndex, 0, str(layerNew))
+            self.list.SetStringItem(itemIndex, 1, str(catNew))
+            dlg = wx.MessageDialog(self, _("Unable to add new layer/category <%(layer)s/%(category)s>.\n"
+                                           "Layer and category number must be integer.\n"
+                                           "Layer number must be greater than zero.") %
+                                   { 'layer': self.layerNew.GetStringSelection(),
+                                     'category' : str(self.catNew.GetValue()) },
+                                   _("Error"), wx.OK | wx.ICON_ERROR)
+            dlg.ShowModal()
+            dlg.Destroy()
+            return False
+
+    def OnRightDown(self, event):
+        """!Mouse right button down
+        """
+        x = event.GetX()
+        y = event.GetY()
+        item, flags = self.list.HitTest((x, y))
+
+        if item !=  wx.NOT_FOUND and \
+                flags & wx.LIST_HITTEST_ONITEM:
+            self.list.Select(item)
+
+        event.Skip()
+
+    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.OnItemDelete,    id = self.popupID1)
+            self.Bind(wx.EVT_MENU, self.OnItemDeleteAll, 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"))
+        if self.list.GetFirstSelected() == -1:
+            menu.Enable(self.popupID1, False)
+
+        menu.Append(self.popupID2, _("Delete all"))
+        menu.AppendSeparator()
+        menu.Append(self.popupID3, _("Reload"))
+
+        self.PopupMenu(menu)
+        menu.Destroy()
+
+    def OnItemSelected(self, event):
+        """!Item selected
+        """
+        event.Skip()
+
+    def OnItemDelete(self, event):
+        """!Delete selected item(s) from the list (layer/category pair)
+        """
+        item = self.list.GetFirstSelected()
+        while item != -1:
+            layer = int (self.list.GetItem(item, 0).GetText())
+            cat = int (self.list.GetItem(item, 1).GetText())
+            self.list.DeleteItem(item)
+            self.cats[self.fid][layer].remove(cat)
+
+            item = self.list.GetFirstSelected()
+            
+        event.Skip()
+        
+    def OnItemDeleteAll(self, event):
+        """!Delete all items from the list
+        """
+        self.list.DeleteAllItems()
+        self.cats[self.fid] = {}
+
+        event.Skip()
+
+    def OnFeature(self, event):
+        """!Feature id changed (on duplicates)
+        """
+        self.fid = int(event.GetString())
+        
+        self.itemDataMap = self.list.Populate(self.cats[self.fid],
+                                              update = True)
+
+        try:
+            newCat = max(self.cats[self.fid][1]) + 1
+        except KeyError:
+            newCat = 1
+            
+        self.catNew.SetValue(newCat)
+        
+        event.Skip()
+        
+    def _getCategories(self, coords, qdist):
+        """!Get layer/category pairs for all available
+        layers
+
+        Return True line found or False if not found
+        """
+        ret = RunCommand('v.what',
+                         parent = self,
+                         quiet = True,
+                         map = self.vectorName,
+                         east_north = '%f,%f' % \
+                             (float(coords[0]), float(coords[1])),
+                         distance = qdist)
+
+        if not ret:
+            return False
+
+        for item in ret.splitlines():
+            litem = item.lower()
+            if "id:" in litem: # get line id
+                self.line = int(item.split(':')[1].strip())
+            elif "layer:" in litem: # add layer
+                layer = int(item.split(':')[1].strip())
+                if layer not in self.cats.keys():
+                    self.cats[layer] = []
+            elif "category:" in litem: # add category
+                self.cats[layer].append(int(item.split(':')[1].strip()))
+
+        return True
+
+    def OnReload(self, event):
+        """!Reload button pressed
+        """
+        # restore original list
+        self.cats = copy.deepcopy(self.cats_orig)
+
+        # polulate list
+        self.itemDataMap = self.list.Populate(self.cats[self.fid],
+                                              update = True)
+
+        event.Skip()
+
+    def OnCancel(self, event):
+        """!Cancel button pressed
+        """
+        self.parent.parent.dialogs['category'] = None
+        if self.digit:
+            self.digit.GetDisplay().SetSelected([])
+            self.parent.UpdateMap(render = False)
+        else:
+            self.parent.parent.OnRender(None)
+            
+        self.Close()
+
+    def OnApply(self, event):
+        """!Apply button pressed
+        """
+        for fid in self.cats.keys():
+            newfid = self.ApplyChanges(fid)
+            if fid == self.fid and newfid > 0:
+                self.fid = newfid
+            
+    def ApplyChanges(self, fid):
+        """!Apply changes 
+
+        @param fid feature id
+        """
+        cats = self.cats[fid]
+        cats_orig = self.cats_orig[fid]
+
+        # action : (catsFrom, catsTo)
+        check = {'catadd': (cats,      cats_orig),
+                 'catdel': (cats_orig, cats)}
+
+        newfid = -1
+        
+        # add/delete new category
+        for action, catsCurr in check.iteritems():
+            for layer in catsCurr[0].keys():
+                catList = []
+                for cat in catsCurr[0][layer]:
+                    if layer not in catsCurr[1].keys() or \
+                            cat not in catsCurr[1][layer]:
+                        catList.append(cat)
+                if catList != []:
+                    if action == 'catadd':
+                        add = True
+                    else:
+                        add = False
+                        
+                    newfid = self.digit.SetLineCats(fid, layer,
+                                                    catList, add)
+                    if len(self.cats.keys()) == 1:
+                        self.fidText.SetLabel("%d" % newfid)
+                    else:
+                        choices = self.fidMulti.GetItems()
+                        choices[choices.index(str(fid))] = str(newfid)
+                        self.fidMulti.SetItems(choices)
+                        self.fidMulti.SetStringSelection(str(newfid))
+                    
+                    self.cats[newfid] = self.cats[fid]
+                    del self.cats[fid]
+                    
+                    fid = newfid
+                    if self.fid < 0:
+                        wx.MessageBox(parent = self, message = _("Unable to update vector map."),
+                                      caption = _("Error"), style = wx.OK | wx.ICON_ERROR)
+        
+        self.cats_orig[fid] = copy.deepcopy(cats)
+        
+        return newfid
+
+    def OnOK(self, event):
+        """!OK button pressed
+        """
+        self.OnApply(event)
+        self.OnCancel(event)
+
+    def OnAddCat(self, event):
+        """!Button 'Add' new category pressed
+        """
+        try:
+            layer = int(self.layerNew.GetStringSelection())
+            cat   = int(self.catNew.GetValue())
+            if layer <= 0:
+                raise ValueError
+        except ValueError:
+            GError(parent = self,
+                   message = _("Unable to add new layer/category <%(layer)s/%(category)s>.\n"
+                               "Layer and category number must be integer.\n"
+                               "Layer number must be greater than zero.") %
+                   {'layer' : str(self.layerNew.GetValue()),
+                    'category' : str(self.catNew.GetValue())})
+            return False
+        
+        if layer not in self.cats[self.fid].keys():
+            self.cats[self.fid][layer] = []
+        
+        self.cats[self.fid][layer].append(cat)
+        
+        # reload list
+        self.itemDataMap = self.list.Populate(self.cats[self.fid],
+                                              update = True)
+        
+        # update category number for add
+        self.catNew.SetValue(cat + 1)
+        
+        event.Skip()
+
+        return True
+
+    def GetLine(self):
+        """!Get id of selected line of 'None' if no line is selected
+        """
+        return self.cats.keys()
+
+    def UpdateDialog(self, query = None, cats = None):
+        """!Update dialog
+        
+        @param query {coordinates, distance} - v.what
+        @param cats  directory layer/cats    - vdigit
+        Return True if updated otherwise False
+        """
+        # line: {layer: [categories]}
+        self.cats = {}
+        # do not display dialog if no line is found (-> self.cats)
+        if cats is None:
+            ret = self._getCategories(query[0], query[1])
+        else:
+            self.cats = cats
+            for line in cats.keys():
+                for layer in cats[line].keys():
+                    self.cats[line][layer] = list(cats[line][layer])
+            ret = 1
+        if ret == 0 or len(self.cats.keys()) < 1:
+            Debug.msg(3, "VDigitCategoryDialog(): nothing found!")
+            return False
+        
+        # make copy of cats (used for 'reload')
+        self.cats_orig = copy.deepcopy(self.cats)
+
+        # polulate list
+        self.fid = self.cats.keys()[0]
+        self.itemDataMap = self.list.Populate(self.cats[self.fid],
+                                              update = True)
+
+        try:
+            newCat = max(self.cats[self.fid][1]) + 1
+        except KeyError:
+            newCat = 1
+        self.catNew.SetValue(newCat)
+        
+        if len(self.cats.keys()) == 1:
+            self.fidText.Show(True)
+            self.fidMulti.Show(False)
+            self.fidText.SetLabel("%d" % self.fid)
+        else:
+            self.fidText.Show(False)
+            self.fidMulti.Show(True)
+            choices = []
+            for fid in self.cats.keys():
+                choices.append(str(fid))
+            self.fidMulti.SetItems(choices)
+            self.fidMulti.SetSelection(0)
+
+        self.Layout()
+        
+        return True
+
+class CategoryListCtrl(wx.ListCtrl,
+                       listmix.ListCtrlAutoWidthMixin,
+                       listmix.TextEditMixin):
+    def __init__(self, parent, id, pos = wx.DefaultPosition,
+                 size = wx.DefaultSize, style = 0):
+        """!List of layers/categories"""
+        self.parent = parent
+        
+        wx.ListCtrl.__init__(self, parent, id, pos, size, style)
+
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+        listmix.TextEditMixin.__init__(self)
+
+    def Populate(self, cats, update = False):
+        """!Populate the list
+        """
+        itemData = {} # requested by sorter
+
+        if not update:
+            self.InsertColumn(0, _("Layer"))
+            self.InsertColumn(1, _("Category"))
+        else:
+            self.DeleteAllItems()
+
+        i = 1
+        for layer in cats.keys():
+            catsList = cats[layer]
+            for cat in catsList:
+                index = self.InsertStringItem(sys.maxint, str(catsList[0]))
+                self.SetStringItem(index, 0, str(layer))
+                self.SetStringItem(index, 1, str(cat))
+                self.SetItemData(index, i)
+                itemData[i] = (str(layer), str(cat))
+                i = i + 1
+
+        if not update:
+            self.SetColumnWidth(0, 100)
+            self.SetColumnWidth(1, wx.LIST_AUTOSIZE)
+
+        self.currentItem = 0
+
+        return itemData
+
+class VDigitZBulkDialog(wx.Dialog):
+    def __init__(self, parent, title, nselected, style = wx.DEFAULT_DIALOG_STYLE):
+        """!Dialog used for Z bulk-labeling tool
+        """
+        wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style)
+
+        self.parent = parent # mapdisplay.BufferedWindow class instance
+
+        # panel  = wx.Panel(parent=self, id=wx.ID_ANY)
+
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        txt = wx.StaticText(parent = self,
+                            label = _("%d lines selected for z bulk-labeling") % nselected);
+        border.Add(item = txt, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+
+        box   = wx.StaticBox (parent = self, id = wx.ID_ANY, label = " %s " % _("Set value"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        flexSizer = wx.FlexGridSizer (cols = 2, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(0)
+
+        # starting value
+        txt = wx.StaticText(parent = self,
+                            label = _("Starting value"));
+        self.value = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (150, -1),
+                                 initial = 0,
+                                 min = -1e6, max = 1e6)
+        flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(self.value, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
+
+        # step
+        txt = wx.StaticText(parent = self,
+                            label = _("Step"))
+        self.step  = wx.SpinCtrl(parent = self, id = wx.ID_ANY, size = (150, -1),
+                                 initial = 0,
+                                 min = 0, max = 1e6)
+        flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(self.step, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
+
+        sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = sizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 0)
+
+        # buttons
+        btnCancel = wx.Button(self, wx.ID_CANCEL)
+        btnOk = wx.Button(self, wx.ID_OK)
+        btnOk.SetDefault()
+
+        # sizers
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(btnCancel)
+        btnSizer.AddButton(btnOk)
+        btnSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = border, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+
+class VDigitDuplicatesDialog(wx.Dialog):
+    def __init__(self, parent, data, title = _("List of duplicates"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
+                 pos = wx.DefaultPosition):
+        """!Show duplicated feature ids
+        """
+        wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style,
+                           pos = pos)
+        
+        self.parent = parent # BufferedWindow
+        self.data = data
+        self.winList = []
+
+        # panel  = wx.Panel(parent=self, id=wx.ID_ANY)
+
+        # notebook
+        self.notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
+
+        id = 1
+        for key in self.data.keys():
+            panel = wx.Panel(parent = self.notebook, id = wx.ID_ANY)
+            self.notebook.AddPage(page = panel, text = " %d " % (id))
+            
+            # notebook body
+            border = wx.BoxSizer(wx.VERTICAL)
+
+            win = CheckListFeature(parent = panel, data = list(self.data[key]))
+            self.winList.append(win.GetId())
+
+            border.Add(item = win, proportion = 1,
+                       flag = wx.ALL | wx.EXPAND, border = 5)
+
+            panel.SetSizer(border)
+
+            id += 1
+
+        # buttons
+        btnCancel = wx.Button(self, wx.ID_CANCEL)
+        btnOk = wx.Button(self, wx.ID_OK)
+        btnOk.SetDefault()
+
+        # sizers
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(btnCancel)
+        btnSizer.AddButton(btnOk)
+        btnSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = self.notebook, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+        self.SetAutoLayout(True)
+
+        # set min size for dialog
+        self.SetMinSize((250, 180))
+
+    def GetUnSelected(self):
+        """!Get unselected items (feature id)
+
+        @return list of ids
+        """
+        ids = []
+        for id in self.winList:
+            wlist = self.FindWindowById(id)
+
+            for item in range(wlist.GetItemCount()):
+                if not wlist.IsChecked(item):
+                    ids.append(int(wlist.GetItem(item, 0).GetText()))
+                    
+        return ids
+
+class CheckListFeature(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin):
+    def __init__(self, parent, data,
+                 pos = wx.DefaultPosition, log = None):
+        """!List of mapset/owner/group
+        """
+        self.parent = parent
+        self.data = data
+
+        wx.ListCtrl.__init__(self, parent, wx.ID_ANY,
+                             style = wx.LC_REPORT)
+
+        listmix.CheckListCtrlMixin.__init__(self)
+
+        self.log = log
+
+        # setup mixins
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+
+        self.LoadData(self.data)
+
+    def LoadData(self, data):
+        """!Load data into list
+        """
+        self.InsertColumn(0, _('Feature id'))
+        self.InsertColumn(1, _('Layer (Categories)'))
+
+        for item in data:
+            index = self.InsertStringItem(sys.maxint, str(item[0]))
+            self.SetStringItem(index, 1, str(item[1]))
+
+        # enable all items by default
+        for item in range(self.GetItemCount()):
+            self.CheckItem(item, True)
+
+        self.SetColumnWidth(col = 0, width = wx.LIST_AUTOSIZE_USEHEADER)
+        self.SetColumnWidth(col = 1, width = wx.LIST_AUTOSIZE_USEHEADER)
+                
+    def OnCheckItem(self, index, flag):
+        """!Mapset checked/unchecked
+        """
+        pass


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/main.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/main.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/main.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,35 @@
+"""!
+ at package vdigit.main
+
+ at brief wxGUI vector digitizer
+
+Classes:
+ - main::VDigit
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+try:
+    from vdigit.wxdigit import IVDigit, GV_LINES
+    haveVDigit = True
+    errorMsg   = ''
+except (ImportError, NameError), err:
+    haveVDigit = False
+    errorMsg   = err
+    GV_LINES   = -1
+    class IVDigit:
+        def __init__(self):
+            pass
+
+class VDigit(IVDigit):
+    def __init__(self, mapwindow):
+        """!Base class of vector digitizer
+        
+        @param mapwindow reference to mapwindow (mapdisp_window.BufferedWindow) instance
+        """
+        IVDigit.__init__(self, mapwindow)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/mapwindow.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/mapwindow.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/mapwindow.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1074 @@
+"""!
+ at package vdigit.mapwindow
+
+ at brief Map display canvas for wxGUI vector digitizer
+
+Classes:
+ - mapwindow::VDigitWindow
+
+(C) 2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import wx
+
+from dbmgr.dialogs  import DisplayAttributesDialog
+from core.gcmd      import RunCommand, GMessage, GError
+from core.debug     import Debug
+from mapdisp.mapwindow import BufferedWindow
+from core.settings  import UserSettings
+from core.utils     import ListOfCatsToRange
+from core.globalvar import QUERYLAYER
+from vdigit.dialogs import VDigitCategoryDialog, VDigitZBulkDialog, VDigitDuplicatesDialog
+
+class VDigitWindow(BufferedWindow):
+    """!A Buffered window extended for vector digitizer.
+    """
+    def __init__(self, parent, id = wx.ID_ANY,
+                 Map = None, tree = None, lmgr = None,
+                 style = wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
+        BufferedWindow.__init__(self, parent, id, Map, tree, lmgr,
+                                style, **kwargs)
+        
+        self.pdcVector = wx.PseudoDC()
+        self.toolbar   = self.parent.GetToolbar('vdigit')
+        self.digit     = None # wxvdigit.IVDigit
+        
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
+        
+    def GetDisplay(self):
+        if self.digit:
+            return self.digit.GetDisplay()
+        return None
+
+    def SetToolbar(self, toolbar):
+        """!Set up related toolbar
+        """
+        self.toolbar = toolbar
+        
+    def _onMotion(self, coord, precision):
+        """!Track mouse motion and update statusbar (see self.Motion)
+
+        @parem coord easting, northing
+        @param precision formatting precision
+        """
+        e, n = coord
+        
+        if self.toolbar.GetAction() != 'addLine' or \
+                self.toolbar.GetAction('type') not in ('line', 'boundary') or \
+                len(self.polycoords) == 0:
+            return False
+        
+        # for linear feature show segment and total length
+        distance_seg = self.Distance(self.polycoords[-1],
+                                     (e, n), screen = False)[0]
+        distance_tot = distance_seg
+        for idx in range(1, len(self.polycoords)):
+            distance_tot += self.Distance(self.polycoords[idx-1],
+                                          self.polycoords[idx],
+                                          screen = False)[0]
+        self.parent.SetStatusText("%.*f, %.*f (seg: %.*f; tot: %.*f)" % \
+                                                (precision, e, precision, n,
+                                                 precision, distance_seg,
+                                                 precision, distance_tot), 0)
+        
+        return True
+    
+    def OnKeyDown(self, event):
+        """!Key pressed"""
+        shift = event.ShiftDown()
+        kc = event.GetKeyCode()
+        
+        event = None
+        if not shift:
+            if kc == ord('P'):
+                event = wx.CommandEvent(winid = self.toolbar.addPoint)
+                tool = self.toolbar.OnAddPoint
+            elif kc == ord('L'):
+                event = wx.CommandEvent(winid = self.toolbar.addLine)
+                tool = self.toolbar.OnAddLine
+        if event:
+            self.toolbar.OnTool(event)
+            tool(event)
+        
+    def _updateMap(self):
+        if not self.toolbar or \
+                not self.toolbar.GetLayer():
+            return
+        
+        # set region
+        self.digit.GetDisplay().UpdateRegion()
+        # re-calculate threshold for digitization tool
+        # self.parent.digit.GetDisplay().GetThreshold()
+        # draw map
+        # self.pdcVector.Clear()
+        self.pdcVector.RemoveAll()
+        
+        try:
+            item = self.tree.FindItemByData('maplayer', self.toolbar.GetLayer())
+        except TypeError:
+            item = None
+        
+        if item and self.tree.IsItemChecked(item):
+            self.redrawAll = True
+            self.digit.GetDisplay().DrawMap()
+        
+        # translate tmp objects (pointer position)
+        if self.toolbar.GetAction() == 'moveLine' and \
+                hasattr(self, "moveInfo"):
+            if 'beginDiff' in self.moveInfo:
+                # move line
+                for id in self.moveInfo['id']:
+                    self.pdcTmp.TranslateId(id,
+                                            self.moveInfo['beginDiff'][0],
+                                            self.moveInfo['beginDiff'][1])
+                del self.moveInfo['beginDiff']
+        
+    def OnLeftDownAddLine(self, event):
+        """!Left mouse button pressed - add new feature
+        """
+        try:
+            mapLayer = self.toolbar.GetLayer().GetName()
+        except:
+            return
+        
+        if self.toolbar.GetAction('type') in ['point', 'centroid']:
+            # add new point / centroiud
+            east, north = self.Pixel2Cell(self.mouse['begin'])
+            nfeat, fids = self.digit.AddFeature(self.toolbar.GetAction('type'), [(east, north)])
+            if nfeat < 1:
+                return
+            
+            self.UpdateMap(render = False) # redraw map
+            
+            # add new record into atribute table
+            if UserSettings.Get(group = 'vdigit', key = "addRecord", subkey = 'enabled'):
+                # select attributes based on layer and category
+                cats = { fids[0] : {
+                        UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value') :
+                            (UserSettings.Get(group = 'vdigit', key = "category", subkey = 'value'), )
+                        }}
+                
+                posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
+                                                 self.mouse['end'][1] + self.dialogOffset))
+                
+                addRecordDlg = DisplayAttributesDialog(parent = self, map = mapLayer,
+                                                       cats = cats,
+                                                       pos = posWindow,
+                                                       action = "add", ignoreError = True)
+                
+                if self.toolbar.GetAction('type') == 'centroid':
+                    for fid in fids:
+                        self._geomAttrb(fid, addRecordDlg, 'area')
+                        self._geomAttrb(fid, addRecordDlg, 'perimeter')
+                
+                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()
+                    
+                    RunCommand('db.execute',
+                               parent = self,
+                               quiet = True, 
+                               input = sqlfile.name)
+                
+                if addRecordDlg.mapDBInfo:
+                    self._updateATM()
+        
+        elif self.toolbar.GetAction('type') in ["line", "boundary", "area"]:
+            # add new point to the line
+            self.polycoords.append(self.Pixel2Cell(event.GetPositionTuple()[:]))
+            self.DrawLines(pdc = self.pdcTmp)
+        
+    def _geomAttrb(self, fid, dialog, attrb):
+        """!Define geometry attributes
+        """
+        mapLayer = self.toolbar.GetLayer()
+        item = self.tree.FindItemByData('maplayer', mapLayer)
+        vdigit = self.tree.GetPyData(item)[0]['vdigit']
+        if not vdigit or \
+                'geomAttr' not in vdigit or \
+                attrb not in vdigit['geomAttr']:
+            return
+        
+        val = -1
+        if attrb == 'length':
+            val = self.digit.GetLineLength(fid)
+            type = attrb
+        elif attrb == 'area':
+            val = self.digit.GetAreaSize(fid)
+            type = attrb
+        elif attrb == 'perimeter':
+            val = self.digit.GetAreaPerimeter(fid)
+            type = 'length'
+        
+        if val > 0:
+            layer = int(UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value'))
+            column = vdigit['geomAttr'][attrb]['column']
+            val = UnitsConvertValue(val, type, vdigit['geomAttr'][attrb]['units'])
+            dialog.SetColumnValue(layer, column, val)
+            dialog.OnReset()
+        
+    def _geomAttrbUpdate(self, fids):
+        """!Update geometry atrributes of currently selected features
+        
+        @param fid list feature id
+        """
+        mapLayer = self.parent.toolbars['vdigit'].GetLayer()
+        vectorName =  mapLayer.GetName()
+        item = self.tree.FindItemByData('maplayer', mapLayer)
+        vdigit = self.tree.GetPyData(item)[0]['vdigit']
+        
+        if vdigit is None or 'geomAttr' not in vdigit:
+            return
+        
+        dbInfo = gselect.VectorDBInfo(vectorName)
+        sqlfile = tempfile.NamedTemporaryFile(mode = "w")
+        for fid in fids:
+            for layer, cats in self.digit.GetLineCats(fid).iteritems():
+                table = dbInfo.GetTable(layer)
+                for attrb, item in vdigit['geomAttr'].iteritems():
+                    val = -1
+                    if attrb == 'length':
+                        val = self.digit.GetLineLength(fid)
+                        type = attrb
+                    elif attrb == 'area':
+                        val = self.digit.GetAreaSize(fid)
+                        type = attrb
+                    elif attrb == 'perimeter':
+                        val = self.digit.GetAreaPerimeter(fid)
+                        type = 'length'
+                    
+                    if val < 0:
+                        continue
+                    val = UnitsConvertValue(val, type, item['units'])
+                    
+                    for cat in cats:
+                        sqlfile.write('UPDATE %s SET %s = %f WHERE %s = %d;\n' % \
+                                          (table, item['column'], val,
+                                           dbInfo.GetKeyColumn(layer), cat))
+            
+            sqlfile.file.flush()
+            RunCommand('db.execute',
+                       parent = True,
+                       quiet = True,
+                       input = sqlfile.name)
+            
+    def _updateATM(self):
+        """!Update open Attribute Table Manager
+        
+        @todo: use AddDataRow() instead
+        """
+        # update ATM
+        digitVector = self.toolbar.GetLayer().GetName()
+                            
+        for atm in self.lmgr.dialogs['atm']:
+            atmVector = atm.GetVectorName()
+            if atmVector == digitVector:
+                layer = UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value')
+                # TODO: use AddDataRow instead
+                atm.LoadData(layer)
+        
+    def OnLeftDownEditLine(self, event):
+        """!Left mouse button pressed - edit linear feature - add new
+        vertex.
+        """
+        self.polycoords.append(self.Pixel2Cell(self.mouse['begin']))
+        self.moveInfo['id'].append(wx.NewId())
+        self.DrawLines(pdc = self.pdcTmp)
+        
+    def OnLeftDownMoveLine(self, event):
+        """!Left mouse button pressed - vector digitizer move
+        feature/vertex, edit linear feature
+        """
+        self.moveInfo = dict()
+        # geographic coordinates of initial position (left-down)
+        self.moveInfo['begin'] = None
+        # list of ids to modify    
+        self.moveInfo['id'] = list()
+        
+        # set pen
+        if self.toolbar.GetAction() in ["moveVertex", "editLine"]:
+            pcolor = UserSettings.Get(group = 'vdigit', key = "symbol",
+                                      subkey = ["highlight", "color"])
+            self.pen = self.polypen = wx.Pen(colour = pcolor,
+                                             width = 2, style = wx.SHORT_DASH)
+            self.pdcTmp.SetPen(self.polypen)
+        
+    def OnLeftDownDisplayCA(self, event):
+        """!Left mouse button pressed - vector digitizer display categories
+        or attributes action
+        """
+        try:
+            mapLayer = self.toolbar.GetLayer().GetName()
+        except:
+            return
+        
+        coords = self.Pixel2Cell(self.mouse['begin'])
+        
+        # unselect
+        self.digit.GetDisplay().SetSelected([])
+        
+        # select feature by point
+        cats = {}
+        self.digit.GetDisplay().SelectLineByPoint(coords) 
+        
+        if not self.digit.GetDisplay().GetSelected():
+            for key in ('attributes', 'category'):
+                if self.parent.dialogs[key] and \
+                        self.parent.dialogs[key].IsShown():
+                    self.parent.dialogs[key].Hide()
+            self.UpdateMap(render = False, renderVector = True)
+            return
+        
+        if UserSettings.Get(group = 'vdigit', key = 'checkForDupl',
+                            subkey = 'enabled'):
+            lines = self.digit.GetDisplay().GetSelected()
+        else:
+            lines = (self.digit.GetDisplay().GetSelected()[0],) # only first found
+                        
+        for line in lines:
+            cats[line] = self.digit.GetLineCats(line)
+        
+        posWindow = self.ClientToScreen((self.mouse['end'][0] + self.dialogOffset,
+                                         self.mouse['end'][1] + self.dialogOffset))
+        
+        if self.toolbar.GetAction() == "displayAttrs":
+            # select attributes based on coordinates (all layers)
+            if self.parent.dialogs['attributes'] is None:
+                self.parent.dialogs['attributes'] = \
+                    DisplayAttributesDialog(parent = self, map = mapLayer,
+                                            cats = cats,
+                                            action = "update")
+            else:
+                # upgrade dialog
+                self.parent.dialogs['attributes'].UpdateDialog(cats = cats)
+           
+            if self.parent.dialogs['attributes'] and \
+                    self.parent.dialogs['attributes'].mapDBInfo:
+                if len(cats.keys()) > 0:
+                    # highlight feature & re-draw map
+                    if not self.parent.dialogs['attributes'].IsShown():
+                        self.parent.dialogs['attributes'].Show()
+                else:
+                    if self.parent.dialogs['attributes'] and \
+                            self.parent.dialogs['attributes'].IsShown():
+                        self.parent.dialogs['attributes'].Hide()
+        
+        else: # displayCats
+            if self.parent.dialogs['category'] is None:
+                # open new dialog
+                dlg = VDigitCategoryDialog(parent = self,
+                                           vectorName = mapLayer,
+                                           cats = cats,
+                                           pos = posWindow,
+                                           title = _("Update categories"))
+                self.parent.dialogs['category'] = dlg
+            else:
+                # update currently open dialog
+                self.parent.dialogs['category'].UpdateDialog(cats = cats)
+                
+            if self.parent.dialogs['category']:
+                if len(cats.keys()) > 0:
+                    # highlight feature & re-draw map
+                    if not self.parent.dialogs['category'].IsShown():
+                        self.parent.dialogs['category'].Show()
+                else:
+                    if self.parent.dialogs['category'].IsShown():
+                        self.parent.dialogs['category'].Hide()
+        
+        self.UpdateMap(render = False, renderVector = True)
+        
+    def OnLeftDownCopyCA(self, event):
+        """!Left mouse button pressed - vector digitizer copy
+        categories or attributes action
+        """
+        if not hasattr(self, "copyCatsList"):
+            self.copyCatsList = []
+        else:
+            self.copyCatsIds = []
+            self.mouse['box'] = 'box'
+        
+    def OnLeftDownCopyLine(self, event):
+        """!Left mouse button pressed - vector digitizer copy lines
+        action
+        """
+        if not hasattr(self, "copyIds"):
+            self.copyIds = []
+            self.layerTmp = None
+        
+    def OnLeftDownBulkLine(self, event):
+        """!Left mouse button pressed - vector digitizer label 3D
+        vector lines
+        """
+        if len(self.polycoords) > 1: # start new line
+            self.polycoords = []
+            self.ClearLines(pdc = self.pdcTmp)
+        self.polycoords.append(self.Pixel2Cell(event.GetPositionTuple()[:]))
+        if len(self.polycoords) == 1:
+            begin = self.Pixel2Cell(self.polycoords[-1])
+            end   = self.Pixel2Cell(self.mouse['end'])
+        else:
+            end   = self.Pixel2Cell(self.polycoords[-1])
+            begin = self.Pixel2Cell(self.mouse['begin'])
+            
+            self.DrawLines(self.pdcTmp, polycoords = (begin, end))
+        
+    def OnLeftDownUndo(self, event):
+        """!Left mouse button pressed with control key - vector
+        digitizer undo functionality
+        """
+        if self.mouse["use"] != "pointer" or not self.toolbar:
+            return
+        
+        action = self.toolbar.GetAction()
+        if (action == "addLine" and \
+                self.toolbar.GetAction('type') in ["line", "boundary", "area"]) or \
+                action == "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 action == "editLine":
+            # remove last vertex & line
+            if len(self.moveInfo['id']) > 1:
+                self.moveInfo['id'].pop()
+                
+            self.UpdateMap(render = False, renderVector = False)
+            
+        elif action in ["deleteLine", "moveLine", "splitLine",
+                        "addVertex", "removeVertex", "moveVertex",
+                        "copyCats", "flipLine", "mergeLine",
+                        "snapLine", "connectLine", "copyLine",
+                        "queryLine", "breakLine", "typeConv"]:
+            # varios tools -> unselected selected features
+            self.digit.GetDisplay().SetSelected([])
+            if action in ["moveLine", "moveVertex", "editLine"] and \
+                    hasattr(self, "moveInfo"):
+                del self.moveInfo
+                
+            elif action == "copyCats":
+                try:
+                    del self.copyCatsList
+                    del self.copyCatsIds
+                except AttributeError:
+                    pass
+                
+            elif action == "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 action == "zbulkLine":
+            # reset polyline
+            self.polycoords = []
+            self.digit.GetDisplay().SetSelected([])
+            self.UpdateMap(render = False)
+        
+        self.redrawAll = True
+        self.UpdateMap(render = False, renderVector = False)
+
+    def _onLeftDown(self, event):
+        """!Left mouse button donw - vector digitizer various actions
+        """
+        try:
+            mapLayer = self.toolbar.GetLayer().GetName()
+        except:
+            GMessage(parent = self,
+                     message = _("No vector map selected for editing."))
+            event.Skip()
+            return
+        
+        action = self.toolbar.GetAction()
+        
+        if not action:
+            GMessage(parent = self,
+                     message = _("Nothing to do. "
+                                 "Choose appropriate tool from digitizer toolbar."))
+            event.Skip()
+            return
+        
+        if action 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 action in ("addVertex",
+                      "removeVertex",
+                      "splitLines"):
+            # unselect
+            self.digit.GetDisplay().SetSelected([])
+        
+        if action == "addLine":
+            self.OnLeftDownAddLine(event)
+        
+        elif action == "editLine" and \
+                hasattr(self, "moveInfo"):
+            self.OnLeftDownEditLine(event)
+
+        elif action in ("moveLine", "moveVertex", "editLine") and \
+                not hasattr(self, "moveInfo"):
+            self.OnLeftDownMoveLine(event)
+        
+        elif action in ("displayAttrs"
+                        "displayCats"):
+            self.OnLeftDownDisplayCA(event)
+            
+        elif action in ("copyCats",
+                        "copyAttrs"):
+            self.OnLeftDownCopyCA(event)
+            
+        elif action == "copyLine":
+            self.OnLeftDownCopyLine(event)
+            
+        elif action == "zbulkLine":
+            self.OnLeftDownBulkLine(event)
+        
+    def OnLeftUpVarious(self, event):
+        """!Left mouse button released - vector digitizer various
+        actions
+        """
+        pos1 = self.Pixel2Cell(self.mouse['begin'])
+        pos2 = self.Pixel2Cell(self.mouse['end'])
+        
+        nselected = 0
+        action = self.toolbar.GetAction()
+        # -> delete line || move line || move vertex
+        if action in ("moveVertex",
+                      "editLine"):
+            if len(self.digit.GetDisplay().GetSelected()) == 0:
+                nselected = self.digit.GetDisplay().SelectLineByPoint(pos1)['point']
+                
+                if action == "editLine":
+                    try:
+                        selVertex = self.digit.GetDisplay().GetSelectedVertex(pos1)[0]
+                    except IndexError:
+                        selVertex = None
+                        
+                    if selVertex:
+                        # self.UpdateMap(render=False)
+                        ids = self.digit.GetDisplay().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.digit.GetDisplay().DrawSelected(False) 
+                                
+                        if selVertex < ids[-1] / 2:
+                            # choose first or last node of line
+                            self.moveInfo['id'].reverse()
+                            self.polycoords.reverse()
+                    else:
+                        # unselect
+                        self.digit.GetDisplay().SetSelected([])
+                        del self.moveInfo
+                
+                    self.UpdateMap(render = False)
+            
+        elif action in ("copyCats",
+                        "copyAttrs"):
+            if not hasattr(self, "copyCatsIds"):
+                # 'from' -> select by point
+                nselected = self.digit.GetDisplay().SelectLineByPoint(pos1)['point']
+                if nselected:
+                    self.copyCatsList = self.digit.GetDisplay().GetSelected()
+            else:
+                # -> 'to' -> select by bbox
+                self.digit.GetDisplay().SetSelected([])
+                # return number of selected features (by box/point)
+                nselected = self.digit.GetDisplay().SelectLinesByBox((pos1, pos2))
+                if nselected == 0:
+                    if self.digit.GetDisplay().SelectLineByPoint(pos1) is not None:
+                        nselected = 1
+                        
+                if nselected > 0:
+                    self.copyCatsIds = self.digit.GetDisplay().GetSelected()
+        
+        elif action == "queryLine":
+            selected = self.digit.SelectLinesByQuery(bbox = (pos1, pos2))
+            nselected = len(selected)
+            if nselected > 0:
+                self.digit.GetDisplay().SetSelected(selected)
+        
+        else:
+            # -> moveLine || deleteLine, etc. (select by point/box)
+            if action == 'moveLine' and \
+                    len(self.digit.GetDisplay().GetSelected()) > 0:
+                nselected = 0
+            else:
+                if action == 'moveLine':
+                    drawSeg = True
+                else:
+                    drawSeg = False
+                
+                nselected = self.digit.GetDisplay().SelectLinesByBox(bbox = (pos1, pos2),
+                                                                     drawSeg = drawSeg)
+                if nselected == 0:
+                    if self.digit.GetDisplay().SelectLineByPoint(pos1) is not None:
+                        nselected = 1
+        
+        if nselected > 0:
+            if action in ("moveLine", "moveVertex") and \
+                    hasattr(self, "moveInfo"):
+                # get pseudoDC id of objects which should be redrawn
+                if action == "moveLine":
+                    # -> move line
+                    self.moveInfo['id'] = self.digit.GetDisplay().GetSelected(grassId = False)
+                else: # moveVertex
+                    self.moveInfo['id'] = self.digit.GetDisplay().GetSelectedVertex(pos1)
+                    if len(self.moveInfo['id']) == 0: # no vertex found
+                        self.digit.GetDisplay().SetSelected([])
+            
+            #
+            # check for duplicates
+            #
+            if UserSettings.Get(group = 'vdigit', key = 'checkForDupl', subkey = 'enabled'):
+                dupl = self.digit.GetDisplay().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:
+                        self.digit.GetDisplay().UnSelect(dlg.GetUnSelected())
+                        # update selected
+                        self.UpdateMap(render = False)
+                
+            if action != "editLine":
+                # -> move line || move vertex
+                self.UpdateMap(render = False)
+        
+        else: # no vector object found
+            if not (action in ("moveLine",
+                                                 "moveVertex") and \
+                        hasattr(self, "moveInfo") and \
+                        len(self.moveInfo['id']) > 0):
+                # avoid left-click when features are already selected
+                self.UpdateMap(render = False, renderVector = False)
+        
+    def OnLeftUpModifyLine(self, event):
+        """!Left mouse button released - vector digitizer split line,
+        add/remove vertex action
+        """
+        pos1 = self.Pixel2Cell(self.mouse['begin'])
+        
+        pointOnLine = self.digit.GetDisplay().SelectLineByPoint(pos1)['point']
+        if not pointOnLine:
+            return
+        
+        if self.toolbar.GetAction() in ["splitLine", "addVertex"]:
+            self.UpdateMap(render = False) # highlight object
+            self.DrawCross(pdc = self.pdcTmp, coords = self.Cell2Pixel((pointOnLine[0], pointOnLine[1])),
+                           size = 5)
+        else: # removeVertex
+            # get only id of vertex
+            try:
+                id = self.digit.GetDisplay().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
+                self.digit.GetDisplay().SetSelected([])
+                self.UpdateMap(render = False)
+        
+    def OnLeftUpCopyLine(self, event):
+        """!Left mouse button released - vector digitizer copy feature
+        action
+        """
+        pos1 = self.Pixel2Cell(self.mouse['begin'])
+        pos2 = self.Pixel2Cell(self.mouse['end'])
+        
+        if UserSettings.Get(group = 'vdigit', key = 'bgmap',
+                            subkey = 'value', internal = True) == '':
+            # no background map -> copy from current vector map layer
+            nselected = self.bdigit.GetDisplay().SelectLinesByBox((pos1, pos2))
+            
+            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 = self.digit.SelectLinesFromBackgroundMap(bbox = (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' % ListOfCatsToRange(self.copyIds),
+                            '-i',
+                            'color=%s' % colorStr,
+                            'fcolor=%s' % colorStr,
+                            'type=point,line,boundary,centroid',
+                            'width=2']
+                
+                if not self.layerTmp:
+                    self.layerTmp = self.Map.AddLayer(type = 'vector',
+                                                      name = QUERYLAYER,
+                                                      command = dVectTmp)
+                else:
+                    self.layerTmp.SetCmd(dVectTmp)
+            else:
+                if self.layerTmp:
+                    self.Map.DeleteLayer(self.layerTmp)
+                    self.layerTmp = None
+            
+            self.UpdateMap(render = True, renderVector = True)
+            
+    def OnLeftUpBulkLine(self, event):
+        """!Left mouse button released - vector digitizer z-bulk line
+        action
+        """
+        # select lines to be labeled
+        pos1 = self.polycoords[0]
+        pos2 = self.polycoords[1]
+        nselected = self.digit.GetDisplay().SelectLinesByBox((pos1, pos2))
+        
+        if nselected > 0:
+            # highlight selected features
+            self.UpdateMap(render = False)
+            self.DrawLines(pdc = self.pdcTmp) # redraw temp line
+        else:
+            self.UpdateMap(render = False, renderVector = False)
+            
+    def OnLeftUpConnectLine(self, event):
+        """!Left mouse button released - vector digitizer connect line
+        action
+        """
+        if len(self.digit.GetDisplay().GetSelected()) > 0:
+            self.UpdateMap(render = False)
+        
+    def _onLeftUp(self, event):
+        """!Left mouse button released"""
+        if hasattr(self, "moveInfo"):
+            if len(self.digit.GetDisplay().GetSelected()) == 0:
+                self.moveInfo['begin'] = self.Pixel2Cell(self.mouse['begin']) # left down
+            
+            # eliminate initial mouse moving efect
+            self.mouse['begin'] = self.mouse['end'] 
+        
+        action = self.toolbar.GetAction()
+        if action in ("deleteLine",
+                      "moveLine",
+                      "moveVertex",
+                      "copyCats",
+                      "copyAttrs",
+                      "editLine",
+                      "flipLine",
+                      "mergeLine",
+                      "snapLine",
+                      "queryLine",
+                      "breakLine",
+                      "typeConv",
+                      "connectLine"):
+            self.OnLeftUpVarious(event)
+        
+        elif action in ("splitLine",
+                        "addVertex",
+                        "removeVertex"):
+            self.OnLeftUpModifyLine(event)
+        
+        elif action == "copyLine":
+            self.OnLeftUpCopyLine(event)
+            
+        elif action == "zbulkLine" and \
+                len(self.polycoords) == 2:
+            self.OnLeftUpBulkLine(event)
+        
+        elif action == "connectLine":
+            self.OnLeftUpConnectLine(event)
+        
+        if len(self.digit.GetDisplay().GetSelected()) > 0:
+            self.redrawAll = None
+        
+    def _onRightDown(self, event):
+        # digitization tool (confirm action)
+        action = self.toolbar.GetAction()
+        if action in ("moveLine", "moveVertex") and \
+                hasattr(self, "moveInfo"):
+            pFrom = self.moveInfo['begin']
+            pTo = self.Pixel2Cell(event.GetPositionTuple())
+            
+            move = (pTo[0] - pFrom[0],
+                    pTo[1] - pFrom[1])
+            
+            if action == "moveLine":
+                # move line
+                if self.digit.MoveSelectedLines(move) < 0:
+                    return
+            elif action == "moveVertex":
+                # move vertex
+                fid = self.digit.MoveSelectedVertex(pFrom, move)
+                if fid < 0:
+                    return
+                
+                self._geomAttrbUpdate([fid,])
+            
+            del self.moveInfo
+        
+    def _onRightUp(self, event):
+        """!Right mouse button released (confirm action)
+        """
+        action = self.toolbar.GetAction()
+        if action == "addLine" and \
+                self.toolbar.GetAction('type') in ["line", "boundary", "area"]:
+            # -> add new line / boundary
+            try:
+                mapName = self.toolbar.GetLayer().GetName()
+            except:
+                mapName = None
+                GError(parent = self,
+                       message = _("No vector map selected for editing."))
+                    
+            if mapName:
+                if self.toolbar.GetAction('type') == 'line':
+                    line = True
+                else:
+                    line = False
+                    
+                if len(self.polycoords) < 2: # ignore 'one-point' lines
+                    return
+                    
+                nfeat, fids = self.digit.AddFeature(self.toolbar.GetAction('type'), self.polycoords)
+                if nfeat < 0:
+                    return
+                    
+                position = self.Cell2Pixel(self.polycoords[-1])
+                self.polycoords = []
+                self.UpdateMap(render = False)
+                self.redrawAll = True
+                self.Refresh()
+                
+                # add new record into atribute table
+                if UserSettings.Get(group = 'vdigit', key = "addRecord", subkey = 'enabled') and \
+                        (line is True or \
+                             (not line and nfeat > 0)):
+                    posWindow = self.ClientToScreen((position[0] + self.dialogOffset,
+                                                     position[1] + self.dialogOffset))
+                        
+                    # select attributes based on layer and category
+                    cats = { fids[0] : {
+                            UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value') :
+                                (UserSettings.Get(group = 'vdigit', key = "category", subkey = 'value'), )
+                            }}
+                    
+                    addRecordDlg = DisplayAttributesDialog(parent = self, map = mapName,
+                                                           cats = cats,
+                                                           pos = posWindow,
+                                                           action = "add", ignoreError = True)
+                    
+                    for fid in fids:
+                        self._geomAttrb(fid, addRecordDlg, 'length')
+                        # auto-placing centroid
+                        self._geomAttrb(fid, addRecordDlg, 'area')
+                        self._geomAttrb(fid, addRecordDlg, 'perimeter')
+
+                    
+                    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()
+                        RunCommand('db.execute',
+                                   parent = True,
+                                   quiet = True,
+                                   input = sqlfile.name)
+                        
+                    if addRecordDlg.mapDBInfo:
+                        self._updateATM()
+            
+        elif action == "deleteLine":
+            # -> delete selected vector features
+            if self.digit.DeleteSelectedLines() < 0:
+                return
+            self._updateATM()
+        elif action == "splitLine":
+            # split line
+            if self.digit.SplitLine(self.Pixel2Cell(self.mouse['begin'])) < 0:
+                return
+        elif action == "addVertex":
+            # add vertex
+            fid = self.digit.AddVertex(self.Pixel2Cell(self.mouse['begin']))
+            if fid < 0:
+                return
+        elif action == "removeVertex":
+            # remove vertex
+            fid = self.digit.RemoveVertex(self.Pixel2Cell(self.mouse['begin']))
+            if fid < 0:
+                return
+            self._geomAttrbUpdate([fid,])
+        elif action in ("copyCats", "copyAttrs"):
+            if action == 'copyCats':
+                if self.digit.CopyCats(self.copyCatsList,
+                                       self.copyCatsIds, copyAttrb = False) < 0:
+                    return
+            else:
+                if self.digit.CopyCats(self.copyCatsList,
+                                       self.copyCatsIds, copyAttrb = True) < 0:
+                    return
+                
+            del self.copyCatsList
+            del self.copyCatsIds
+            
+            self._updateATM()
+                
+        elif action == "editLine" and \
+                hasattr(self, "moveInfo"):
+            line = self.digit.GetDisplay().GetSelected()[0]
+            if self.digit.EditLine(line, self.polycoords) < 0:
+                return
+                
+            del self.moveInfo
+                
+        elif action == "flipLine":
+            if self.digit.FlipLine() < 0:
+                return
+        elif action == "mergeLine":
+            if self.digit.MergeLine() < 0:
+                return
+        elif action == "breakLine":
+            if self.digit.BreakLine() < 0:
+                return
+        elif action == "snapLine":
+            if self.digit.SnapLine() < 0:
+                return
+        elif action == "connectLine":
+            if len(self.digit.GetDisplay().GetSelected()) > 1:
+                if self.digit.ConnectLine() < 0:
+                    return
+        elif action == "copyLine":
+            if self.digit.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 action == "zbulkLine" and len(self.polycoords) == 2:
+            pos1 = self.polycoords[0]
+            pos2 = self.polycoords[1]
+            
+            selected = self.digit.GetDisplay().GetSelected()
+            dlg = VDigitZBulkDialog(parent = self, title = _("Z bulk-labeling dialog"),
+                                    nselected = len(selected))
+            if dlg.ShowModal() == wx.ID_OK:
+                if self.digit.ZBulkLines(pos1, pos2, dlg.value.GetValue(),
+                                         dlg.step.GetValue()) < 0:
+                    return
+            self.UpdateMap(render = False)
+        elif action == "typeConv":
+            # -> feature type conversion
+            # - point <-> centroid
+            # - line <-> boundary
+            if self.digit.TypeConvForSelectedLines() < 0:
+                return
+
+        if action != "addLine":
+            # unselect and re-render
+            self.digit.GetDisplay().SetSelected([])
+            self.polycoords = []
+            self.UpdateMap(render = False)
+        
+    def _onMouseMoving(self, event):
+        self.mouse['end'] = event.GetPositionTuple()[:]
+        
+        Debug.msg (5, "BufferedWindow.OnMouseMoving(): coords=%f,%f" % \
+                       (self.mouse['end'][0], self.mouse['end'][1]))
+
+        action = self.toolbar.GetAction()
+        if action == "addLine" and \
+                self.toolbar.GetAction('type') in ["line", "boundary", "area"]:
+            if len(self.polycoords) > 0:
+                self.MouseDraw(pdc = self.pdcTmp, begin = self.Cell2Pixel(self.polycoords[-1]))
+        
+        elif action in ["moveLine", "moveVertex", "editLine"] \
+                and hasattr(self, "moveInfo"):
+            dx = self.mouse['end'][0] - self.mouse['begin'][0]
+            dy = self.mouse['end'][1] - self.mouse['begin'][1]
+        
+            # draw lines on new position
+            if action == "moveLine" and \
+                    len(self.moveInfo['id']) > 0:
+                # move line
+                for id in self.moveInfo['id']:
+                    self.pdcTmp.TranslateId(id, dx, dy)
+            elif action in ["moveVertex", "editLine"]:
+                # move vertex ->
+                # (vertex, left vertex, left line,
+                # right vertex, right line)
+                
+                # do not draw static lines
+                if action == "moveVertex" and \
+                        len(self.moveInfo['id']) > 0:
+                    self.polycoords = []
+                    self.pdcTmp.RemoveId(self.moveInfo['id'][0])
+                    if self.moveInfo['id'][1] > 0: # previous vertex
+                        x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.moveInfo['id'][1])[0:2])
+                        self.pdcTmp.RemoveId(self.moveInfo['id'][1] + 1)
+                        self.polycoords.append((x, y))
+                    self.polycoords.append(self.Pixel2Cell(self.mouse['end']))
+
+                    if self.moveInfo['id'][2] > 0: # next vertex
+                        x, y = self.Pixel2Cell(self.pdcTmp.GetIdBounds(self.moveInfo['id'][2])[0:2])
+                        self.pdcTmp.RemoveId(self.moveInfo['id'][2]-1)
+                        self.polycoords.append((x, y))
+                    
+                    self.ClearLines(pdc = self.pdcTmp)
+                    self.DrawLines(pdc = self.pdcTmp)
+                        
+                if action == "editLine":
+                    self.MouseDraw(pdc = self.pdcTmp,
+                                   begin = self.Cell2Pixel(self.polycoords[-1]))
+                
+            self.Refresh() # TODO: use RefreshRect()
+            self.mouse['begin'] = self.mouse['end']
+            
+        elif action == "zbulkLine":
+            if len(self.polycoords) == 1:
+                # draw mouse moving
+                self.MouseDraw(self.pdcTmp)
+                
+    def _zoom(self, event):
+        tmp1 = self.mouse['end']
+        tmp2 = self.Cell2Pixel(self.moveInfo['begin'])
+        dx = tmp1[0] - tmp2[0]
+        dy = tmp1[1] - tmp2[1]
+        self.moveInfo['beginDiff'] = (dx, dy)
+        for id in self.moveInfo['id']:
+            self.pdcTmp.RemoveId(id)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/preferences.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/preferences.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/preferences.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,782 @@
+"""!
+ at package vdigit.preferences
+
+ at brief wxGUI vector digitizer preferences dialogs
+
+Classes:
+ - preferences::VDigitSettingsDialog
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import textwrap
+
+import wx
+import wx.lib.colourselect as csel
+
+from core             import globalvar
+from core.debug       import Debug
+from gui_core.gselect import ColumnSelect
+from core.units       import Units
+from core.settings    import UserSettings
+
+class VDigitSettingsDialog(wx.Dialog):
+    def __init__(self, parent, title, style = wx.DEFAULT_DIALOG_STYLE):
+        """!Standard settings dialog for digitization purposes
+        """
+        wx.Dialog.__init__(self, parent = parent, id = wx.ID_ANY, title = title, style = style)
+        
+        self.parent = parent # mapdisplay.MapFrame class instance
+        self.digit = self.parent.MapWindow.digit
+        
+        # notebook
+        notebook = wx.Notebook(parent = self, id = wx.ID_ANY, style = wx.BK_DEFAULT)
+        self._createSymbologyPage(notebook)
+        self.digit.SetCategory()
+        self._createGeneralPage(notebook)
+        self._createAttributesPage(notebook)
+        self._createQueryPage(notebook)
+
+        # buttons
+        btnApply = wx.Button(self, wx.ID_APPLY)
+        btnCancel = wx.Button(self, wx.ID_CANCEL)
+        btnSave = wx.Button(self, wx.ID_SAVE)
+        btnSave.SetDefault()
+
+        # bindigs
+        btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+        btnApply.SetToolTipString(_("Apply changes for this session"))
+        btnApply.SetDefault()
+        btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
+        btnSave.SetToolTipString(_("Close dialog and save changes to user settings file"))
+        btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+        btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
+        
+        # sizers
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(btnCancel)
+        btnSizer.AddButton(btnApply)
+        btnSizer.AddButton(btnSave)
+        btnSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = notebook, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)
+        mainSizer.Add(item = btnSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
+        
+        self.Bind(wx.EVT_CLOSE, self.OnCancel)
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+
+    def _createSymbologyPage(self, notebook):
+        """!Create notebook page concerning symbology settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("Symbology"))
+
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        
+        flexSizer = wx.FlexGridSizer (cols = 3, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(0)
+
+        self.symbology = {}
+        for label, key in self._symbologyData():
+            textLabel = wx.StaticText(panel, wx.ID_ANY, label)
+            color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                      colour = UserSettings.Get(group = 'vdigit', key = 'symbol',
+                                                              subkey = [key, 'color']), size = globalvar.DIALOG_COLOR_SIZE)
+            isEnabled = UserSettings.Get(group = 'vdigit', key = 'symbol',
+                                         subkey = [key, 'enabled'])
+            if isEnabled is not None:
+                enabled = wx.CheckBox(panel, id = wx.ID_ANY, label = "")
+                enabled.SetValue(isEnabled)
+                self.symbology[key] = (enabled, color)
+            else:
+                enabled = (1, 1)
+                self.symbology[key] = (None, color)
+            
+            flexSizer.Add(textLabel, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+            flexSizer.Add(enabled, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
+            flexSizer.Add(color, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
+            color.SetName("GetColour")
+        
+        sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 10)
+        
+        panel.SetSizer(sizer)
+        
+        return panel
+
+    def _createGeneralPage(self, notebook):
+        """!Create notebook page concerning general settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("General"))
+
+        border = wx.BoxSizer(wx.VERTICAL)
+        
+        #
+        # display section
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Display"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        flexSizer = wx.FlexGridSizer (cols = 3, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(0)
+        # line width
+        text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Line width"))
+        self.lineWidthValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (75, -1),
+                                          initial = UserSettings.Get(group = 'vdigit', key = "lineWidth", subkey = 'value'),
+                                          min = 1, max = 1e6)
+        units = wx.StaticText(parent = panel, id = wx.ID_ANY, size = (115, -1),
+                              label = UserSettings.Get(group = 'vdigit', key = "lineWidth", subkey = 'units'),
+                              style = wx.ALIGN_LEFT)
+        flexSizer.Add(text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(self.lineWidthValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
+        flexSizer.Add(units, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL | wx.LEFT,
+                      border = 10)
+
+        sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+
+        #
+        # snapping section
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Snapping"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        flexSizer = wx.FlexGridSizer(cols = 3, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(0)
+
+        # snapping
+        text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Snapping threshold"))
+        self.snappingValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (75, -1),
+                                         initial = UserSettings.Get(group = 'vdigit', key = "snapping", subkey = 'value'),
+                                         min = -1, max = 1e6)
+        self.snappingValue.Bind(wx.EVT_SPINCTRL, self.OnChangeSnappingValue)
+        self.snappingValue.Bind(wx.EVT_TEXT, self.OnChangeSnappingValue)
+        self.snappingUnit = wx.Choice(parent = panel, id = wx.ID_ANY, size = (125, -1),
+                                      choices = [_("screen pixels"), _("map units")])
+        self.snappingUnit.SetStringSelection(UserSettings.Get(group = 'vdigit', key = "snapping", subkey = 'units'))
+        self.snappingUnit.Bind(wx.EVT_CHOICE, self.OnChangeSnappingUnits)
+        flexSizer.Add(text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(self.snappingValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
+        flexSizer.Add(self.snappingUnit, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE)
+
+        vertexSizer = wx.BoxSizer(wx.VERTICAL)
+        self.snapVertex = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                      label = _("Snap also to vertex"))
+        self.snapVertex.SetValue(UserSettings.Get(group = 'vdigit', key = "snapToVertex", subkey = 'enabled'))
+        vertexSizer.Add(item = self.snapVertex, proportion = 0, flag = wx.EXPAND)
+        self.mapUnits = self.parent.MapWindow.Map.GetProjInfo()['units']
+        self.snappingInfo = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                          label = _("Snapping threshold is %(value).1f %(units)s") % \
+                                              {'value' : self.digit.GetDisplay().GetThreshold(),
+                                               'units' : self.mapUnits})
+        vertexSizer.Add(item = self.snappingInfo, proportion = 0,
+                        flag = wx.ALL | wx.EXPAND, border = 1)
+
+        sizer.Add(item = flexSizer, proportion = 1, flag = wx.EXPAND)
+        sizer.Add(item = vertexSizer, proportion = 1, flag = wx.EXPAND)
+        border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+
+        #
+        # select box
+        #
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Select vector features"))
+        # feature type
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        inSizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.selectFeature = {}
+        for feature in ('point', 'line',
+                        'centroid', 'boundary'):
+            chkbox = wx.CheckBox(parent = panel, label = feature)
+            self.selectFeature[feature] = chkbox.GetId()
+            chkbox.SetValue(UserSettings.Get(group = 'vdigit', key = 'selectType',
+                                             subkey = [feature, 'enabled']))
+            inSizer.Add(item = chkbox, proportion = 0,
+                        flag = wx.EXPAND | wx.ALL, border = 5)
+        sizer.Add(item = inSizer, proportion = 0, flag = wx.EXPAND)
+        # threshold
+        flexSizer = wx.FlexGridSizer (cols = 3, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(0)
+        text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Select threshold"))
+        self.selectThreshValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (75, -1),
+                                             initial = UserSettings.Get(group = 'vdigit', key = "selectThresh", subkey = 'value'),
+                                             min = 1, max = 1e6)
+        units = wx.StaticText(parent = panel, id = wx.ID_ANY, size = (115, -1),
+                              label = UserSettings.Get(group = 'vdigit', key = "lineWidth", subkey = 'units'),
+                              style = wx.ALIGN_LEFT)
+        flexSizer.Add(text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(self.selectThreshValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
+        flexSizer.Add(units, proportion = 0, flag = wx.ALIGN_RIGHT | wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL | wx.LEFT,
+                      border = 10)
+
+        self.selectIn = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                    label = _("Select only features inside of selection bounding box"))
+        self.selectIn.SetValue(UserSettings.Get(group = 'vdigit', key = "selectInside", subkey = 'enabled'))
+        self.selectIn.SetToolTipString(_("By default are selected all features overlapping selection bounding box "))
+        
+        self.checkForDupl = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                        label = _("Check for duplicates"))
+        self.checkForDupl.SetValue(UserSettings.Get(group = 'vdigit', key = "checkForDupl", subkey = 'enabled'))
+
+
+        sizer.Add(item = flexSizer, proportion = 0, flag = wx.EXPAND)
+        sizer.Add(item = self.selectIn, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1)
+        sizer.Add(item = self.checkForDupl, proportion = 0, flag = wx.EXPAND | wx.ALL, border = 1)        
+        border.Add(item = sizer, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+
+        #
+        # digitize lines box
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Digitize line features"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        self.intersect = wx.CheckBox(parent = panel, label = _("Break lines at intersection"))
+        self.intersect.SetValue(UserSettings.Get(group = 'vdigit', key = 'breakLines', subkey = 'enabled'))
+        
+        sizer.Add(item = self.intersect, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
+
+        border.Add(item = sizer, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+
+        #
+        # save-on-exit box
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Save changes"))
+        # save changes on exit?
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        self.save = wx.CheckBox(parent = panel, label = _("Save changes on exit"))
+        self.save.SetValue(UserSettings.Get(group = 'vdigit', key = 'saveOnExit', subkey = 'enabled'))
+        sizer.Add(item = self.save, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = sizer, proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
+
+        panel.SetSizer(border)
+        
+        return panel
+
+    def _createQueryPage(self, notebook):
+        """!Create notebook page for query tool"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("Query tool"))
+
+        border = wx.BoxSizer(wx.VERTICAL)
+
+        #
+        # query tool box
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Choose query tool"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        LocUnits = self.parent.MapWindow.Map.GetProjInfo()['units']
+
+        self.queryBox = wx.CheckBox(parent = panel, id = wx.ID_ANY, label = _("Select by box"))
+        self.queryBox.SetValue(UserSettings.Get(group = 'vdigit', key = "query", subkey = 'box'))
+
+        sizer.Add(item = self.queryBox, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
+        sizer.Add((0, 5))
+
+        #
+        # length
+        #
+        self.queryLength = wx.RadioButton(parent = panel, id = wx.ID_ANY, label = _("length"))
+        self.queryLength.Bind(wx.EVT_RADIOBUTTON, self.OnChangeQuery)
+        sizer.Add(item = self.queryLength, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
+        flexSizer = wx.FlexGridSizer (cols = 4, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(0)
+        txt = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Select lines"))
+        self.queryLengthSL = wx.Choice (parent = panel, id = wx.ID_ANY, 
+                                        choices  =  [_("shorter than"), _("longer than")])
+        self.queryLengthSL.SetSelection(UserSettings.Get(group = 'vdigit', key = "queryLength", subkey = 'than-selection'))
+        self.queryLengthValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (100, -1),
+                                            initial = 1,
+                                            min = 0, max = 1e6)
+        self.queryLengthValue.SetValue(UserSettings.Get(group = 'vdigit', key = "queryLength", subkey = 'thresh'))
+        units = wx.StaticText(parent = panel, id = wx.ID_ANY, label = "%s" % LocUnits)
+        flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(self.queryLengthSL, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
+        flexSizer.Add(self.queryLengthValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
+        flexSizer.Add(units, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        sizer.Add(item = flexSizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
+
+        #
+        # dangle
+        #
+        self.queryDangle = wx.RadioButton(parent = panel, id = wx.ID_ANY, label = _("dangle"))
+        self.queryDangle.Bind(wx.EVT_RADIOBUTTON, self.OnChangeQuery)
+        sizer.Add(item = self.queryDangle, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
+        flexSizer = wx.FlexGridSizer (cols = 4, hgap = 5, vgap = 5)
+        flexSizer.AddGrowableCol(0)
+        txt = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Select dangles"))
+        self.queryDangleSL = wx.Choice (parent = panel, id = wx.ID_ANY, 
+                                        choices = [_("shorter than"), _("longer than")])
+        self.queryDangleSL.SetSelection(UserSettings.Get(group = 'vdigit', key = "queryDangle", subkey = 'than-selection'))
+        self.queryDangleValue = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (100, -1),
+                                       initial = 1,
+                                       min = 0, max = 1e6)
+        self.queryDangleValue.SetValue(UserSettings.Get(group = 'vdigit', key = "queryDangle", subkey = 'thresh'))
+        units = wx.StaticText(parent = panel, id = wx.ID_ANY, label = "%s" % LocUnits)
+        flexSizer.Add(txt, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(self.queryDangleSL, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
+        flexSizer.Add(self.queryDangleValue, proportion = 0, flag = wx.ALIGN_CENTER | wx.FIXED_MINSIZE)
+        flexSizer.Add(units, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        sizer.Add(item = flexSizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
+
+        if UserSettings.Get(group = 'vdigit', key = "query", subkey = 'selection') == 0:
+            self.queryLength.SetValue(True)
+        else:
+            self.queryDangle.SetValue(True)
+
+        # enable & disable items
+        self.OnChangeQuery(None)
+
+        border.Add(item = sizer, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 5)
+
+        panel.SetSizer(border)
+        
+        return panel
+
+    def _createAttributesPage(self, notebook):
+        """!Create notebook page for attributes"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        notebook.AddPage(page = panel, text = _("Attributes"))
+
+        border = wx.BoxSizer(wx.VERTICAL)
+
+        #
+        # add new record
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Digitize new feature"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        # checkbox
+        self.addRecord = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                     label = _("Add new record into table"))
+        self.addRecord.SetValue(UserSettings.Get(group = 'vdigit', key = "addRecord", subkey = 'enabled'))
+        sizer.Add(item = self.addRecord, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
+        # settings
+        flexSizer = wx.FlexGridSizer(cols = 2, hgap = 3, vgap = 3)
+        flexSizer.AddGrowableCol(0)
+        settings = ((_("Layer"), 1), (_("Category"), 1), (_("Mode"), _("Next to use")))
+        # layer
+        text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Layer"))
+        self.layer = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (125, -1),
+                                 min = 1, max = 1e3)
+        self.layer.SetValue(int(UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value')))
+        flexSizer.Add(item = text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(item = self.layer, proportion = 0,
+                      flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+        # category number
+        text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Category number"))
+        self.category = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (125, -1),
+                                    initial = UserSettings.Get(group = 'vdigit', key = "category", subkey = 'value'),
+                                    min = -1e9, max = 1e9) 
+        if UserSettings.Get(group = 'vdigit', key = "categoryMode", subkey = 'selection') != 1:
+            self.category.Enable(False)
+        flexSizer.Add(item = text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(item = self.category, proportion = 0,
+                      flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+        # category mode
+        text = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Category mode"))
+        self.categoryMode = wx.Choice(parent = panel, id = wx.ID_ANY, size = (125, -1),
+                                      choices = [_("Next to use"), _("Manual entry"), _("No category")])
+        self.categoryMode.SetSelection(UserSettings.Get(group = 'vdigit', key = "categoryMode", subkey = 'selection'))
+        flexSizer.Add(item = text, proportion = 0, flag = wx.ALIGN_CENTER_VERTICAL)
+        flexSizer.Add(item = self.categoryMode, proportion = 0,
+                      flag = wx.FIXED_MINSIZE | wx.ALIGN_CENTER_VERTICAL)
+
+        sizer.Add(item = flexSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = sizer, proportion = 0,
+                   flag = wx.ALL | wx.EXPAND, border = 5)
+
+        #
+        # delete existing record
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY, label = " %s " % _("Delete existing feature(s)"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        
+        # checkbox
+        self.deleteRecord = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                        label = _("Delete record from table"))
+        self.deleteRecord.SetValue(UserSettings.Get(group = 'vdigit', key = "delRecord", subkey = 'enabled'))
+        sizer.Add(item = self.deleteRecord, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = sizer, proportion = 0,
+                   flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+
+        #
+        # geometry attributes (currently only length and area are supported)
+        #
+        box   = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                              label = " %s " % _("Geometry attributes"))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(hgap = 3, vgap = 3)
+        gridSizer.AddGrowableCol(0)
+        self.geomAttrb = { 'length' : { 'label' : _('length') },
+                           'area' : { 'label' : _('area') },
+                           'perimeter' : { 'label' : _('perimeter') } }
+
+        digitToolbar = self.parent.toolbars['vdigit']
+        try:
+            vectorName = digitToolbar.GetLayer().GetName()
+        except AttributeError:
+            vectorName = None # no vector selected for editing
+        layer = UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value')
+        mapLayer = self.parent.toolbars['vdigit'].GetLayer()
+        tree = self.parent.tree
+        item = tree.FindItemByData('maplayer', mapLayer)
+        row = 0
+        for attrb in ['length', 'area', 'perimeter']:
+            # checkbox
+            check = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+                                label = self.geomAttrb[attrb]['label'])
+            ### self.deleteRecord.SetValue(UserSettings.Get(group='vdigit', key="delRecord", subkey='enabled'))
+            check.Bind(wx.EVT_CHECKBOX, self.OnGeomAttrb)
+            # column (only numeric)
+            column = ColumnSelect(parent = panel, size = (200, -1))
+            column.InsertColumns(vector = vectorName,
+                                 layer = layer, excludeKey = True,
+                                 type = ['integer', 'double precision'])
+            # units 
+            if attrb == 'area':
+                choices = Units.GetUnitsList('area')
+            else:
+                choices = Units.GetUnitsList('length')
+            win_units = wx.Choice(parent = panel, id = wx.ID_ANY,
+                                  choices = choices, size = (120, -1))
+            
+            # default values
+            check.SetValue(False)
+            if item and tree.GetPyData(item)[0]['vdigit'] and \
+                    'geomAttr' in tree.GetPyData(item)[0]['vdigit'] and \
+                    attrb in tree.GetPyData(item)[0]['vdigit']['geomAttr']:
+                check.SetValue(True)
+                column.SetStringSelection(tree.GetPyData(item)[0]['vdigit']['geomAttr'][attrb]['column'])
+                if attrb == 'area':
+                    type = 'area'
+                else:
+                    type = 'length'
+                unitsIdx = Units.GetUnitsIndex(type, 
+                                                tree.GetPyData(item)[0]['vdigit']['geomAttr'][attrb]['units'])
+                win_units.SetSelection(unitsIdx)
+
+            if not vectorName:
+                check.Enable(False)
+                column.Enable(False)
+            
+            if not check.IsChecked():
+                column.Enable(False)
+            
+            self.geomAttrb[attrb]['check']  = check.GetId()
+            self.geomAttrb[attrb]['column'] = column.GetId()
+            self.geomAttrb[attrb]['units']  = win_units.GetId()
+
+            gridSizer.Add(item = check,
+                          flag = wx.ALIGN_CENTER_VERTICAL,
+                          pos = (row, 0))
+            gridSizer.Add(item = column,
+                          pos = (row, 1))
+            gridSizer.Add(item = win_units,
+                          pos = (row, 2))
+            row += 1
+        
+        note = '\n'.join(textwrap.wrap(_("Note: These settings are stored "
+                                         "in the workspace not in the vector digitizer "
+                                         "preferences."), 55))
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = note),
+                      pos = (3, 0), span = (1, 3))
+                      
+        sizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 1)
+        border.Add(item = sizer, proportion = 0,
+                   flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+
+        # bindings
+        self.Bind(wx.EVT_CHECKBOX, self.OnChangeAddRecord, self.addRecord)
+        self.Bind(wx.EVT_CHOICE, self.OnChangeCategoryMode, self.categoryMode)
+        self.Bind(wx.EVT_SPINCTRL, self.OnChangeLayer, self.layer)
+
+        panel.SetSizer(border)
+        
+        return panel
+
+    def _symbologyData(self):
+        """!Data for _createSymbologyPage()
+
+        label | checkbox | color
+        """
+
+        return (
+            #            ("Background", "symbolBackground"),
+            (_("Highlight"), "highlight"),
+            (_("Highlight (duplicates)"), "highlightDupl"),
+            (_("Point"), "point"),
+            (_("Line"), "line"),
+            (_("Boundary (no area)"), "boundaryNo"),
+            (_("Boundary (one area)"), "boundaryOne"),
+            (_("Boundary (two areas)"), "boundaryTwo"),
+            (_("Centroid (in area)"), "centroidIn"),
+            (_("Centroid (outside area)"), "centroidOut"),
+            (_("Centroid (duplicate in area)"), "centroidDup"),
+            (_("Node (one line)"), "nodeOne"),
+            (_("Node (two lines)"), "nodeTwo"),
+            (_("Vertex"), "vertex"),
+            (_("Area (closed boundary + centroid)"), "area"),
+            (_("Direction"), "direction"),)
+
+    def OnGeomAttrb(self, event):
+        """!Register geometry attributes (enable/disable)
+        """
+        checked = event.IsChecked()
+        id = event.GetId()
+        key = None
+        for attrb, val in self.geomAttrb.iteritems():
+            if val['check'] == id:
+                key = attrb
+                break
+        
+        column = self.FindWindowById(self.geomAttrb[key]['column'])
+        if checked:
+            column.Enable()
+        else:
+            column.Enable(False)
+        
+    def OnChangeCategoryMode(self, event):
+        """!Change category mode
+        """
+        mode = event.GetSelection()
+        UserSettings.Set(group = 'vdigit', key = "categoryMode", subkey = 'selection', value = mode)
+        if mode == 1: # manual entry
+            self.category.Enable(True)
+        elif self.category.IsEnabled(): # disable
+            self.category.Enable(False)
+        
+        if mode == 2 and self.addRecord.IsChecked(): # no category
+            self.addRecord.SetValue(False)
+        
+        self.digit.SetCategory()
+        self.category.SetValue(UserSettings.Get(group = 'vdigit', key = 'category', subkey = 'value'))
+
+    def OnChangeLayer(self, event):
+        """!Layer changed
+        """
+        layer = event.GetInt()
+        if layer > 0:
+            UserSettings.Set(group = 'vdigit', key = 'layer', subkey = 'value', value = layer)
+            self.digit.SetCategory()
+            self.category.SetValue(UserSettings.Get(group = 'vdigit', key = 'category', subkey = 'value'))
+            
+        event.Skip()
+
+    def OnChangeAddRecord(self, event):
+        """!Checkbox 'Add new record' status changed
+        """
+        pass
+        # self.category.SetValue(self.digit.SetCategory())
+            
+    def OnChangeSnappingValue(self, event):
+        """!Change snapping value - update static text
+        """
+        value = self.snappingValue.GetValue()
+        
+        if value < 0:
+            region = self.parent.MapWindow.Map.GetRegion()
+            res = (region['nsres'] + region['ewres']) / 2.
+            threshold = self.digit.GetDisplay().GetThreshold(value = res)
+        else:
+            if self.snappingUnit.GetStringSelection() == "map units":
+                threshold = value
+            else:
+                threshold = self.digit.GetDisplay().GetThreshold(value = value)
+            
+        if value == 0:
+            self.snappingInfo.SetLabel(_("Snapping disabled"))
+        elif value < 0:
+            self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s "
+                                         "(based on comp. resolution)") % 
+                                       {'value' : threshold,
+                                        'units' : self.mapUnits.lower()})
+        else:
+            self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s") % 
+                                       {'value' : threshold,
+                                        'units' : self.mapUnits.lower()})
+        
+        event.Skip()
+
+    def OnChangeSnappingUnits(self, event):
+        """!Snapping units change -> update static text
+        """
+        value = self.snappingValue.GetValue()
+        units = self.snappingUnit.GetStringSelection()
+        threshold = self.digit.GetDisplay().GetThreshold(value = value, units = units)
+
+        if units == "map units":
+            self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s") % 
+                                       {'value' : value,
+                                        'units' : self.mapUnits})
+        else:
+            self.snappingInfo.SetLabel(_("Snapping threshold is %(value).1f %(units)s") % 
+                                       {'value' : threshold,
+                                        'units' : self.mapUnits})
+            
+        event.Skip()
+
+    def OnChangeQuery(self, event):
+        """!Change query
+        """
+        if self.queryLength.GetValue():
+            # length
+            self.queryLengthSL.Enable(True)
+            self.queryLengthValue.Enable(True)
+            self.queryDangleSL.Enable(False)
+            self.queryDangleValue.Enable(False)
+        else:
+            # dangle
+            self.queryLengthSL.Enable(False)
+            self.queryLengthValue.Enable(False)
+            self.queryDangleSL.Enable(True)
+            self.queryDangleValue.Enable(True)
+
+    def OnSave(self, event):
+        """!Button 'Save' pressed
+        """
+        self.UpdateSettings()
+        self.parent.toolbars['vdigit'].settingsDialog = None
+
+        fileSettings = {}
+        UserSettings.ReadSettingsFile(settings = fileSettings)
+        fileSettings['vdigit'] = UserSettings.Get(group = 'vdigit')
+        
+        file = UserSettings.SaveToFile(fileSettings)
+        self.parent.GetLayerManager().goutput.WriteLog(_('Vector digitizer settings saved to file <%s>.') % file)
+        
+        self.Destroy()
+
+        event.Skip()
+        
+    def OnApply(self, event):
+        """!Button 'Apply' pressed
+        """
+        self.UpdateSettings()
+
+    def OnCancel(self, event):
+        """!Button 'Cancel' pressed
+        """
+        self.parent.toolbars['vdigit'].settingsDialog = None
+        self.Destroy()
+
+        if event:
+            event.Skip()
+        
+    def UpdateSettings(self):
+        """!Update digitizer settings
+        """
+        self.parent.GetLayerManager().WorkspaceChanged() # geometry attributes
+        # symbology
+        for key, (enabled, color) in self.symbology.iteritems():
+            if enabled:
+                UserSettings.Set(group = 'vdigit', key = 'symbol',
+                                 subkey = [key, 'enabled'],
+                                 value = enabled.IsChecked())
+                UserSettings.Set(group = 'vdigit', key = 'symbol',
+                                 subkey = [key, 'color'],
+                                 value = tuple(color.GetColour()))
+            else:
+                UserSettings.Set(group = 'vdigit', key = 'symbol',
+                                 subkey = [key, 'color'],
+                                 value = tuple(color.GetColour()))
+        # display
+        UserSettings.Set(group = 'vdigit', key = "lineWidth", subkey = 'value',
+                         value = int(self.lineWidthValue.GetValue()))
+
+        # snapping
+        UserSettings.Set(group = 'vdigit', key = "snapping", subkey = 'value',
+                         value = int(self.snappingValue.GetValue()))
+        UserSettings.Set(group = 'vdigit', key = "snapping", subkey = 'units',
+                         value = self.snappingUnit.GetStringSelection())
+        UserSettings.Set(group = 'vdigit', key = "snapToVertex", subkey = 'enabled',
+                         value = self.snapVertex.IsChecked())
+        
+        # digitize new feature
+        UserSettings.Set(group = 'vdigit', key = "addRecord", subkey = 'enabled',
+                         value = self.addRecord.IsChecked())
+        UserSettings.Set(group = 'vdigit', key = "layer", subkey = 'value',
+                         value = int(self.layer.GetValue()))
+        UserSettings.Set(group = 'vdigit', key = "category", subkey = 'value',
+                         value = int(self.category.GetValue()))
+        UserSettings.Set(group = 'vdigit', key = "categoryMode", subkey = 'selection',
+                         value = self.categoryMode.GetSelection())
+
+        # delete existing feature
+        UserSettings.Set(group = 'vdigit', key = "delRecord", subkey = 'enabled',
+                         value = self.deleteRecord.IsChecked())
+
+        # geometry attributes (workspace)
+        mapLayer = self.parent.toolbars['vdigit'].GetLayer()
+        tree = self.parent.tree
+        item = tree.FindItemByData('maplayer', mapLayer)
+        for key, val in self.geomAttrb.iteritems():
+            checked = self.FindWindowById(val['check']).IsChecked()
+            column  = self.FindWindowById(val['column']).GetValue()
+            unitsIdx = self.FindWindowById(val['units']).GetSelection()
+            if item and not tree.GetPyData(item)[0]['vdigit']: 
+                tree.GetPyData(item)[0]['vdigit'] = { 'geomAttr' : dict() }
+            
+            if checked: # enable
+                if key == 'area':
+                    type = key
+                else:
+                    type = 'length'
+                unitsKey = Units.GetUnitsKey(type, unitsIdx)
+                tree.GetPyData(item)[0]['vdigit']['geomAttr'][key] = { 'column' : column,
+                                                                       'units' : unitsKey }
+            else:
+                if item and tree.GetPyData(item)[0]['vdigit'] and \
+                        key in tree.GetPyData(item)[0]['vdigit']['geomAttr']:
+                    del tree.GetPyData(item)[0]['vdigit']['geomAttr'][key]
+        
+        # query tool
+        if self.queryLength.GetValue():
+            UserSettings.Set(group = 'vdigit', key = "query", subkey = 'selection',
+                             value = 0)
+        else:
+            UserSettings.Set(group = 'vdigit', key = "query", subkey = 'type',
+                             value = 1)
+        UserSettings.Set(group = 'vdigit', key = "query", subkey = 'box',
+                         value = self.queryBox.IsChecked())
+        UserSettings.Set(group = 'vdigit', key = "queryLength", subkey = 'than-selection',
+                         value = self.queryLengthSL.GetSelection())
+        UserSettings.Set(group = 'vdigit', key = "queryLength", subkey = 'thresh',
+                         value = int(self.queryLengthValue.GetValue()))
+        UserSettings.Set(group = 'vdigit', key = "queryDangle", subkey = 'than-selection',
+                         value = self.queryDangleSL.GetSelection())
+        UserSettings.Set(group = 'vdigit', key = "queryDangle", subkey = 'thresh',
+                         value = int(self.queryDangleValue.GetValue()))
+
+        # select features
+        for feature in ('point', 'line',
+                        'centroid', 'boundary'):
+            UserSettings.Set(group = 'vdigit', key = 'selectType',
+                             subkey = [feature, 'enabled'],
+                             value = self.FindWindowById(self.selectFeature[feature]).IsChecked())
+        UserSettings.Set(group = 'vdigit', key = "selectThresh", subkey = 'value',
+                         value = int(self.selectThreshValue.GetValue()))
+        UserSettings.Set(group = 'vdigit', key = "checkForDupl", subkey = 'enabled',
+                         value = self.checkForDupl.IsChecked())
+        UserSettings.Set(group = 'vdigit', key = "selectInside", subkey = 'enabled',
+                         value = self.selectIn.IsChecked())
+
+        # on-exit
+        UserSettings.Set(group = 'vdigit', key = "saveOnExit", subkey = 'enabled',
+                         value = self.save.IsChecked())
+
+        # break lines
+        UserSettings.Set(group = 'vdigit', key = "breakLines", subkey = 'enabled',
+                         value = self.intersect.IsChecked())
+        
+        self.digit.UpdateSettings()
+        
+        # redraw map if auto-rendering is enabled
+        if self.parent.IsAutoRendered(): 
+            self.parent.OnRender(None)


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/toolbars.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/toolbars.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/toolbars.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,806 @@
+"""!
+ at package vdigit.toolbars
+
+ at brief wxGUI vector digitizer toolbars
+
+List of classes:
+ - toolbars::VDigitToolbar
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+import wx
+
+from grass.script import core as grass
+
+from gui_core.toolbars  import BaseToolbar, BaseIcons
+from gui_core.dialogs   import CreateNewVector
+from vdigit.preferences import VDigitSettingsDialog
+from vdigit.main        import VDigit
+from core.debug         import Debug
+from core.settings      import UserSettings
+from core.gcmd          import GError
+from icons.icon         import MetaIcon
+
+class VDigitToolbar(BaseToolbar):
+    """!Toolbar for digitization
+    """
+    def __init__(self, parent, mapcontent, layerTree = None, log = None):
+        self.mapcontent    = mapcontent # Map class instance
+        self.layerTree     = layerTree  # reference to layer tree associated to map display
+        self.log           = log        # log area
+        BaseToolbar.__init__(self, parent)
+        self.digit         = None
+        
+        # currently selected map layer for editing (reference to MapLayer instance)
+        self.mapLayer = None
+        # list of vector layers from Layer Manager (only in the current mapset)
+        self.layers   = [] 
+        
+        self.comboid    = None
+        
+        # only one dialog can be open
+        self.settingsDialog   = None
+        
+        # create toolbars (two rows optionally)
+        self.InitToolbar(self._toolbarData())
+        self.Bind(wx.EVT_TOOL, self.OnTool)
+        
+        # default action (digitize new point, line, etc.)
+        self.action = { 'desc' : '',
+                        'type' : '',
+                        'id'   : -1 }
+        
+        # list of available vector maps
+        self.UpdateListOfLayers(updateTool = True)
+        
+        # realize toolbar
+        self.Realize()
+        # workaround for Mac bug. May be fixed by 2.8.8, but not before then.
+        self.combo.Hide()
+        self.combo.Show()
+        
+        # disable undo/redo
+        self.EnableTool(self.undo, False)
+        
+        # toogle to pointer by default
+        self.OnTool(None)
+        
+        self.FixSize(width = 105)
+        
+    def _toolbarData(self):
+        """!Toolbar data
+        """
+        data = []
+        
+        icons = {
+            'addPoint'        : MetaIcon(img = 'point-create',
+                                         label = _('Digitize new point'),
+                                         desc = _('Left: new point')),
+            'addLine'         : MetaIcon(img = 'line-create',
+                                         label = _('Digitize new line'),
+                                         desc = _('Left: new point; Ctrl+Left: undo last point; Right: close line')),
+            'addBoundary'     : MetaIcon(img = 'boundary-create',
+                                         label = _('Digitize new boundary'),
+                                         desc = _('Left: new point; Ctrl+Left: undo last point; Right: close line')),
+            'addCentroid'     : MetaIcon(img = 'centroid-create',
+                                         label = _('Digitize new centroid'),
+                                         desc = _('Left: new point')),
+            'addArea'         : MetaIcon(img = 'polygon-create',
+                                         label = _('Digitize new area (composition of boundaries without category and one centroid with category)'),
+                                         desc = _('Left: new point')),
+            'addVertex'       : MetaIcon(img = 'vertex-create',
+                                         label = _('Add new vertex'),
+                                         desc = _('Left: Select; Ctrl+Left: Unselect; Right: Confirm')),
+            'deleteLine'      : MetaIcon(img = 'line-delete',
+                                         label = _('Delete feature(s)'),
+                                         desc = _('Left: Select; Ctrl+Left: Unselect; Right: Confirm')),
+            'displayAttr'     : MetaIcon(img = 'attributes-display',
+                                         label = _('Display/update attributes'),
+                                         desc = _('Left: Select')),
+            'displayCats'     : MetaIcon(img = 'cats-display',
+                                         label = _('Display/update categories'),
+                                         desc = _('Left: Select')),
+            'editLine'        : MetaIcon(img = 'line-edit',
+                                         label = _('Edit line/boundary'),
+                                         desc = _('Left: new point; Ctrl+Left: undo last point; Right: close line')),
+            'moveLine'        : MetaIcon(img = 'line-move',
+                                         label = _('Move feature(s)'),
+                                         desc = _('Left: Select; Ctrl+Left: Unselect; Right: Confirm')),
+            'moveVertex'      : MetaIcon(img = 'vertex-move',
+                                         label = _('Move vertex'),
+                                         desc = _('Left: Select; Ctrl+Left: Unselect; Right: Confirm')),
+            'removeVertex'    : MetaIcon(img = 'vertex-delete',
+                                         label = _('Remove vertex'),
+                                         desc = _('Left: Select; Ctrl+Left: Unselect; Right: Confirm')),
+            'settings'        : BaseIcons['settings'].SetLabel(_('Digitization settings')),
+            'quit'            : BaseIcons['quit'].SetLabel(label = _('Quit digitizer'),
+                                                           desc = _('Quit digitizer and save changes')),
+            'additionalTools' : MetaIcon(img = 'tools',
+                                         label = _('Additional tools '
+                                                   '(copy, flip, connect, etc.)'),
+                                         desc = _('Left: Select; Ctrl+Left: Unselect; Right: Confirm')),
+            'undo'             : MetaIcon(img = 'undo',
+                                          label = _('Undo'),
+                                          desc = _('Undo previous changes')),
+            }
+        
+        return self._getToolbarData(((None, ),
+                                     ("addPoint", icons["addPoint"],
+                                      self.OnAddPoint,
+                                      wx.ITEM_CHECK),
+                                     ("addLine", icons["addLine"],
+                                      self.OnAddLine,
+                                      wx.ITEM_CHECK),
+                                     ("addBoundary", icons["addBoundary"],
+                                      self.OnAddBoundary,
+                                      wx.ITEM_CHECK),
+                                     ("addCentroid", icons["addCentroid"],
+                                      self.OnAddCentroid,
+                                      wx.ITEM_CHECK),
+                                     ("addArea", icons["addArea"],
+                                      self.OnAddArea,
+                                      wx.ITEM_CHECK),
+                                     ("moveVertex", icons["moveVertex"],
+                                      self.OnMoveVertex,
+                                      wx.ITEM_CHECK),
+                                     ("addVertex", icons["addVertex"],
+                                      self.OnAddVertex,
+                                      wx.ITEM_CHECK),
+                                     ("removeVertex", icons["removeVertex"],
+                                      self.OnRemoveVertex,
+                                      wx.ITEM_CHECK),
+                                     ("editLine", icons["editLine"],
+                                      self.OnEditLine,
+                                      wx.ITEM_CHECK),
+                                     ("moveLine", icons["moveLine"],
+                                      self.OnMoveLine,
+                                      wx.ITEM_CHECK),
+                                     ("deleteLine", icons["deleteLine"],
+                                      self.OnDeleteLine,
+                                      wx.ITEM_CHECK),
+                                     ("displayCats", icons["displayCats"],
+                                      self.OnDisplayCats,
+                                      wx.ITEM_CHECK),
+                                     ("displayAttr", icons["displayAttr"],
+                                      self.OnDisplayAttr,
+                                      wx.ITEM_CHECK),
+                                     ("additionalTools", icons["additionalTools"],
+                                      self.OnAdditionalToolMenu,
+                                      wx.ITEM_CHECK),                                      
+                                     (None, ),
+                                     ("undo", icons["undo"],
+                                      self.OnUndo),
+                                     ("settings", icons["settings"],
+                                      self.OnSettings),
+                                     ("quit", icons["quit"],
+                                      self.OnExit))
+                                    )
+    
+    def OnTool(self, event):
+        """!Tool selected -> disable selected tool in map toolbar"""
+        aId = self.parent.toolbars['map'].GetAction(type = 'id')
+        self.parent.toolbars['map'].ToggleTool(aId, False)
+        
+        # set cursor
+        cursor = self.parent.cursors["cross"]
+        self.parent.MapWindow.SetCursor(cursor)
+        
+        # pointer
+        self.parent.OnPointer(None)
+        
+        if event:
+            # deselect previously selected tool
+            aId = self.action.get('id', -1)
+            if aId != event.GetId() and \
+                    self.action['id'] != -1:
+                self.ToggleTool(self.action['id'], False)
+            else:
+                self.ToggleTool(self.action['id'], True)
+            
+            self.action['id'] = event.GetId()
+            
+            event.Skip()
+        
+        if self.action['id'] != -1:
+            self.ToggleTool(self.action['id'], True)
+        
+        # clear tmp canvas
+        if self.action['id'] != aId:
+            self.parent.MapWindow.ClearLines(pdc = self.parent.MapWindow.pdcTmp)
+            if self.digit and \
+                    len(self.parent.MapWindow.digit.GetDisplay().GetSelected()) > 0:
+                # cancel action
+                self.parent.MapWindow.OnMiddleDown(None)
+        
+        # set focus
+        self.parent.MapWindow.SetFocus()
+        
+    def OnAddPoint(self, event):
+        """!Add point to the vector map Laier"""
+        Debug.msg (2, "VDigitToolbar.OnAddPoint()")
+        self.action = { 'desc' : "addLine",
+                        'type' : "point",
+                        'id'   : self.addPoint }
+        self.parent.MapWindow.mouse['box'] = 'point'
+        
+    def OnAddLine(self, event):
+        """!Add line to the vector map layer"""
+        Debug.msg (2, "VDigitToolbar.OnAddLine()")
+        self.action = { 'desc' : "addLine",
+                        'type' : "line",
+                        'id'   : self.addLine }
+        self.parent.MapWindow.mouse['box'] = 'line'
+        ### self.parent.MapWindow.polycoords = [] # reset temp line
+                
+    def OnAddBoundary(self, event):
+        """!Add boundary to the vector map layer"""
+        Debug.msg (2, "VDigitToolbar.OnAddBoundary()")
+        if self.action['desc'] != 'addLine' or \
+                self.action['type'] != 'boundary':
+            self.parent.MapWindow.polycoords = [] # reset temp line
+        self.action = { 'desc' : "addLine",
+                        'type' : "boundary",
+                        'id'   : self.addBoundary }
+        self.parent.MapWindow.mouse['box'] = 'line'
+        
+    def OnAddCentroid(self, event):
+        """!Add centroid to the vector map layer"""
+        Debug.msg (2, "VDigitToolbar.OnAddCentroid()")
+        self.action = { 'desc' : "addLine",
+                        'type' : "centroid",
+                        'id'   : self.addCentroid }
+        self.parent.MapWindow.mouse['box'] = 'point'
+
+    def OnAddArea(self, event):
+        """!Add area to the vector map layer"""
+        Debug.msg (2, "VDigitToolbar.OnAddCentroid()")
+        self.action = { 'desc' : "addLine",
+                        'type' : "area",
+                        'id'   : self.addArea }
+        self.parent.MapWindow.mouse['box'] = 'line'
+
+    def OnExit (self, event=None):
+        """!Quit digitization tool"""
+        # stop editing of the currently selected map layer
+        if self.mapLayer:
+            self.StopEditing()
+        
+        # close dialogs if still open
+        if self.settingsDialog:
+            self.settingsDialog.OnCancel(None)
+            
+        # set default mouse settings
+        self.parent.MapWindow.mouse['use'] = "pointer"
+        self.parent.MapWindow.mouse['box'] = "point"
+        self.parent.MapWindow.polycoords = []
+        
+        # disable the toolbar
+        self.parent.RemoveToolbar("vdigit")
+        
+    def OnMoveVertex(self, event):
+        """!Move line vertex"""
+        Debug.msg(2, "Digittoolbar.OnMoveVertex():")
+        self.action = { 'desc' : "moveVertex",
+                        'id'   : self.moveVertex }
+        self.parent.MapWindow.mouse['box'] = 'point'
+
+    def OnAddVertex(self, event):
+        """!Add line vertex"""
+        Debug.msg(2, "Digittoolbar.OnAddVertex():")
+        self.action = { 'desc' : "addVertex",
+                        'id'   : self.addVertex }
+        self.parent.MapWindow.mouse['box'] = 'point'
+        
+    def OnRemoveVertex(self, event):
+        """!Remove line vertex"""
+        Debug.msg(2, "Digittoolbar.OnRemoveVertex():")
+        self.action = { 'desc' : "removeVertex",
+                        'id'   : self.removeVertex }
+        self.parent.MapWindow.mouse['box'] = 'point'
+
+    def OnEditLine(self, event):
+        """!Edit line"""
+        Debug.msg(2, "Digittoolbar.OnEditLine():")
+        self.action = { 'desc' : "editLine",
+                        'id'   : self.editLine }
+        self.parent.MapWindow.mouse['box'] = 'line'
+
+    def OnMoveLine(self, event):
+        """!Move line"""
+        Debug.msg(2, "Digittoolbar.OnMoveLine():")
+        self.action = { 'desc' : "moveLine",
+                        'id'   : self.moveLine }
+        self.parent.MapWindow.mouse['box'] = 'box'
+
+    def OnDeleteLine(self, event):
+        """!Delete line"""
+        Debug.msg(2, "Digittoolbar.OnDeleteLine():")
+        self.action = { 'desc' : "deleteLine",
+                        'id'   : self.deleteLine }
+        self.parent.MapWindow.mouse['box'] = 'box'
+
+    def OnDisplayCats(self, event):
+        """!Display/update categories"""
+        Debug.msg(2, "Digittoolbar.OnDisplayCats():")
+        self.action = { 'desc' : "displayCats",
+                        'id'   : self.displayCats }
+        self.parent.MapWindow.mouse['box'] = 'point'
+
+    def OnDisplayAttr(self, event):
+        """!Display/update attributes"""
+        Debug.msg(2, "Digittoolbar.OnDisplayAttr():")
+        self.action = { 'desc' : "displayAttrs",
+                        'id'   : self.displayAttr }
+        self.parent.MapWindow.mouse['box'] = 'point'
+        
+    def OnUndo(self, event):
+        """!Undo previous changes"""
+        self.digit.Undo()
+        
+        event.Skip()
+
+    def EnableUndo(self, enable=True):
+        """!Enable 'Undo' in toolbar
+        
+        @param enable False for disable
+        """
+        if enable:
+            if self.GetToolEnabled(self.undo) is False:
+                self.EnableTool(self.undo, True)
+        else:
+            if self.GetToolEnabled(self.undo) is True:
+                self.EnableTool(self.undo, False)
+        
+    def OnSettings(self, event):
+        """!Show settings dialog"""
+        if self.digit is None:
+            try:
+                self.digit = self.parent.MapWindow.digit = VDigit(mapwindow = self.parent.MapWindow)
+            except SystemExit:
+                self.digit = self.parent.MapWindow.digit = None
+        
+        if not self.settingsDialog:
+            self.settingsDialog = VDigitSettingsDialog(parent = self.parent, title = _("Digitization settings"),
+                                                       style = wx.DEFAULT_DIALOG_STYLE)
+            self.settingsDialog.Show()
+
+    def OnAdditionalToolMenu(self, event):
+        """!Menu for additional tools"""
+        point = wx.GetMousePosition()
+        toolMenu = wx.Menu()
+        
+        for label, itype, handler, desc in (
+            (_('Break selected lines/boundaries at intersection'),
+             wx.ITEM_CHECK, self.OnBreak, "breakLine"),
+            (_('Connect selected lines/boundaries'),
+             wx.ITEM_CHECK, self.OnConnect, "connectLine"),
+            (_('Copy categories'),
+             wx.ITEM_CHECK, self.OnCopyCats, "copyCats"),
+            (_('Copy features from (background) vector map'),
+             wx.ITEM_CHECK, self.OnCopy, "copyLine"),
+            (_('Copy attributes'),
+             wx.ITEM_CHECK, self.OnCopyAttrb, "copyAttrs"),
+            (_('Feature type conversion'),
+             wx.ITEM_CHECK, self.OnTypeConversion, "typeConv"),
+            (_('Flip selected lines/boundaries'),
+             wx.ITEM_CHECK, self.OnFlip, "flipLine"),
+            (_('Merge selected lines/boundaries'),
+             wx.ITEM_CHECK, self.OnMerge, "mergeLine"),
+            (_('Snap selected lines/boundaries (only to nodes)'),
+             wx.ITEM_CHECK, self.OnSnap, "snapLine"),
+            (_('Split line/boundary'),
+             wx.ITEM_CHECK, self.OnSplitLine, "splitLine"),
+            (_('Query features'),
+             wx.ITEM_CHECK, self.OnQuery, "queryLine"),
+            (_('Z bulk-labeling of 3D lines'),
+             wx.ITEM_CHECK, self.OnZBulk, "zbulkLine")):
+            # Add items to the menu
+            item = wx.MenuItem(parentMenu = toolMenu, id = wx.ID_ANY,
+                               text = label,
+                               kind = itype)
+            toolMenu.AppendItem(item)
+            self.parent.MapWindow.Bind(wx.EVT_MENU, handler, item)
+            if self.action['desc'] == desc:
+                item.Check(True)
+        
+        # Popup the menu.  If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.parent.MapWindow.PopupMenu(toolMenu)
+        toolMenu.Destroy()
+        
+        if self.action['desc'] == 'addPoint':
+            self.ToggleTool(self.additionalTools, False)
+        
+    def OnCopy(self, event):
+        """!Copy selected features from (background) vector map"""
+        if self.action['desc'] == 'copyLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnCopy():")
+        self.action = { 'desc' : "copyLine",
+                        'id'   : self.additionalTools }
+        self.parent.MapWindow.mouse['box'] = 'box'
+
+    def OnSplitLine(self, event):
+        """!Split line"""
+        if self.action['desc'] == 'splitLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnSplitLine():")
+        self.action = { 'desc' : "splitLine",
+                        'id'   : self.additionalTools }
+        self.parent.MapWindow.mouse['box'] = 'point'
+
+
+    def OnCopyCats(self, event):
+        """!Copy categories"""
+        if self.action['desc'] == 'copyCats': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.copyCats, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnCopyCats():")
+        self.action = { 'desc' : "copyCats",
+                        'id'   : self.additionalTools }
+        self.parent.MapWindow.mouse['box'] = 'point'
+
+    def OnCopyAttrb(self, event):
+        """!Copy attributes"""
+        if self.action['desc'] == 'copyAttrs': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.copyCats, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnCopyAttrb():")
+        self.action = { 'desc' : "copyAttrs",
+                        'id'   : self.additionalTools }
+        self.parent.MapWindow.mouse['box'] = 'point'
+        
+
+    def OnFlip(self, event):
+        """!Flip selected lines/boundaries"""
+        if self.action['desc'] == 'flipLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnFlip():")
+        self.action = { 'desc' : "flipLine",
+                        'id'   : self.additionalTools }
+        self.parent.MapWindow.mouse['box'] = 'box'
+
+    def OnMerge(self, event):
+        """!Merge selected lines/boundaries"""
+        if self.action['desc'] == 'mergeLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnMerge():")
+        self.action = { 'desc' : "mergeLine",
+                        'id'   : self.additionalTools }
+        self.parent.MapWindow.mouse['box'] = 'box'
+
+    def OnBreak(self, event):
+        """!Break selected lines/boundaries"""
+        if self.action['desc'] == 'breakLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnBreak():")
+        self.action = { 'desc' : "breakLine",
+                        'id'   : self.additionalTools }
+        self.parent.MapWindow.mouse['box'] = 'box'
+
+    def OnSnap(self, event):
+        """!Snap selected features"""
+        if self.action['desc'] == 'snapLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnSnap():")
+        self.action = { 'desc' : "snapLine",
+                        'id'   : self.additionalTools }
+        self.parent.MapWindow.mouse['box'] = 'box'
+
+    def OnConnect(self, event):
+        """!Connect selected lines/boundaries"""
+        if self.action['desc'] == 'connectLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnConnect():")
+        self.action = { 'desc' : "connectLine",
+                        'id'   : self.additionalTools }
+        self.parent.MapWindow.mouse['box'] = 'box'
+
+    def OnQuery(self, event):
+        """!Query selected lines/boundaries"""
+        if self.action['desc'] == 'queryLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnQuery(): %s" % \
+                      UserSettings.Get(group = 'vdigit', key = 'query', subkey = 'selection'))
+        self.action = { 'desc' : "queryLine",
+                        'id'   : self.additionalTools }
+        self.parent.MapWindow.mouse['box'] = 'box'
+
+    def OnZBulk(self, event):
+        """!Z bulk-labeling selected lines/boundaries"""
+        if not self.digit.IsVector3D():
+            GError(parent = self.parent,
+                   message = _("Vector map is not 3D. Operation canceled."))
+            return
+        
+        if self.action['desc'] == 'zbulkLine': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnZBulk():")
+        self.action = { 'desc' : "zbulkLine",
+                        'id'   : self.additionalTools }
+        self.parent.MapWindow.mouse['box'] = 'line'
+
+    def OnTypeConversion(self, event):
+        """!Feature type conversion
+
+        Supported conversions:
+         - point <-> centroid
+         - line <-> boundary
+        """
+        if self.action['desc'] == 'typeConv': # select previous action
+            self.ToggleTool(self.addPoint, True)
+            self.ToggleTool(self.additionalTools, False)
+            self.OnAddPoint(event)
+            return
+        
+        Debug.msg(2, "Digittoolbar.OnTypeConversion():")
+        self.action = { 'desc' : "typeConv",
+                        'id'   : self.additionalTools }
+        self.parent.MapWindow.mouse['box'] = 'box'
+
+    def OnSelectMap (self, event):
+        """!Select vector map layer for editing
+
+        If there is a vector map layer already edited, this action is
+        firstly terminated. The map layer is closed. After this the
+        selected map layer activated for editing.
+        """
+        if event.GetSelection() == 0: # create new vector map layer
+            if self.mapLayer:
+                openVectorMap = self.mapLayer.GetName(fullyQualified = False)['name']
+            else:
+                openVectorMap = None
+            dlg = CreateNewVector(self.parent,
+                                  exceptMap = openVectorMap, log = self.log,
+                                  cmd = (('v.edit',
+                                          { 'tool' : 'create' },
+                                          'map')),
+                                  disableAdd = True)
+            
+            if dlg and dlg.GetName():
+                # add layer to map layer tree
+                if self.layerTree:
+                    mapName = dlg.GetName() + '@' + grass.gisenv()['MAPSET']
+                    self.layerTree.AddLayer(ltype = 'vector',
+                                            lname = mapName,
+                                            lcmd = ['d.vect', 'map=%s' % mapName])
+                    
+                    vectLayers = self.UpdateListOfLayers(updateTool = True)
+                    selection = vectLayers.index(mapName)
+                
+                # create table ?
+                if dlg.IsChecked('table'):
+                    lmgr = self.parent.GetLayerManager()
+                    if lmgr:
+                        lmgr.OnShowAttributeTable(None, selection = 'table')
+                dlg.Destroy()
+            else:
+                self.combo.SetValue(_('Select vector map'))
+                if dlg:
+                    dlg.Destroy()
+                return
+        else:
+            selection = event.GetSelection() - 1 # first option is 'New vector map'
+        
+        # skip currently selected map
+        if self.layers[selection] == self.mapLayer:
+            return
+        
+        if self.mapLayer:
+            # deactive map layer for editing
+            self.StopEditing()
+        
+        # select the given map layer for editing
+        self.StartEditing(self.layers[selection])
+        
+        event.Skip()
+        
+    def StartEditing (self, mapLayer):
+        """!Start editing selected vector map layer.
+        
+        @param mapLayer MapLayer to be edited
+        """
+        # deactive layer
+        self.mapcontent.ChangeLayerActive(mapLayer, False)
+        
+        # clean map canvas
+        self.parent.MapWindow.EraseMap()
+        
+        # unset background map if needed
+        if mapLayer:
+            if UserSettings.Get(group = 'vdigit', key = 'bgmap',
+                                subkey = 'value', internal = True) == mapLayer.GetName():
+                UserSettings.Set(group = 'vdigit', key = 'bgmap',
+                                 subkey = 'value', value = '', internal = True)
+            
+            self.parent.SetStatusText(_("Please wait, "
+                                        "opening vector map <%s> for editing...") % mapLayer.GetName(),
+                                        0)
+        
+        self.parent.MapWindow.pdcVector = wx.PseudoDC()
+        self.digit = self.parent.MapWindow.digit = VDigit(mapwindow = self.parent.MapWindow)
+        
+        self.mapLayer = mapLayer
+        
+        # open vector map
+        if self.digit.OpenMap(mapLayer.GetName()) is None:
+            self.mapLayer = None
+            self.StopEditing()
+            return False
+        
+        # update toolbar
+        self.combo.SetValue(mapLayer.GetName())
+        self.parent.toolbars['map'].combo.SetValue (_('Digitize'))
+        lmgr = self.parent.GetLayerManager()
+        if lmgr:
+            lmgr.toolbars['tools'].Enable('vdigit', enable = False)
+        
+        Debug.msg (4, "VDigitToolbar.StartEditing(): layer=%s" % mapLayer.GetName())
+        
+        # change cursor
+        if self.parent.MapWindow.mouse['use'] == 'pointer':
+            self.parent.MapWindow.SetCursor(self.parent.cursors["cross"])
+        
+        if not self.parent.MapWindow.resize:
+            self.parent.MapWindow.UpdateMap(render = True)
+        
+        # respect opacity
+        opacity = mapLayer.GetOpacity(float = True)
+        if opacity < 1.0:
+            alpha = int(opacity * 255)
+            self.digit.GetDisplay().UpdateSettings(alpha = alpha)
+        
+        return True
+
+    def StopEditing(self):
+        """!Stop editing of selected vector map layer.
+
+        @return True on success
+        @return False on failure
+        """
+        self.combo.SetValue (_('Select vector map'))
+        
+        # save changes
+        if self.mapLayer:
+            Debug.msg (4, "VDigitToolbar.StopEditing(): layer=%s" % self.mapLayer.GetName())
+            if UserSettings.Get(group = 'vdigit', key = 'saveOnExit', subkey = 'enabled') is False:
+                if self.digit.GetUndoLevel() > -1:
+                    dlg = wx.MessageDialog(parent = self.parent,
+                                           message = _("Do you want to save changes "
+                                                     "in vector map <%s>?") % self.mapLayer.GetName(),
+                                           caption = _("Save changes?"),
+                                           style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
+                    if dlg.ShowModal() == wx.ID_NO:
+                        # revert changes
+                        self.digit.Undo(0)
+                    dlg.Destroy()
+            
+            self.parent.SetStatusText(_("Please wait, "
+                                        "closing and rebuilding topology of "
+                                        "vector map <%s>...") % self.mapLayer.GetName(),
+                                      0)
+            self.digit.CloseMap()
+            
+            lmgr = self.parent.GetLayerManager()
+            if lmgr:
+                lmgr.toolbars['tools'].Enable('vdigit', enable = True)
+                lmgr.GetLogWindow().GetProgressBar().SetValue(0)
+                lmgr.GetLogWindow().WriteCmdLog(_("Editing of vector map <%s> successfully finished") % \
+                                                    self.mapLayer.GetName(),
+                                                switchPage = False)
+            # re-active layer 
+            item = self.parent.tree.FindItemByData('maplayer', self.mapLayer)
+            if item and self.parent.tree.IsItemChecked(item):
+                self.mapcontent.ChangeLayerActive(self.mapLayer, True)
+        
+        # change cursor
+        self.parent.MapWindow.SetCursor(self.parent.cursors["default"])
+        self.parent.MapWindow.pdcVector = None
+        
+        # close dialogs
+        for dialog in ('attributes', 'category'):
+            if self.parent.dialogs[dialog]:
+                self.parent.dialogs[dialog].Close()
+                self.parent.dialogs[dialog] = None
+        
+        del self.digit
+        del self.parent.MapWindow.digit
+        
+        self.mapLayer = None
+        
+        self.parent.MapWindow.redrawAll = True
+        
+        return True
+    
+    def UpdateListOfLayers (self, updateTool = False):
+        """!Update list of available vector map layers.
+        This list consists only editable layers (in the current mapset)
+
+        @param updateTool True to update also toolbar
+        """
+        Debug.msg (4, "VDigitToolbar.UpdateListOfLayers(): updateTool=%d" % \
+                   updateTool)
+        
+        layerNameSelected = None
+         # name of currently selected layer
+        if self.mapLayer:
+            layerNameSelected = self.mapLayer.GetName()
+        
+        # select vector map layer in the current mapset
+        layerNameList = []
+        self.layers = self.mapcontent.GetListOfLayers(l_type = "vector",
+                                                      l_mapset = grass.gisenv()['MAPSET'])
+        
+        for layer in self.layers:
+            if not layer.name in layerNameList: # do not duplicate layer
+                layerNameList.append (layer.GetName())
+        
+        if updateTool: # update toolbar
+            if not self.mapLayer:
+                value = _('Select vector map')
+            else:
+                value = layerNameSelected
+
+            if not self.comboid:
+                self.combo = wx.ComboBox(self, id = wx.ID_ANY, value = value,
+                                         choices = [_('New vector map'), ] + layerNameList, size = (80, -1),
+                                         style = wx.CB_READONLY)
+                self.comboid = self.InsertControl(0, self.combo)
+                self.parent.Bind(wx.EVT_COMBOBOX, self.OnSelectMap, self.comboid)
+            else:
+                self.combo.SetItems([_('New vector map'), ] + layerNameList)
+            
+            self.Realize()
+        
+        return layerNameList
+
+    def GetLayer(self):
+        """!Get selected layer for editing -- MapLayer instance"""
+        return self.mapLayer


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/wxdigit.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/wxdigit.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/wxdigit.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1747 @@
+"""!
+ at package vdigit.wxdigit
+
+ at brief wxGUI vector digitizer (base class)
+
+Code based on wxVdigit C++ component from GRASS 6.4.0
+(gui/wxpython/vdigit). Converted to Python in 2010/12-2011/01.
+
+List of classes:
+ - wxdigit::VDigitError
+ - wxdigit::IVDigit
+
+ at todo Read large amounts of data from Vlib into arrays, which could
+then be processed using NumPy and rendered using glDrawArrays or
+glDrawElements, so no per-line/per-vertex processing in Python. Bulk
+data processing with NumPy is much faster than iterating in Python
+(and NumPy would be an excellent candidate for acceleration via
+e.g. OpenCL or CUDA; I'm surprised it hasn't happened already).
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import grass.script.core as grass
+
+from core.gcmd        import GError
+from core.debug       import Debug
+from core.settings    import UserSettings
+from vdigit.wxdisplay import DisplayDriver
+
+try:
+    from grass.lib.gis    import *
+    from grass.lib.vector import *
+    from grass.lib.vedit  import *
+    from grass.lib.dbmi   import *
+except ImportError:
+    pass
+
+class VDigitError:
+    def __init__(self, parent):
+        """!Class for managing error messages of vector digitizer
+
+        @param parent parent window for dialogs
+        """
+        self.parent  = parent
+        self.caption = _('Digitization Error')
+    
+    def NoMap(self, name = None):
+        """!No map for editing"""
+        if name:
+            message = _('Unable to open vector map <%s>.') % name
+        else:
+            message =  _('No vector map open for editing.')
+        GError(message + ' ' + _('Operation canceled.'),
+               parent  = self.parent,
+               caption = self.caption)
+
+    def WriteLine(self):
+        """!Writing line failed
+        """
+        GError(message = _('Writing new feature failed. '
+                           'Operation cancelled.'),
+               parent  = self.parent,
+               caption = self.caption)
+
+    def ReadLine(self, line):
+        """!Reading line failed
+        """
+        GError(message = _('Reading feature id %d failed. '
+                           'Operation canceled.') % line,
+               parent  = self.parent,
+               caption = self.caption)
+
+    def DbLink(self, dblink):
+        """!No dblink available
+        """
+        GError(message = _('Database link %d not available. '
+                           'Operation canceled.') % dblink,
+               parent  = self.parent,
+               caption = self.caption)
+
+    def Driver(self, driver):
+        """!Staring driver failed
+        """
+        GError(message = _('Unable to start database driver <%s>. '
+                           'Operation canceled.') % driver,
+               parent  = self.parent,
+               caption = self.caption)
+
+    def Database(self, driver, database):
+        """!Opening database failed
+        """
+        GError(message = _('Unable to open database <%(db)s> by driver <%(driver)s>. '
+                           'Operation canceled.') % { 'db' : database, 'driver' : driver},
+               parent  = self.parent,
+               caption = self.caption)
+
+    def DbExecute(self, sql):
+        """!Sql query failed
+        """
+        GError(message = _("Unable to execute SQL query '%s'. "
+                           "Operation canceled.") % sql,
+               parent  = self.parent,
+               caption = self.caption)
+
+    def DeadLine(self, line):
+        """!Dead line
+        """
+        GError(message = _("Feature id %d is marked as dead. "
+                           "Operation canceled.") % line,
+               parent  = self.parent,
+               caption = self.caption)
+
+    def FeatureType(self, ftype):
+        """!Unknown feature type
+        """
+        GError(message = _("Unsupported feature type %d. "
+                           "Operation canceled.") % ftype,
+               parent  = self.parent,
+               caption = self.caption)
+        
+class IVDigit:
+    def __init__(self, mapwindow):
+        """!Base class for vector digitizer (ctypes interface)
+        
+        @parem mapwindow reference for map window (BufferedWindow)
+        """
+        self.poMapInfo   = None      # pointer to Map_info
+        self.mapWindow = mapwindow
+
+        # background map
+        self.bgMapInfo   = Map_info()
+        self.poBgMapInfo = self.popoBgMapInfo = None
+        
+        if not mapwindow.parent.IsStandalone():
+            goutput = mapwindow.parent.GetLayerManager().GetLogWindow()
+            log = goutput.GetLog(err = True)
+            progress = goutput.GetProgressBar()
+        else:
+            log = sys.stderr
+            progress = None
+        
+        self.toolbar = mapwindow.parent.toolbars['vdigit']
+        
+        self._error   = VDigitError(parent = self.mapWindow)
+        
+        self._display = DisplayDriver(device    = mapwindow.pdcVector,
+                                      deviceTmp = mapwindow.pdcTmp,
+                                      mapObj    = mapwindow.Map,
+                                      window    = mapwindow,
+                                      glog      = log,
+                                      gprogress = progress)
+        
+        # GRASS lib
+        self.poPoints = Vect_new_line_struct()
+        self.poCats   = Vect_new_cats_struct()
+        
+        # self.SetCategory()
+        
+        # layer / max category
+        self.cats = dict()
+        
+        self._settings = dict()
+        self.UpdateSettings() # -> self._settings
+        
+        # undo/redo
+        self.changesets = dict()
+        self.changesetCurrent = -1 # first changeset to apply
+        self.changesetEnd     = -1 # last changeset to be applied
+        
+        if self.poMapInfo:
+            self.InitCats()
+        
+    def __del__(self):
+        Debug.msg(1, "IVDigit.__del__()")
+        Vect_destroy_line_struct(self.poPoints)
+        self.poPoints = None
+        Vect_destroy_cats_struct(self.poCats)
+        self.poCats = None
+        
+        if self.poBgMapInfo:
+            Vect_close(self.poBgMapInfo)
+            self.poBgMapInfo = self.popoBgMapInfo = None
+            del self.bgMapInfo
+        
+    def CloseBackgroundMap(self):
+        """!Close background vector map"""
+        if not self.poBgMapInfo:
+            return
+        
+        Vect_close(self.poBgMapInfo)
+        self.poBgMapInfo = self.popoBgMapInfo = None
+        
+    def OpenBackgroundMap(self, bgmap):
+        """!Open background vector map
+
+        @todo support more background maps then only one
+        
+        @param bgmap name of vector map to be opened
+
+        @return pointer to map_info
+        @return None on error
+        """
+        name   = create_string_buffer(GNAME_MAX)
+        mapset = create_string_buffer(GMAPSET_MAX)
+        if not G__name_is_fully_qualified(bgmap, name, mapset):
+            name   = str(bgmap)
+            mapset = str(G_find_vector2(bgmap, ''))
+        else:
+            name   = str(name.value)
+            mapset = str(mapset.value)
+        
+        if (name == Vect_get_name(self.poMapInfo) and \
+                mapset == Vect_get_mapset(self.poMapInfo)):
+            self.poBgMapInfo = self.popoBgMapInfo = None
+            self._error.NoMap(bgmap)
+            return
+        
+        self.poBgMapInfo = pointer(self.bgMapInfo)
+        self.popoBgMapInfo = pointer(self.poBgMapInfo)
+	if Vect_open_old(self.poBgMapInfo, name, mapset) == -1:
+            self.poBgMapInfo = self.popoBgMapInfo = None
+            self._error.NoMap(bgmap)
+            return
+        
+    def _getSnapMode(self):
+        """!Get snapping mode
+
+         - snap to vertex
+         - snap to nodes
+         - no snapping
+        
+        @return snap mode
+        """
+        threshold = self._display.GetThreshold()
+        if threshold > 0.0:
+            if UserSettings.Get(group = 'vdigit', key = 'snapToVertex', subkey = 'enabled'):
+                return SNAPVERTEX
+            else:
+                return SNAP
+        else:
+            return NO_SNAP
+    
+    def _breakLineAtIntersection(self, line, pointsLine, changeset):
+        """!Break given line at intersection
+
+        \param line line id
+        \param pointsLine line geometry
+        \param changeset id
+  
+        \return number of modified lines
+        """
+        if not self._checkMap():
+            return -1
+        
+        if not Vect_line_alive(self.poMapInfo, line):
+            return 0
+        
+        if not pointsLine:
+            if Vect_read_line(self.poMapInfo, self.poPoints, None, line) < 0:
+                self._error.ReadLine(line)
+                return -1
+            points = self.poPoints
+        else:
+            points = pointsLine
+        
+        listLine  = Vect_new_list()
+        listRef   = Vect_new_list()
+        listBreak = Vect_new_list()
+    
+        pointsCheck = Vect_new_line_struct()
+    
+        lineBox = bound_box()
+        # find all relevant lines
+        Vect_get_line_box(self.poMapInfo, line, byref(lineBox))
+        Vect_select_lines_by_box(self.poMapInfo, byref(lineBox),
+                                 GV_LINES, listLine)
+    
+        # check for intersection
+        Vect_list_append(listBreak, line)
+        Vect_list_append(listRef, line)
+        for i in range(listLine.contents.n_values):
+            lineBreak = listLine.contents.value[i]
+            if lineBreak == line:
+		continue
+            
+            ltype = Vect_read_line(self.poMapInfo, pointsCheck, None, lineBreak)
+            if not (ltype & GV_LINES):
+                continue
+            
+            if Vect_line_check_intersection(self.poPoints, pointsCheck,
+                                            WITHOUT_Z):
+                Vect_list_append(listBreak, lineBreak)
+        
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        
+        for i in range(listBreak.contents.n_values):
+            self._addActionToChangeset(changeset, listBreak.contents.value[i], add = False)
+        
+        ret = Vect_break_lines_list(self.poMapInfo, listBreak, listRef,
+                                    GV_LINES, None)
+        
+        for i in range(listBreak.contents.n_values):
+            if Vect_line_alive(self.poMapInfo, listBreak.contents.value[i]):
+                self._removeActionFromChangeset(changeset, listBreak.contents.value[i],
+                                                add = False)
+        
+        for line in range(nlines + 1, Vect_get_num_lines(self.poMapInfo) + 1):
+            self._addActionToChangeset(changeset, line, add = True)
+        
+        Vect_destroy_line_struct(pointsCheck)
+
+        Vect_destroy_list(listLine)
+        Vect_destroy_list(listBreak)
+        Vect_destroy_list(listRef)
+        
+        return ret
+    
+    def _addActionsBefore(self):
+        """!Register action before operation
+  
+        @return changeset id
+        """
+        changeset = len(self.changesets)
+        for line in self._display.selected['ids']:
+            if Vect_line_alive(self.poMapInfo, line):
+                self._addActionToChangeset(changeset, line, add = False)
+        
+        return changeset
+
+    def _applyChangeset(self, changeset, undo):
+        """!Apply changeset (undo/redo changeset)
+        
+        @param changeset changeset id
+        @param undo True for undo otherwise redo
+
+        @return 1 changeset applied
+        @return 0 changeset not applied
+        @return -1 on error
+        """
+        if changeset < 0 or changeset > len(self.changesets.keys()):
+            return -1
+        
+        if self.changesetEnd < 0:
+            self.changesetEnd = changeset
+            
+        ret = 0
+        actions = self.changesets[changeset]
+        for action in actions: 
+            add = action['add']
+            line = action['line']
+            if (undo and add) or \
+                    (not undo and not add):
+                if Vect_line_alive(self.poMapInfo, line):
+                    Debug.msg(3, "IVDigit._applyChangeset(): changeset=%d, action=add, line=%d -> deleted",
+                              changeset, line)
+                    Vect_delete_line(self.poMapInfo, line)
+                    ret = 1
+                else:
+                    Debug.msg(3, "Digit.ApplyChangeset(): changeset=%d, action=add, line=%d dead",
+                              changeset, line)
+            else: # delete
+                offset = action['offset']
+                if not Vect_line_alive(self.poMapInfo, line):
+                    Debug.msg(3, "Digit.ApplyChangeset(): changeset=%d, action=delete, line=%d -> added",
+                              changeset, line)
+                    if Vect_restore_line(self.poMapInfo, line, offset) < 0:
+                        return -1
+                    ret = 1
+                else:
+                    Debug.msg(3, "Digit.ApplyChangeset(): changeset=%d, action=delete, line=%d alive",
+                              changeset, line)
+        
+        return ret
+    
+    def _addActionsAfter(self, changeset, nlines):
+        """!Register action after operation
+
+        @param changeset changeset id
+        @param nline number of lines
+        """
+        for line in self._display.selected['ids']:
+            if Vect_line_alive(self.poMapInfo, line):
+                self._removeActionFromChangeset(changeset, line, add = False)
+        
+        for line in range(nlines + 1, Vect_get_num_lines(self.poMapInfo)):
+            if Vect_line_alive(self.poMapInfo, line):
+                self._addActionToChangeset(changeset, line, add = True)
+        
+    def _addActionToChangeset(self, changeset, line, add):
+        """!Add action to changeset
+        
+        @param changeset id of changeset
+        @param line feature id
+        @param add True to add, otherwise delete
+        """
+        if not self._checkMap():
+            return 
+        
+        if not Vect_line_alive(self.poMapInfo, line):
+            return
+        
+        offset = Vect_get_line_offset(self.poMapInfo, line)
+        
+        if changeset not in self.changesets:
+            self.changesets[changeset] = list()
+            self.changesetCurrent = changeset
+        
+        self.changesets[changeset].append({ 'add'    : add,
+                                            'line'   : line,
+                                            'offset' : offset })
+        
+        Debug.msg(3, "IVDigit._addActionToChangeset(): changeset=%d, add=%d, line=%d, offset=%d",
+                  changeset, add, line, offset)
+        
+    def _removeActionFromChangeset(self, changeset, line, add):
+        """!Remove action from changeset
+        
+        @param changeset changeset id
+        @param line line id
+        @param add True for add, False for delete
+        
+        @return number of actions in changeset
+        @return -1 on error
+        """
+        if changeset not in self.changesets.keys():
+            return -1
+        
+        alist = self.changesets[changeset] 
+        for action in alist:
+            if action['add'] == add and action['line'] == line:
+                alist.remove(action)
+        
+        return len(alist)
+
+    def AddFeature(self, ftype, points):
+        """!Add new feature
+        
+        @param ftype feature type (point, line, centroid, boundary)
+        @param points tuple of points ((x, y), (x, y), ...)
+        
+        @return tuple (number of added features, feature ids)
+        """
+        if UserSettings.Get(group = 'vdigit', key = "categoryMode", subkey = 'selection') == 2:
+            layer = -1 # -> no category
+            cat   = -1
+        else:
+            layer = UserSettings.Get(group = 'vdigit', key = "layer", subkey = 'value')
+            cat   = self.SetCategory()
+        
+        if ftype == 'point':
+            vtype = GV_POINT
+        elif ftype == 'line':
+            vtype = GV_LINE
+        elif ftype == 'centroid':
+            vtype = GV_CENTROID
+        elif ftype == 'boundary':
+            vtype = GV_BOUNDARY
+        elif ftype == 'area':
+            vtype = GV_AREA
+        else:
+            GError(parent = self.mapWindow,
+                   message = _("Unknown feature type '%s'") % ftype)
+            return (-1, None)
+        
+        if vtype & GV_LINES and len(points) < 2:
+            GError(parent = self.mapWindow,
+                   message = _("Not enough points for line"))
+            return (-1, None)
+        
+        self.toolbar.EnableUndo()
+        
+        return self._addFeature(vtype, points, layer, cat,
+                                self._getSnapMode(), self._display.GetThreshold())
+    
+    def DeleteSelectedLines(self):
+        """!Delete selected features
+
+        @return number of deleted features
+        """
+        deleteRec = UserSettings.Get(group = 'vdigit', key = 'delRecord', subkey = 'enabled')
+        if not self._checkMap():
+            return -1
+        
+        n_dblinks = Vect_get_num_dblinks(self.poMapInfo)
+        Cats_del = None
+        
+        # collect categories for delete if requested
+        if deleteRec:
+            poCatsDel = Vect_new_cats_struct()
+            for i in self._display.selected['ids']:
+                if Vect_read_line(self.poMapInfo, None, self.poCats, i) < 0:
+                    Vect_destroy_cats_struct(poCatsDel)
+                    self._error.ReadLine(i)
+                    return -1
+                
+                cats = self.poCats.contents
+                for j in range(cats.n_cats):
+                    Vect_cat_set(poCatsDel, cats.field[j], cats.cat[j])
+        
+        # register changeset
+        changeset = self._addActionsBefore()
+        poList = self._display.GetSelectedIList()
+        nlines = Vedit_delete_lines(self.poMapInfo, poList)
+        
+        Vect_destroy_list(poList)
+        self._display.selected['ids'] = list()
+        
+        if nlines > 0 and deleteRec:
+            handle  = dbHandle()
+            poHandle = pointer(handle)
+            stmt    = dbString()
+            poStmt   = pointer(stmt)
+            
+            for dblink in range(n_dblinks):
+                poFi = Vect_get_dblink(self.poMapInfo, dblink)
+                if poFi is None:
+                    self._error.DbLink(dblink)
+                    return -1
+                
+                Fi = poFi.contents
+                poDriver = db_start_driver(Fi.driver)
+                if poDriver is None:
+                    self._error.Driver(Fi.driver)
+                    return -1
+                
+                db_init_handle(poHandle)
+                db_set_handle(poHandle, Fi.database, None)
+                if db_open_database(poDriver, poHandle) != DB_OK:
+                    self._error.Database(Fi.driver, Fi.database)
+                    return -1
+                
+                db_init_string(poStmt)
+                db_set_string(poStmt, "DELETE FROM %s WHERE" % Fi.table)
+                n_cats = 0;
+                catsDel = poCatsDel.contents
+                for c in range(catsDel.n_cats):
+                    if catsDel.field[c] == Fi.number:
+                        if n_cats > 0:
+                            db_append_string(poStmt, " or")
+                    
+		    db_append_string(poStmt, " %s = %d" % (Fi.key, catsDel.cat[c]))
+		    n_cats += 1
+                
+                Vect_cat_del(poCatsDel, Fi.number)
+                
+                if n_cats and \
+                        db_execute_immediate(poDriver, poStmt) != DB_OK:
+                    self._error.DbExecute(db_get_string(poStmt))
+                    return -1
+                
+                db_close_database(poDriver)
+                db_shutdown_driver(poDriver)
+        
+        if poCatsDel:
+            Vect_destroy_cats_struct(poCatsDel)
+        
+        if nlines > 0:
+            self.toolbar.EnableUndo()
+        
+        return nlines
+
+    def MoveSelectedLines(self, move):
+        """!Move selected features
+
+        @param move direction (x, y)
+        """
+        if not self._checkMap():
+            return -1
+        
+        thresh = self._display.GetThreshold()
+        snap   = self._getSnapMode()
+        
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        
+        # register changeset
+        changeset = self._addActionsBefore()
+        
+        poList = self._display.GetSelectedIList()
+        nlines = Vedit_move_lines(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
+                                  poList,
+                                  move[0], move[1], 0,
+                                  snap, thresh)
+        Vect_destroy_list(poList)
+        
+        if nlines > 0:
+            self._addActionsAfter(changeset, nlines)
+        else:
+            del self.changesets[changeset]
+        
+        if nlines > 0 and self._settings['breakLines']:
+            for i in range(1, nlines):
+                self._breakLineAtIntersection(nlines + i, None, changeset)
+        
+        if nlines > 0:
+            self.toolbar.EnableUndo()
+        
+        return nlines
+
+    def MoveSelectedVertex(self, point, move):
+        """!Move selected vertex of the line
+
+        @param point location point
+        @param move  x,y direction
+        
+        @return id of new feature
+        @return 0 vertex not moved (not found, line is not selected)
+        @return -1 on error
+        """
+        if not self._checkMap():
+            return -1
+        
+        if len(self._display.selected['ids']) != 1:
+            return -1
+        
+        Vect_reset_line(self.poPoints)
+        Vect_append_point(self.poPoints, point[0], point[1], 0.0)
+        
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        
+        changeset = self._addActionsBefore()
+        
+        # move only first found vertex in bbox 
+        poList = self._display.GetSelectedIList()
+        moved = Vedit_move_vertex(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
+                                  poList, self.poPoints,
+                                  self._display.GetThreshold(type = 'selectThresh'),
+                                  self._display.GetThreshold(),
+                                  move[0], move[1], 0.0,
+                                  1, self._getSnapMode())
+        Vect_destroy_list(poList)
+        
+        if moved > 0:
+            self._addActionsAfter(changeset, nlines)
+        else:
+            del self.changesets[changeset]
+        
+        if moved > 0 and self._settings['breakLines']:
+            self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
+                                          None, changeset)
+        
+        if moved > 0:
+            self.toolbar.EnableUndo()
+        
+        return moved
+
+    def AddVertex(self, coords):
+        """!Add new vertex to the selected line/boundary on position 'coords'
+
+        @param coords coordinates to add vertex
+
+        @return id of new feature
+        @return 0 nothing changed
+        @return -1 on failure
+        """
+        added = self._ModifyLineVertex(coords, add = True)
+        
+        if added > 0:
+            self.toolbar.EnableUndo()
+
+        return added
+
+    def RemoveVertex(self, coords):
+        """!Remove vertex from the selected line/boundary on position 'coords'
+
+        @param coords coordinates to remove vertex
+
+        @return id of new feature
+        @return 0 nothing changed
+        @return -1 on failure
+        """
+        deleted = self._ModifyLineVertex(coords, add = False)
+        
+        if deleted > 0:
+            self.toolbar.EnableUndo()
+
+        return deleted
+
+
+    def SplitLine(self, point):
+        """!Split/break selected line/boundary on given position
+
+        @param point point where to split line
+        
+        @return 1 line modified
+        @return 0 nothing changed
+        @return -1 error
+        """
+        thresh = self._display.GetThreshold('selectThresh')
+        if not self._checkMap():
+            return -1
+        
+        poList  = self._display.GetSelectedIList()
+        Vect_reset_line(self.poPoints)
+        Vect_append_point(self.poPoints, point[0], point[1], 0.0)
+        
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        
+        changeset = self._addActionsBefore()
+        
+        ret = Vedit_split_lines(self.poMapInfo, poList,
+                                self.poPoints, thresh, None)
+        Vect_destroy_list(poList)
+        
+        if ret > 0:
+            self._addActionsAfter(changeset, nlines)
+            self.toolbar.EnableUndo()
+        else:
+            del self.changesets[changeset]
+        
+        return ret
+
+    def EditLine(self, line, coords):
+        """!Edit existing line/boundary
+
+        @param line feature id to be modified
+        @param coords list of coordinates of modified line
+
+        @return feature id of new line
+        @return -1 on error
+        """
+        if not self._checkMap():
+            return -1
+        
+        if len(coords) < 2:
+            self.DeleteSelectedLines()
+            return 0
+        
+        if not Vect_line_alive(self.poMapInfo, line):
+            self._error.DeadLine(line)
+            return -1
+        
+        # read original feature
+        ltype = Vect_read_line(self.poMapInfo, None, self.poCats, line)
+        if ltype < 0:
+            self._error.ReadLine(line)
+            return -1
+        
+        # build feature geometry
+        Vect_reset_line(self.poPoints)
+        for p in coords:
+            Vect_append_point(self.poPoints, p[0], p[1], 0.0)
+
+        # apply snapping (node or vertex)
+        snap = self._getSnapMode()
+        if snap != NO_SNAP:
+            modeSnap = not (snap == SNAP)
+            Vedit_snap_line(self.poMapInfo, self.popoBgMapInfo,
+                            int(self.poBgMapInfo is not None),
+                           -1, self.poPoints, self._display.GetThreshold(), modeSnap)
+
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        
+        changeset = self._addActionsBefore()
+        newline = Vect_rewrite_line(self.poMapInfo, line, ltype,
+                                    self.poPoints, self.poCats)
+        if newline > 0:
+            self._addActionsAfter(changeset, nlines)
+            self.toolbar.EnableUndo()
+        else:
+            del self.changesets[changeset]
+        
+        if newline > 0 and self._settings['breakLines']:
+            self._breakLineAtIntersection(newline, None, changeset)
+        
+        return newline
+
+    def FlipLine(self):
+        """!Flip selected lines/boundaries
+
+        @return number of modified lines
+        @return -1 on error
+        """
+        if not self._checkMap():
+            return -1
+        
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        
+        # register changeset
+        changeset = self._addActionsBefore()
+        
+        poList = self._display.GetSelectedIList()
+        ret = Vedit_flip_lines(self.poMapInfo, poList)
+        Vect_destroy_list(poList)
+        
+        if ret > 0:
+            self._addActionsAfter(changeset, nlines)
+            self.toolbar.EnableUndo()
+        else:
+            del self.changesets[changeset]
+        
+        return ret
+
+    def MergeLine(self):
+        """!Merge selected lines/boundaries
+
+        @return number of modified lines
+        @return -1 on error
+        """
+        if not self._checkMap():
+            return -1
+        
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        
+        changeset = self._addActionsBefore()
+        
+        poList = self._display.GetSelectedIList()
+        ret = Vedit_merge_lines(self.poMapInfo, poList)
+        Vect_destroy_list(poList)
+        
+        if ret > 0:
+            self._addActionsAfter(changeset, nlines)
+            self.toolbar.EnableUndo()
+        else:
+            del self.changesets[changeset]
+                
+        return ret
+
+    def BreakLine(self):
+        """!Break selected lines/boundaries
+
+        @return number of modified lines
+        @return -1 on error
+        """
+        if not self._checkMap():
+            return -1
+        
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        
+        changeset = self._addActionsBefore()
+        
+        poList = self._display.GetSelectedIList()
+        ret = Vect_break_lines_list(self.poMapInfo, poList, None,
+                                    GV_LINES, None)
+        Vect_destroy_list(poList)
+        
+        if ret > 0:
+            self._addActionsAfter(changeset, nlines)
+            self.toolbar.EnableUndo()
+        else:
+            del self.changesets[changeset]
+                
+        return ret
+
+    def SnapLine(self):
+        """!Snap selected lines/boundaries
+
+        @return on success
+        @return -1 on error
+        """
+        if not self._checkMap():
+            return -1
+        
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        
+        changeset = self._addActionsBefore()
+        
+        poList = self._display.GetSelectedIList()
+        Vect_snap_lines_list(self.poMapInfo, poList,
+                             self._display.GetThreshold(), None)
+        Vect_destroy_list(poList)
+        
+        if nlines < Vect_get_num_lines(self.poMapInfo):
+            self._addActionsAfter(changeset, nlines)
+            self.toolbar.EnableUndo()
+        else:
+            del self.changesets[changeset]
+        
+    def ConnectLine(self):
+        """!Connect selected lines/boundaries
+
+        @return 1 lines connected
+        @return 0 lines not connected
+        @return -1 on error
+        """
+        if not self._checkMap():
+            return -1
+        
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        
+        # register changeset
+        changeset = self._addActionsBefore()
+        
+        poList = self._display.GetSelectedIList()
+        ret = Vedit_connect_lines(self.poMapInfo, poList,
+                                  self._display.GetThreshold())
+        Vect_destroy_list(poList)
+        
+        if ret > 0:
+            self._addActionsAfter(changeset, nlines)
+            self.toolbar.EnableUndo()
+        else:
+            del self.changesets[changeset]
+        
+        return ret
+        
+    def CopyLine(self, ids = []):
+        """!Copy features from (background) vector map
+
+        @param ids list of line ids to be copied
+
+        @return number of copied features
+        @return -1 on error
+        """
+        if not self._checkMap():
+            return -1
+        
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        
+        poList = self._display.GetSelectedIList(ids)
+        ret = Vedit_copy_lines(self.poMapInfo, self.poBgMapInfo,
+                               poList)
+        Vect_destroy_list(poList)
+        
+        if ret > 0:
+            changeset = len(self.changesets)
+            for line in (range(nlines + 1, Vect_get_num_lines(self.poMapInfo))):
+                self._addActionToChangeset(changeset, line, add = True)
+            self.toolbar.EnableUndo()
+        else:
+            del self.changesets[changeset]
+
+        if ret > 0 and self.poBgMapInfo and self._settings['breakLines']:
+            for i in range(1, ret):
+                self._breakLineAtIntersection(nlines + i, None, changeset)
+        
+        return ret
+
+    def CopyCats(self, fromId, toId, copyAttrb = False):
+        """!Copy given categories to objects with id listed in ids
+
+        @param cats ids of 'from' feature
+        @param ids  ids of 'to' feature(s)
+
+        @return number of modified features
+        @return -1 on error
+        """
+        if len(fromId) < 1 or len(toId) < 1:
+            return 0
+        
+        poCatsFrom = self.poCats
+        poCatsTo = Vect_new_cats_struct();
+        
+        nlines = 0
+        
+        for fline in fromId:
+            if not Vect_line_alive(self.poMapInfo, fline):
+                continue
+            
+            if Vect_read_line(self.poMapInfo, None, poCatsFrom, fline) < 0:
+                self._error.ReadLine(fline)
+                return -1
+            
+            for tline in toId:
+                if not Vect_line_alive(self.poMapInfo, tline):
+                    continue
+                
+                ltype = Vect_read_line(self.poMapInfo, self.poPoints, poCatsTo, tline)
+                if ltype < 0:
+                    self._error.ReadLine(fline)
+                    return -1
+                
+                catsFrom = poCatsFrom.contents
+                for i in range(catsFrom.n_cats):
+                    if not copyAttrb:
+                        # duplicate category
+                        cat = catsFrom.cat[i]
+                    else:
+                        # duplicate attributes
+                        cat = self.cats[catsFrom.field[i]] + 1
+                        self.cats[catsFrom.field[i]] = cat
+                        poFi = Vect_get_field(self.poMapInfo, catsFrom.field[i])
+                        if not poFi:
+                            self._error.DbLink(i)
+                            return -1
+                        
+                        fi = poFi.contents
+                        driver = db_start_driver(fi.driver)
+                        if not driver:
+                            self._error.Driver(fi.driver)
+                            return -1
+                        
+                        handle = dbHandle()
+                        db_init_handle(byref(handle))
+                        db_set_handle(byref(handle), fi.database, None)
+                        if db_open_database(driver, byref(handle)) != DB_OK:
+                            db_shutdown_driver(driver)
+                            self._error.Database(fi.driver, fi.database)
+                            return -1
+                        
+                        stmt = dbString()
+                        db_init_string(byref(stmt))
+                        db_set_string(byref(stmt),
+                                      "SELECT * FROM %s WHERE %s=%d" % (fi.table, fi.key,
+                                                                        catsFrom.cat[i]))
+                        
+                        cursor = dbCursor()
+                        if db_open_select_cursor(driver, byref(stmt), byref(cursor),
+                                                 DB_SEQUENTIAL) != DB_OK:
+                                db_close_database_shutdown_driver(driver)
+                                return -1
+                        
+                        table = db_get_cursor_table(byref(cursor))
+                        ncols = db_get_table_number_of_columns(table)
+                        
+                        sql = "INSERT INTO %s VALUES (" % fi.table
+                        # fetch the data
+                        more = c_int()
+                        while True:
+                            if db_fetch(byref(cursor), DB_NEXT, byref(more)) != DB_OK:
+                                db_close_database_shutdown_driver(driver)
+                                return -1
+                            if not more.value:
+                                break
+                            
+                            value_string = dbString()
+                            for col in range(ncols):
+                                if col > 0:
+                                    sql += ","
+                                    
+                                column = db_get_table_column(table, col)
+                                if db_get_column_name(column) == fi.key:
+                                    sql += "%d" % cat
+                                    continue
+                                
+                                value = db_get_column_value(column)
+                                db_convert_column_value_to_string(column, byref(value_string))
+                                if db_test_value_isnull(value):
+                                    sql += "NULL"
+                                else:
+                                    ctype = db_sqltype_to_Ctype(db_get_column_sqltype(column))
+                                    if ctype != DB_C_TYPE_STRING:
+                                        sql += db_get_string(byref(value_string))
+                                    else:
+                                        sql += "'%s'" % db_get_string(byref(value_string))
+                        
+                        sql += ")"
+                        db_set_string(byref(stmt), sql)
+                        if db_execute_immediate(driver, byref(stmt)) != DB_OK:
+                            db_close_database_shutdown_driver(driver)
+                            return -1
+                        
+                        db_close_database_shutdown_driver(driver)
+                        G_free(poFi)
+                
+                if Vect_cat_set(poCatsTo, catsFrom.field[i], cat) < 1:
+                    continue
+                
+                if Vect_rewrite_line(self.poMapInfo, tline, ltype, self.poPoints, poCatsTo) < 0:
+                    self._error.WriteLine()
+                    return -1
+                
+                nlines +=1
+        
+        Vect_destroy_cats_struct(poCatsTo)
+        
+        if nlines > 0:
+            self.toolbar.EnableUndo()
+        
+        return nlines
+
+    def _selectLinesByQueryThresh(self):
+        """!Generic method used for SelectLinesByQuery() -- to get
+        threshold value"""
+        thresh = 0.0
+        if UserSettings.Get(group = 'vdigit', key = 'query', subkey = 'selection') == 0:
+            thresh = UserSettings.Get(group = 'vdigit', key = 'queryLength', subkey = 'thresh')
+            if UserSettings.Get(group = 'vdigit', key = "queryLength", subkey = 'than-selection') == 0:
+                thresh = -1 * thresh
+        else:
+            thresh = UserSettings.Get(group = 'vdigit', key = 'queryDangle', subkey = 'thresh')
+            if UserSettings.Get(group = 'vdigit', key = "queryDangle", subkey = 'than-selection') == 0:
+                thresh = -1 * thresh
+        
+        return thresh
+
+    def SelectLinesByQuery(self, bbox):
+        """!Select features by query
+        
+        @todo layer / 3D
+        
+        @param bbox bounding box definition
+        """
+        if not self._checkMap():
+            return -1
+        
+        thresh = self._selectLinesByQueryThresh()
+        
+        query = QUERY_UNKNOWN
+        if UserSettings.Get(group = 'vdigit', key = 'query', subkey = 'selection') == 0:
+            query = QUERY_LENGTH
+        else:
+            query = QUERY_DANGLE
+        
+        ftype = GV_POINTS | GV_LINES # TODO: 3D
+        layer = 1 # TODO
+        
+        ids = list()
+        poList = Vect_new_list()
+        coList = poList.contents
+        if UserSettings.Get(group = 'vdigit', key = 'query', subkey = 'box'):
+            Vect_reset_line(self.poPoints)
+            x1, y1 = bbox[0]
+            x2, y2 = bbox[1]
+            z1 = z2 = 0.0
+            
+            Vect_append_point(self.poPoints, x1, y1, z1)
+            Vect_append_point(self.poPoints, x2, y1, z2)
+            Vect_append_point(self.poPoints, x2, y2, z1)
+            Vect_append_point(self.poPoints, x1, y2, z2)
+            Vect_append_point(self.poPoints, x1, y1, z1)
+	
+            Vect_select_lines_by_polygon(self.poMapInfo, self.poPoints, 0, None,
+                                         ftype, poList)
+            
+            if coList.n_values == 0:
+                return ids
+        
+        Vedit_select_by_query(self.poMapInfo,
+                              ftype, layer, thresh, query,
+                              poList)
+        
+        for i in range(coList.n_values):
+            ids.append(int(coList.value[i]))
+            
+        Debug.msg(3, "IVDigit.SelectLinesByQuery(): lines=%d", coList.n_values)    
+        Vect_destroy_list(poList)
+        
+        return ids
+
+    def IsVector3D(self):
+        """!Check if open vector map is 3D
+        """
+        if not self._checkMap():
+            return False
+        
+        return Vect_is_3d(self.poMapInfo)
+    
+    def GetLineLength(self, line):
+        """!Get line length
+
+        @param line feature id
+
+        @return line length
+        @return -1 on error
+        """
+        if not self._checkMap():
+            return -1
+        
+        if not Vect_line_alive(self.poMapInfo, line):
+            return -1
+    
+        ltype = Vect_read_line(self.poMapInfo, self.poPoints, None, line)
+        if ltype < 0:
+            self._error.ReadLine(line)
+            return ret
+        
+        length = -1
+        if ltype & GV_LINES: # lines & boundaries
+            length = Vect_line_length(self.poPoints)
+        
+        return length
+
+    def GetAreaSize(self, centroid):
+        """!Get area size
+
+        @param centroid centroid id
+
+        @return area size
+        @return -1 on error
+        """
+        if not self._checkMap():
+            return -1
+        
+        ltype = Vect_read_line(self.poMapInfo, None, None, centroid)
+        if ltype < 0:
+            self._error.ReadLine(line)
+            return ret
+        
+        if ltype != GV_CENTROID:
+            return -1
+        
+        area = Vect_get_centroid_area(self.poMapInfo, centroid)
+        size = -1
+        if area > 0:
+            if not Vect_area_alive(self.poMapInfo, area):
+                return size
+            
+            size = Vect_get_area_area(self.poMapInfo, area)
+        
+        return size
+        
+    def GetAreaPerimeter(self, centroid):
+        """!Get area perimeter
+        
+        @param centroid centroid id
+        
+        @return area size
+        @return -1 on error
+        """
+        if not self._checkMap():
+            return -1
+        
+        ltype = Vect_read_line(self.poMapInfo, None, None, centroid)
+        if ltype < 0:
+            self._error.ReadLine(line)
+            return ret
+        
+        if ltype != GV_CENTROID:
+            return -1
+        
+        area = Vect_get_centroid_area(self.poMapInfo, centroid)
+        perimeter = -1
+        if area > 0:
+            if not Vect_area_alive(self.poMapInfo, area):
+                return -1
+            
+            Vect_get_area_points(self.poMapInfo, area, self.poPoints)
+            perimeter = Vect_area_perimeter(self.poPoints)
+        
+        return perimeter
+    
+    def SetLineCats(self, line, layer, cats, add = True):
+        """!Set categories for given line and layer
+
+        @param line feature id
+        @param layer layer number (-1 for first selected line)
+        @param cats list of categories
+        @param add if True to add, otherwise do delete categories
+
+        @return new feature id (feature need to be rewritten)
+        @return -1 on error
+        """
+        if not self._checkMap():
+            return -1
+        
+        if line < 1 and len(self._display.selected['ids']) < 1:
+            return -1
+        
+        update = False
+        if line == -1:
+            update = True
+            line = self._display.selected['ids'][0]
+	
+        if not Vect_line_alive(self.poMapInfo, line):
+            return -1
+        
+        ltype = Vect_read_line(self.poMapInfo, self.poPoints, self.poCats, line)
+        if ltype < 0:
+            self._error.ReadLine(line)
+            return -1
+        
+        for c in cats:
+            if add:
+                Vect_cat_set(self.poCats, layer, c)
+            else:
+                Vect_field_cat_del(self.poCats, layer, c)
+        
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        changeset = self._addActionsBefore()
+        newline = Vect_rewrite_line(self.poMapInfo, line, ltype,
+                                    self.poPoints, self.poCats)
+        
+        if newline > 0:
+            self._addActionsAfter(changeset, nlines)
+            self.toolbar.EnableUndo()
+        
+        if update:
+            # update line id since the line was rewritten
+            self._display.selected['ids'][0] =  newline
+        
+        return newline
+
+    def TypeConvForSelectedLines(self):
+        """!Feature type conversion for selected objects.
+
+        Supported conversions:
+         - point <-> centroid
+         - line <-> boundary
+
+        @return number of modified features
+        @return -1 on error
+        """
+        if not self._checkMap():
+            return -1
+        
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        
+        # register changeset
+        changeset = self._addActionsBefore()
+        
+        poList = self._display.GetSelectedIList()
+        ret = Vedit_chtype_lines(self.poMapInfo, poList)
+        Vect_destroy_list(poList)
+        
+        if ret > 0:
+            self._addActionsAfter(changeset, nlines)
+            self.toolbar.EnableUndo()
+        else:
+            del self.changesets[changeset]
+        
+        return ret
+
+    def Undo(self, level = -1):
+        """!Undo action
+
+        @param level levels to undo (0 to revert all)
+
+        @return id of current changeset
+        """
+        changesetLast = len(self.changesets.keys()) - 1
+
+        if changesetLast < 0:
+            return changesetLast
+        
+        if self.changesetCurrent == -2: # value uninitialized 
+            self.changesetCurrent = changesetLast
+            
+        if level > 0 and self.changesetCurrent < 0:
+            self.changesetCurrent = 0
+        
+        if level == 0:
+            # 0 -> undo all
+            level = -1 * changesetLast + 1
+
+        Debug.msg(2, "Digit.Undo(): changeset_last=%d, changeset_current=%d, level=%d",
+                  changesetLast, self.changesetCurrent, level)
+    
+        if level < 0: # undo
+            if self.changesetCurrent + level < -1:
+                return changesetCurrent;
+            for changeset in range(self.changesetCurrent, self.changesetCurrent + level, -1):
+                self._applyChangeset(changeset, undo = True)
+        elif level > 0: # redo 
+            if self.changesetCurrent + level > len(self.changesets.keys()):
+                return self.changesetCurrent
+            for changeset in range(self.changesetCurrent, self.changesetCurrent + level):
+                self._applyChangeset(changeset, undo = False)
+        
+        self.changesetCurrent += level
+
+        Debug.msg(2, "Digit.Undo(): changeset_current=%d, changeset_last=%d, changeset_end=%d",
+                  self.changesetCurrent, changesetLast, self.changesetEnd)
+        
+        if self.changesetCurrent == self.changesetEnd:
+            self.changesetEnd = changesetLast
+            return -1
+        
+        self.mapWindow.UpdateMap(render = False)
+        
+        if self.changesetCurrent < 0: # disable undo tool
+            self.toolbar.EnableUndo(False)
+
+    def ZBulkLines(self, pos1, pos2, start, step):
+        """!Z-bulk labeling
+
+        @param pos1 reference line (start point)
+        @param pos1 reference line (end point)
+        @param start starting value
+        @param step step value
+
+        @return number of modified lines
+        @return -1 on error
+        """
+        if not self._checkMap():
+            return -1
+        
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        
+        # register changeset
+        changeset = self._addActionsBefore()
+        
+        poList = self._display.GetSelectedIList()
+        ret = Vedit_bulk_labeling(self.poMapInfo, poList,
+                                  pos1[0], pos1[1], pos2[0], pos2[1],
+                                  start, step)
+        Vect_destroy_list(poList)
+        
+        if ret > 0:
+            self._addActionsAfter(changeset, nlines)
+            self.toolbar.EnableUndo()
+        else:
+            del self.changesets[changeset]
+        
+        return ret
+    
+    def GetDisplay(self):
+        """!Get display driver instance"""
+        return self._display
+    
+    def OpenMap(self, name):
+        """!Open vector map for editing
+        
+        @param map name of vector map to be set up
+        """
+        Debug.msg (3, "AbstractDigit.SetMapName map=%s" % name)
+        
+        name, mapset = name.split('@')
+        
+        self.poMapInfo = self._display.OpenMap(str(name), str(mapset), True)
+        
+        if self.poMapInfo:
+            self.InitCats()
+        
+        return self.poMapInfo
+    
+    def CloseMap(self):
+        """!Close currently open vector map
+        """
+        if not self._checkMap():
+            return
+        
+        self._display.CloseMap()
+
+    def InitCats(self):
+        """!Initialize categories information
+        
+        @return 0 on success
+        @return -1 on error
+        """
+        self.cats.clear()
+        if not self._checkMap():
+            return -1
+        
+        ndblinks = Vect_get_num_dblinks(self.poMapInfo)
+        for i in range(ndblinks):
+            fi = Vect_get_dblink(self.poMapInfo, i).contents
+            if fi:
+                self.cats[fi.number] = None
+        
+        # find max category
+        nfields = Vect_cidx_get_num_fields(self.poMapInfo)
+        Debug.msg(2, "wxDigit.InitCats(): nfields=%d", nfields)
+        
+        for i in range(nfields):
+            field = Vect_cidx_get_field_number(self.poMapInfo, i)
+            ncats = Vect_cidx_get_num_cats_by_index(self.poMapInfo, i)
+            if field <= 0:
+                continue
+            for j in range(ncats):
+                cat = c_int()
+                type = c_int()
+                id = c_int()
+                Vect_cidx_get_cat_by_index(self.poMapInfo, i, j,
+                                           byref(cat), byref(type), byref(id))
+                if field in self.cats:
+                    if cat > self.cats[field]:
+                        self.cats[field] = cat.value
+                else:
+                    self.cats[field] = cat.value
+            Debug.msg(3, "wxDigit.InitCats(): layer=%d, cat=%d", field, self.cats[field])
+            
+        # set default values
+        for field, cat in self.cats.iteritems():
+            if cat == None:
+                self.cats[field] = 0 # first category 1
+	    Debug.msg(3, "wxDigit.InitCats(): layer=%d, cat=%d", field, self.cats[field])
+        
+    def _checkMap(self):
+        """!Check if map is open
+        """
+        if not self.poMapInfo:
+            self._error.NoMap()
+            return False
+        
+        return True
+
+    def _addFeature(self, ftype, coords, layer, cat, snap, threshold):
+        """!Add new feature(s) to the vector map
+
+        @param ftype feature type (GV_POINT, GV_LINE, GV_BOUNDARY, ...)
+        @coords tuple of coordinates ((x, y), (x, y), ...)
+        @param layer layer number (-1 for no cat)
+        @param cat category number
+        @param snap snap to node/vertex
+        @param threshold threshold for snapping
+        
+        @return tuple (number of added features, list of fids)
+        @return number of features -1 on error
+        """
+        fids = list()
+        if not self._checkMap():
+            return (-1, None)
+        
+        is3D = bool(Vect_is_3d(self.poMapInfo))
+        
+        Debug.msg(2, "IVDigit._addFeature(): npoints=%d, layer=%d, cat=%d, snap=%d",
+                  len(coords), layer, cat, snap)
+        
+        if not (ftype & (GV_POINTS | GV_LINES | GV_AREA)): # TODO: 3D
+            self._error.FeatureType(ftype)
+            return (-1, None)
+        
+        # set category
+        Vect_reset_cats(self.poCats)
+        if layer > 0 and ftype != GV_AREA:
+            Vect_cat_set(self.poCats, layer, cat)
+            self.cats[layer] = max(cat, self.cats.get(layer, 1))
+        
+        # append points
+        Vect_reset_line(self.poPoints)
+        for c in coords:
+            Vect_append_point(self.poPoints, c[0], c[1], 0.0)
+        
+        if ftype & (GV_BOUNDARY | GV_AREA):
+            # close boundary
+            points = self.poPoints.contents
+            last = points.n_points - 1
+            if Vect_points_distance(points.x[0], points.x[0], points.z[0],
+                                    points.x[last], points.x[last], points.z[last],
+                                    is3D) <= threshold:
+                points.x[last] = points.x[0]
+                points.y[last] = points.y[0]
+                points.z[last] = points.z[0]
+        
+        if snap != NO_SNAP:
+            # apply snapping (node or vertex)
+            modeSnap = not (snap == SNAP)
+            Vedit_snap_line(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
+                            -1, self.poPoints, threshold, modeSnap)
+        
+        if ftype == GV_AREA:
+            ltype = GV_BOUNDARY
+        else:
+            ltype = ftype
+        newline = Vect_write_line(self.poMapInfo, ltype, self.poPoints, self.poCats)
+        if newline < 0:
+            self._error.WriteLine()
+            return (-1, None)
+        else:
+            fids.append(newline)
+        
+        left = right = -1
+        if ftype & GV_AREA:
+            # add centroids for left/right area
+            bpoints = Vect_new_line_struct()
+            cleft = c_int()
+            cright = c_int()
+            
+            Vect_get_line_areas(self.poMapInfo, newline,
+                                byref(cleft), byref(cright))
+            left = cleft.value
+            right = cright.value
+            
+            Debug.msg(3, "IVDigit._addFeature(): area - left=%d right=%d",
+                      left, right)
+            
+            # check if area exists and has no centroid inside
+            if layer > 0 and (left > 0 or right > 0):
+                Vect_cat_set(self.poCats, layer, cat)
+                self.cats[layer] = max(cat, self.cats.get(layer, 0))
+            
+            x = c_double()
+            y = c_double()
+            if left > 0 and \
+                    Vect_get_area_centroid(self.poMapInfo, left) == 0:
+                # if Vect_get_area_points(self.poMapInfo, left, bpoints) > 0 and
+                # Vect_find_poly_centroid(bpoints, byref(x), byref(y)) == 0:
+                if Vect_get_point_in_area(self.poMapInfo, left, byref(x), byref(y)) == 0:
+                    Vect_reset_line(bpoints)
+                    Vect_append_point(bpoints, x.value, y.value, 0.0)
+                    newline = Vect_write_line(self.poMapInfo, GV_CENTROID,
+                                              bpoints, self.poCats)
+                    if newline < 0:
+                        self._error.WriteLine()
+                        return (len(fids), fids)
+                    else:
+                        fids.append(newline)
+                    
+            if right > 0 and \
+                    Vect_get_area_centroid(self.poMapInfo, right) == 0:
+                # if Vect_get_area_points(byref(self.poMapInfo), right, bpoints) > 0 and 
+                # Vect_find_poly_centroid(bpoints, byref(x), byref(y)) == 0:
+                if Vect_get_point_in_area(self.poMapInfo, right, byref(x), byref(y)) == 0:
+                    Vect_reset_line(bpoints)
+                    Vect_append_point(bpoints, x.value, y.value, 0.0)
+                    newline =  Vect_write_line(self.poMapInfo, GV_CENTROID,
+                                               bpoints, self.poCats)
+                    if newline < 0:
+                        self._error.WriteLine()
+                        return (len(fids, fids))
+                    else:
+                        fids.append(newline)
+                    
+            Vect_destroy_line_struct(bpoints)
+        
+        # register changeset
+        changeset = len(self.changesets)
+        self._addActionToChangeset(changeset, newline, add = True)
+        
+        # break at intersection
+        if self._settings['breakLines']:
+            self._breakLineAtIntersection(newline, self.poPoints, changeset)
+        
+        return (len(fids), fids)
+    
+    def _ModifyLineVertex(self, coords, add = True):
+        """!Add or remove vertex
+        
+        Shape of line/boundary is not changed when adding new vertex.
+        
+        @param coords coordinates of point
+        @param add True to add, False to remove
+        
+        @return id id of the new feature
+        @return 0 nothing changed
+        @return -1 error
+        """
+        if not self._checkMap():
+            return -1
+        
+        selected = self._display.selected
+        if len(selected['ids']) != 1:
+            return 0
+        
+        poList  = self._display.GetSelectedIList()
+        Vect_reset_line(self.poPoints)
+        Vect_append_point(self.poPoints, coords[0], coords[1], 0.0)
+        
+        nlines = Vect_get_num_lines(self.poMapInfo)
+        thresh = self._display.GetThreshold(type = 'selectThresh')
+        
+        changeset = self._addActionsBefore()
+        
+        if add:
+            ret = Vedit_add_vertex(self.poMapInfo, poList,
+                                   self.poPoints, thresh)
+        else:
+            ret = Vedit_remove_vertex(self.poMapInfo, poList,
+                                      self.poPoints, thresh)
+        Vect_destroy_list(poList)
+        
+        if ret > 0:
+            self._addActionsAfter(changeset, nlines)
+        else:
+            del self.changesets[changeset]
+        
+        if not add and ret > 0 and self._settings['breakLines']:
+            self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
+                                          None, changeset)
+                
+        return nlines + 1 # feature is write at the end of the file
+    
+    def GetLineCats(self, line):
+        """!Get list of layer/category(ies) for selected feature.
+
+        @param line feature id (-1 for first selected feature)
+
+        @return list of layer/cats
+        """
+        ret = dict()
+        if not self._checkMap():
+            return ret
+        
+        if line == -1 and len(self._display.selected['ids']) < 1:
+            return ret
+        
+        if line == -1:
+            line = self._display.selected['ids'][0]
+            
+        if not Vect_line_alive(self.poMapInfo, line):
+            self._error.DeadLine(line)
+            return ret
+        
+        if Vect_read_line(self.poMapInfo, None, self.poCats, line) < 0:
+            self._error.ReadLine(line)
+            return ret
+        
+        cats = self.poCats.contents
+        for i in range(cats.n_cats):
+            field = cats.field[i]
+            if field not in ret:
+                ret[field] = list()
+            ret[field].append(cats.cat[i])
+        
+        return ret
+
+    def GetLayers(self):
+        """!Get list of layers
+        
+        Requires self.InitCats() to be called.
+
+        @return list of layers
+        """
+        return self.cats.keys()
+    
+    def UpdateSettings(self):
+        """!Update digit (and display) settings
+        """
+        self._display.UpdateSettings()
+        
+        self._settings['breakLines']  = bool(UserSettings.Get(group = 'vdigit', key = "breakLines",
+                                                              subkey = 'enabled'))
+        
+    def SetCategory(self):
+        """!Update self.cats based on settings"""
+        sel = UserSettings.Get(group = 'vdigit', key = 'categoryMode', subkey = 'selection')
+        cat = None
+        if sel == 0: # next to usep
+            cat = self._setCategoryNextToUse()
+        elif sel == 1:
+            cat = UserSettings.Get(group = 'vdigit', key = 'category', subkey = 'value')
+        
+        if cat:
+            layer = UserSettings.Get(group = 'vdigit', key = 'layer', subkey = 'value')
+            self.cats[layer] = cat
+        
+        return cat
+    
+    def _setCategoryNextToUse(self):
+        """!Find maximum category number for the given layer and
+        update the settings
+
+        @return category to be used
+        """
+        # get max category number for given layer and update the settings
+        layer = UserSettings.Get(group = 'vdigit', key = 'layer', subkey = 'value')
+        cat = self.cats.get(layer, 0) + 1
+        UserSettings.Set(group = 'vdigit', key = 'category', subkey = 'value',
+                         value = cat)
+        Debug.msg(1, "IVDigit._setCategoryNextToUse(): cat=%d", cat)
+        
+        return cat
+
+    def SelectLinesFromBackgroundMap(self, bbox):
+        """!Select features from background map
+
+        @param bbox bounding box definition
+        
+        @return list of selected feature ids
+        """
+        # try select features by box first
+        if self._display.SelectLinesByBox(bbox, poMapInfo = self.poBgMapInfo) < 1:
+            self._display.SelectLineByPoint(bbox[0], poMapInfo = self.poBgMapInfo)['line']
+            
+        return self._display.selected['ids']
+        
+    def GetUndoLevel(self):
+        """!Get undo level (number of active changesets)
+        
+        Note: Changesets starts wiht 0
+        """
+        return self.changesetCurrent


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/vdigit/wxdisplay.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/vdigit/wxdisplay.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/vdigit/wxdisplay.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,1035 @@
+"""!
+ at package vdigit.wxdisplay
+
+ at brief wxGUI vector digitizer (display driver)
+
+Code based on wxVdigit C++ component from GRASS 6.4.0
+(gui/wxpython/vdigit). Converted to Python in 2010/12-2011/01.
+
+List of classes:
+ - wxdisplay::DisplayDriver
+
+(C) 2007-2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+"""
+
+import locale
+
+import wx
+
+from core.debug    import Debug
+from core.settings import UserSettings
+
+try:
+    from grass.lib.gis    import *
+    from grass.lib.vector import *
+    from grass.lib.vedit  import *
+except ImportError:
+    pass
+
+log      = None
+progress = None
+
+def print_error(msg, type):
+    """!Redirect stderr"""
+    global log
+    if log:
+        log.write(msg)
+    else:
+        print msg
+    
+    return 0
+
+def print_progress(value):
+    """!Redirect progress info"""
+    global progress
+    if progress:
+        progress.SetValue(value)
+    else:
+        print value
+    
+    return 0
+
+errtype = CFUNCTYPE(UNCHECKED(c_int), String, c_int)
+errfunc = errtype(print_error)
+pertype = CFUNCTYPE(UNCHECKED(c_int), c_int)
+perfunc = pertype(print_progress)
+
+class DisplayDriver:
+    def __init__(self, device, deviceTmp, mapObj, window, glog, gprogress):
+        """!Display driver used by vector digitizer
+        
+        @param device    wx.PseudoDC device where to draw vector objects
+        @param deviceTmp wx.PseudoDC device where to draw temporary vector objects
+        @param mapOng    Map Object (render.Map)
+        @param windiow   parent window for dialogs
+        @param glog      logging device (None to discard messages)
+        @param gprogress progress bar device (None to discard message)
+        """
+        global errfunc, perfunc, log, progress
+        log = glog
+        progress = gprogress
+        
+        G_gisinit('wxvdigit')
+        locale.setlocale(locale.LC_NUMERIC, 'C')
+        G_set_error_routine(errfunc) 
+        G_set_percent_routine(perfunc)
+        
+        self.mapInfo   = None     # open vector map (Map_Info structure)
+        self.poMapInfo = None     # pointer to self.mapInfo
+        self.is3D      = False    # is open vector map 3D
+        
+        self.dc      = device     # PseudoDC devices
+        self.dcTmp   = deviceTmp
+        self.mapObj  = mapObj
+        self.region  = mapObj.GetCurrentRegion()
+        self.window  = window
+        self.log     = log        # log device
+
+        self.firstNode = True     # track PseudoDC Id of selected features
+        self.lastNodeId = -1
+        
+        # GRASS lib
+        self.poPoints = Vect_new_line_struct()
+        self.poCats   = Vect_new_cats_struct()
+        
+        # selected objects
+        self.selected = {
+            'field'   : -1,      # field number
+            'cats'    : list(),  # list of cats
+            'ids'     : list(),  # list of ids
+            'idsDupl' : list(),  # list of duplicated features
+            }
+        
+        # digitizer settings
+        self.settings = {
+            'highlight'     : None,
+            'highlightDupl' : { 'enabled' : False,
+                                'color'   : None },
+            'point'         : { 'enabled' : False,
+                                'color'   : None },
+            'line'          : { 'enabled' : False,
+                                'color'   : None },
+            'boundaryNo'    : { 'enabled' : False,
+                                'color'   : None },
+            'boundaryOne'   : { 'enabled' : False,
+                                'color'   : None },
+            'boundaryTwo'   : { 'enabled' : False,
+                                'color'   : None },
+            'centroidIn'    : { 'enabled' : False,
+                                'color'   : None },
+            'centroidOut'   : { 'enabled' : False,
+                                'color'   : None },
+            'centroidDup'   : { 'enabled' : False,
+                                'color'   : None },
+            'nodeOne'       : { 'enabled' : False,
+                                'color'   : None },
+            'nodeTwo'       : { 'enabled' : False,
+                                'color'   : None },
+            'vertex'        : { 'enabled' : False,
+                                'color'   : None },
+            'area'          : { 'enabled' : False,
+                                'color'   : None },
+            'direction'     : { 'enabled' : False,
+                                'color'   : None },
+            'lineWidth'     : -1,    # screen units 
+            }
+        
+        # topology
+        self._resetTopology()
+        
+        self._drawSelected = False
+        self._drawSegments = False
+        
+        self.UpdateSettings()
+        
+    def __del__(self):
+        """!Close currently open vector map"""
+        G_unset_error_routine()
+        G_unset_percent_routine()
+        
+        if self.poMapInfo:
+            self.CloseMap()
+        
+        Vect_destroy_line_struct(self.poPoints)
+        Vect_destroy_cats_struct(self.poCats)
+
+    def _resetTopology(self):
+        """!Reset topology dict
+        """
+        self.topology = {
+            'highlight'   : 0,
+            'point'       : 0,
+            'line'        : 0,
+            'boundaryNo'  : 0,
+            'boundaryOne' : 0,
+            'boundaryTwo' : 0,
+            'centroidIn'  : 0,
+            'centroidOut' : 0,
+            'centroidDup' : 0,
+            'nodeOne'     : 0,
+            'nodeTwo'     : 0,
+            'vertex'      : 0,
+            }
+        
+    def _cell2Pixel(self, east, north, elev):
+        """!Conversion from geographic coordinates (east, north)
+        to screen (x, y)
+  
+        @todo 3D stuff...
+
+        @param east, north, elev geographical coordinates
+
+        @return x, y screen coordinates (integer)
+        """
+        map_res = max(self.region['ewres'], self.region['nsres'])
+        w = self.region['center_easting']  - (self.mapObj.width  / 2) * map_res
+        n = self.region['center_northing'] + (self.mapObj.height / 2) * map_res
+        
+        return int((east - w) / map_res), int((n - north) / map_res)
+    
+    def _drawCross(self, pdc, point, size = 5):
+        """!Draw cross symbol of given size to device content
+   
+        Used for points, nodes, vertices
+
+        @param[in,out] PseudoDC where to draw
+        @param point coordinates of center
+        @param size size of the cross symbol
+   
+        @return 0 on success
+        @return -1 on failure
+        """
+        if not pdc or not point:
+            return -1
+        
+        pdc.DrawLine(point.x - size, point.y, point.x + size, point.y)
+        pdc.DrawLine(point.x, point.y - size, point.x, point.y + size)
+        
+        return 0
+    
+    def _drawObject(self, robj):
+        """!Draw given object to the device
+        
+        The object is defined as robject() from vedit.h.
+        
+        @param robj object to be rendered
+        
+        @return  1 on success
+        @return -1 on failure (vector feature marked as dead, etc.)
+        """
+        if not self.dc or not self.dcTmp:
+            return -1
+        
+        Debug.msg(3, "_drawObject(): line=%d type=%d npoints=%d", robj.fid, robj.type, robj.npoints)
+        brush = None
+        if self._isSelected(robj.fid):
+            pdc = self.dcTmp
+            if robj.type == TYPE_AREA:
+                return 1
+            else:
+                if self.settings['highlightDupl']['enabled'] and self._isDuplicated(robj.fid):
+                    pen = wx.Pen(self.settings['highlightDupl']['color'], self.settings['lineWidth'], wx.SOLID)
+                else:            
+                    pen = wx.Pen(self.settings['highlight'], self.settings['lineWidth'], wx.SOLID)
+                    
+            dcId = 1
+            self.topology['highlight'] += 1
+            if not self._drawSelected:
+                return
+        else:
+            pdc = self.dc
+            pen, brush = self._definePen(robj.type)
+            dcId = 0
+        
+        pdc.SetPen(pen)        
+        if brush:
+            pdc.SetBrush(brush)
+        
+        if robj.type & (TYPE_POINT | TYPE_CENTROIDIN | TYPE_CENTROIDOUT | TYPE_CENTROIDDUP |
+                        TYPE_NODEONE | TYPE_NODETWO | TYPE_VERTEX): # -> point
+            if dcId > 0:
+                if robj.type == TYPE_VERTEX:
+                    dcId = 3 # first vertex
+                elif robj.type & (TYPE_NODEONE | TYPE_NODETWO):
+                    if self.firstNode:
+                        dcId = 1
+                        self.firstNode = False
+                    else:
+                        dcId = self.lastNodeId
+            
+            for i in range(robj.npoints):
+                p = robj.point[i]
+                if dcId > 0:
+                    pdc.SetId(dcId)
+                    dcId += 2
+                self._drawCross(pdc, p)
+        else:
+            if dcId > 0 and self._drawSegments:
+                self.fisrtNode = True
+                self.lastNodeId = robj.npoints * 2 - 1
+                dcId = 2 # first segment
+                i = 0
+                while i < robj.npoints - 1:
+                    point_beg = wx.Point(robj.point[i].x, robj.point[i].y)
+                    point_end = wx.Point(robj.point[i+1].x, robj.point[i+1].y)
+                    pdc.SetId(dcId) # set unique id & set bbox for each segment
+                    pdc.SetPen(pen)
+                    pdc.SetIdBounds(dcId - 1, wx.Rect(point_beg.x, point_beg.y, 0, 0))
+                    pdc.SetIdBounds(dcId, wx.RectPP(point_beg, point_end))
+                    pdc.DrawLine(point_beg.x, point_beg.y,
+                                 point_end.x, point_end.y)
+                    i    += 1
+                    dcId += 2
+                pdc.SetIdBounds(dcId - 1, wx.Rect(robj.point[robj.npoints - 1].x,
+                                                  robj.point[robj.npoints - 1].y,
+                                                  0, 0))
+            else:
+                points = list()
+                for i in range(robj.npoints):
+                    p = robj.point[i]
+                    points.append(wx.Point(p.x, p.y))
+                    
+                if robj.type == TYPE_AREA:
+                    pdc.DrawPolygon(points)
+                else:
+                    pdc.DrawLines(points)
+        
+    def _definePen(self, rtype):
+        """!Define pen/brush based on rendered object)
+        
+        Updates also self.topology dict
+
+        @return pen, brush
+        """
+        if rtype == TYPE_POINT:
+            key = 'point'
+        elif rtype == TYPE_LINE:
+            key = 'line'
+        elif rtype == TYPE_BOUNDARYNO:
+            key = 'boundaryNo'
+        elif rtype == TYPE_BOUNDARYTWO:
+            key = 'boundaryTwo'
+        elif rtype == TYPE_BOUNDARYONE:
+            key = 'boundaryOne'
+        elif rtype == TYPE_CENTROIDIN:
+            key = 'centroidIn'
+        elif rtype == TYPE_CENTROIDOUT:
+            key = 'centroidOut'
+        elif rtype == TYPE_CENTROIDDUP:
+            key = 'centroidDup'
+        elif rtype == TYPE_NODEONE:
+            key = 'nodeOne'
+        elif rtype == TYPE_NODETWO:
+            key = 'nodeTwo'
+        elif rtype == TYPE_VERTEX:
+            key = 'vertex'
+        elif rtype == TYPE_AREA:
+            key = 'area' 
+        elif rtype == TYPE_ISLE:
+            key = 'isle'
+        elif rtype == TYPE_DIRECTION:
+            key = 'direction'
+        
+        if key not in ('direction', 'area', 'isle'):
+            self.topology[key] += 1
+        
+        if key in ('area', 'isle'):
+            pen = wx.TRANSPARENT_PEN
+            if key == 'area':
+                brush = wx.Brush(self.settings[key]['color'], wx.SOLID)
+            else:
+                brush = wx.TRANSPARENT_BRUSH
+        else:
+            pen = wx.Pen(self.settings[key]['color'], self.settings['lineWidth'], wx.SOLID)
+            brush = None
+        
+        return pen, brush
+        
+    def _getDrawFlag(self):
+        """!Get draw flag from the settings
+        
+        See vedit.h for list of draw flags.
+        
+        @return draw flag (int)
+        """
+        ret = 0
+        if self.settings['point']['enabled']:
+            ret |= DRAW_POINT
+        if self.settings['line']['enabled']:
+            ret |= DRAW_LINE
+        if self.settings['boundaryNo']['enabled']:
+            ret |= DRAW_BOUNDARYNO
+        if self.settings['boundaryTwo']['enabled']:
+            ret |= DRAW_BOUNDARYTWO
+        if self.settings['boundaryOne']['enabled']:
+            ret |= DRAW_BOUNDARYONE
+        if self.settings['centroidIn']['enabled']:
+            ret |= DRAW_CENTROIDIN
+        if self.settings['centroidOut']['enabled']:
+            ret |= DRAW_CENTROIDOUT
+        if self.settings['centroidDup']['enabled']:
+            ret |= DRAW_CENTROIDDUP
+        if self.settings['nodeOne']['enabled']:
+            ret |= DRAW_NODEONE
+        if self.settings['nodeTwo']['enabled']:
+            ret |= DRAW_NODETWO
+        if self.settings['vertex']['enabled']:
+            ret |= DRAW_VERTEX
+        if self.settings['area']['enabled']:
+            ret |= DRAW_AREA
+        if self.settings['direction']['enabled']:
+            ret |= DRAW_DIRECTION
+        
+        return ret
+        
+    def _isSelected(self, line, force = False):
+        """!Check if vector object selected?
+   
+        @param line feature id
+
+        @return True if vector object is selected
+        @return False if vector object is not selected
+        """
+        if line in self.selected['ids']:
+            return True
+        
+        return False
+
+    def _isDuplicated(self, line):
+        """!Check for already marked duplicates
+        
+        @param line feature id
+
+        @return True line already marked as duplicated
+        @return False not duplicated
+        """
+        return line in self.selected['idsDupl']
+    
+    def _getRegionBox(self):
+        """!Get bound_box() from current region
+
+        @return bound_box
+        """
+        box = bound_box()
+        
+        box.N = self.region['n']
+        box.S = self.region['s']
+        box.E = self.region['e']
+        box.W = self.region['w']
+        box.T = PORT_DOUBLE_MAX
+        box.B = -PORT_DOUBLE_MAX
+        
+        return box
+
+    def DrawMap(self, force = False):
+        """!Draw content of the vector map to the device
+        
+        @param force force drawing
+        @return number of drawn features
+        @return -1 on error
+        """
+        Debug.msg(1, "DisplayDriver.DrawMap(): force=%d", force)
+        
+        if not self.poMapInfo or not self.dc or not self.dcTmp:
+            return -1
+        
+        rlist = Vedit_render_map(self.poMapInfo, byref(self._getRegionBox()), self._getDrawFlag(),
+                                 self.region['center_easting'], self.region['center_northing'],
+                                 self.mapObj.width, self.mapObj.height,
+                                 max(self.region['nsres'], self.region['ewres'])).contents
+        
+        self._resetTopology()
+        
+        self.dc.BeginDrawing()
+        self.dcTmp.BeginDrawing()
+        
+        # draw objects
+        for i in range(rlist.nitems):
+            robj = rlist.item[i].contents
+            self._drawObject(robj)
+        
+        self.dc.EndDrawing()
+        self.dcTmp.EndDrawing()
+        
+        # reset list of selected features by cat 
+        # list of ids - see IsSelected()
+        self.selected['field'] = -1
+        self.selected['cats'] = list()
+        
+    def _getSelectType(self):
+        """!Get type(s) to be selected
+
+        Used by SelectLinesByBox() and SelectLineByPoint()
+        """
+        ftype = 0
+        for feature in (('point',    GV_POINT),
+                        ('line',     GV_LINE),
+                        ('centroid', GV_CENTROID),
+                        ('boundary', GV_BOUNDARY)):
+            if UserSettings.Get(group = 'vdigit', key = 'selectType',
+                                subkey = [feature[0], 'enabled']):
+                ftype |= feature[1]
+        
+        return ftype
+
+    def _validLine(self, line):
+        """!Check if feature id is valid
+
+        @param line feature id
+
+        @return True valid feature id
+        @return False invalid
+        """
+        if line > 0 and line <= Vect_get_num_lines(self.poMapInfo):
+            return True
+        
+        return False
+    
+    def SelectLinesByBox(self, bbox, drawSeg = False, poMapInfo = None):
+        """!Select vector objects by given bounding box
+        
+        If line id is already in the list of selected lines, then it will
+        be excluded from this list.
+        
+        @param bbox bounding box definition
+        @param drawSeg True to draw segments of line
+        @param poMapInfo use external Map_info, None for self.poMapInfo
+
+        @return number of selected features
+        @return None on error
+        """
+        thisMapInfo = poMapInfo is None
+        if not poMapInfo:
+            poMapInfo = self.poMapInfo
+        
+        if not poMapInfo:
+            return None
+        
+        if thisMapInfo:
+            self._drawSegments = drawSeg
+            self._drawSelected = True
+        
+            # select by ids
+            self.selected['cats'] = list()
+        
+        poList = Vect_new_list()
+        x1, y1 = bbox[0]
+        x2, y2 = bbox[1]
+        poBbox = Vect_new_line_struct()
+        Vect_append_point(poBbox, x1, y1, 0.0)
+        Vect_append_point(poBbox, x2, y1, 0.0)
+        Vect_append_point(poBbox, x2, y2, 0.0)
+        Vect_append_point(poBbox, x1, y2, 0.0)
+        Vect_append_point(poBbox, x1, y1, 0.0)
+        
+        Vect_select_lines_by_polygon(poMapInfo, poBbox,
+                                     0, None, # isles
+                                     self._getSelectType(), poList)
+        
+        flist = poList.contents
+        nlines = flist.n_values
+        Debug.msg(1, "DisplayDriver.SelectLinesByBox() num = %d", nlines)
+        for i in range(nlines):
+            line = flist.value[i]
+            if UserSettings.Get(group = 'vdigit', key = 'selectInside',
+                                subkey = 'enabled'):
+                inside = True
+                if not self._validLine(line):
+                    return None
+                Vect_read_line(poMapInfo, self.poPoints, None, line)
+                points = self.poPoints.contents
+                for p in range(points.n_points):
+                    if not Vect_point_in_poly(points.x[p], points.y[p],
+                                              poBbox):
+                        inside = False
+                        break
+                    
+                if not inside:
+                    continue # skip lines just overlapping bbox
+            
+            if not self._isSelected(line):
+                self.selected['ids'].append(line)
+            else:
+                self.selected['ids'].remove(line)
+        
+        Vect_destroy_line_struct(poBbox)
+        Vect_destroy_list(poList)
+        
+        return nlines
+
+    def SelectLineByPoint(self, point, poMapInfo = None):
+        """!Select vector feature by given point in given
+        threshold
+   
+        Only one vector object can be selected. Bounding boxes of
+        all segments are stores.
+        
+        @param point points coordinates (x, y)
+        @param poMapInfo use external Map_info, None for self.poMapInfo
+
+        @return dict {'line' : feature id, 'point' : point on line}
+        """
+        thisMapInfo = poMapInfo is None
+        if not poMapInfo:
+            poMapInfo = self.poMapInfo
+        
+        if not poMapInfo:
+            return { 'line' : -1, 'point': None }
+        
+        if thisMapInfo:
+            self._drawSelected = True
+            # select by ids 
+            self.selected['cats'] = list()
+        
+        poFound = Vect_new_list()
+        
+        lineNearest = Vect_find_line_list(poMapInfo, point[0], point[1], 0,
+                                           self._getSelectType(), self.GetThreshold(), self.is3D,
+                                           None, poFound)
+        Debug.msg(1, "DisplayDriver.SelectLineByPoint() found = %d", lineNearest)
+        
+        if lineNearest > 0:
+            if not self._isSelected(lineNearest):
+                self.selected['ids'].append(lineNearest)
+            else:
+                self.selected['ids'].remove(lineNearest)
+        
+        px = c_double()
+        py = c_double()
+        pz = c_double()
+        if not self._validLine(lineNearest):
+            return { 'line' : -1, 'point': None }
+	ftype = Vect_read_line(poMapInfo, self.poPoints, self.poCats, lineNearest)
+	Vect_line_distance (self.poPoints, point[0], point[1], 0.0, self.is3D,
+			    byref(px), byref(py), byref(pz),
+			    None, None, None)
+        
+	# check for duplicates 
+	if self.settings['highlightDupl']['enabled']:
+            found = poFound.contents
+	    for i in range(found.n_values):
+		line = found.value[i]
+		if line != lineNearest:
+                    self.selected['ids'].append(line)
+	    
+            self.GetDuplicates()
+	    
+	    for i in range(found.n_values):
+		line = found.value[i]
+		if line != lineNearest and not self._isDuplicated(line):
+                    self.selected['ids'].remove(line)
+        
+        Vect_destroy_list(poFound)
+        
+        if thisMapInfo:
+            # drawing segments can be very expensive
+            # only one features selected
+            self._drawSegments = True
+        
+        return { 'line'  : lineNearest,
+                 'point' : (px.value, py.value, pz.value) }
+    
+    def _listToIList(self, plist):
+        """!Generate from list struct_ilist
+        """
+        ilist = Vect_new_list()
+        for val in plist:
+            Vect_list_append(ilist, val)
+        
+        return ilist
+        
+    def GetSelectedIList(self, ilist = None):
+        """!Get list of selected objects as struct_ilist
+
+        Returned IList must be freed by Vect_destroy_list().
+        
+        @return struct_ilist
+        """
+        if ilist:
+            return self._listToIList(ilist)
+        
+        return self._listToIList(self.selected['ids'])
+        
+    def GetSelected(self, grassId = True):
+        """!Get ids of selected objects
+        
+        @param grassId True for feature id, False for PseudoDC id
+        
+        @return list of ids of selected vector objects
+        """
+        if grassId:
+            return self.selected['ids']
+        
+        dc_ids = list()
+        
+        if not self._drawSegments:
+            dc_ids.append(1)
+        elif len(self.selected['ids']) > 0:
+            # only first selected feature
+            Vect_read_line(self.poMapInfo, self.poPoints, None,
+                           self.selected['ids'][0])
+            points = self.poPoints.contents
+            # node - segment - vertex - segment - node
+            for i in range(1, 2 * points.n_points):
+                dc_ids.append(i)
+        
+        return dc_ids
+        
+    def SetSelected(self, ids, layer = -1):
+        """!Set selected vector objects
+
+        @param list of ids (None to unselect features)
+        @param layer layer number for features selected based on category number
+        """
+        if ids:
+            self._drawSelected = True
+        else:
+            self._drawSelected = False
+
+        self.selected['field'] = layer        
+        if layer > 0:
+            self.selected['cats']  = ids
+            self.selected['ids']   = list()
+            ### cidx is not up-to-date
+            # Vect_cidx_find_all(self.poMapInfo, layer, GV_POINTS | GV_LINES, lid, ilist)
+            nlines = Vect_get_num_lines(self.poMapInfo)
+            for line in range(1, nlines + 1):
+                if not Vect_line_alive(self.poMapInfo, line):
+                    continue
+                
+                ltype = Vect_read_line (self.poMapInfo, None, self.poCats, line)
+                if not (ltype & (GV_POINTS | GV_LINES)):
+                    continue
+                
+                found = False
+                cats = self.poCats.contents
+                for i in range(0, cats.n_cats):
+                    for cat in self.selected['cats']:
+                        if cats.cat[i] == cat:
+                            found = True
+                            break
+                if found:
+                    self.selected['ids'].append(line)
+        else:
+            self.selected['ids']   = ids
+            self.selected['cats']  = []
+        
+    def GetSelectedVertex(self, pos):
+        """!Get PseudoDC vertex id of selected line
+
+        Set bounding box for vertices of line.
+        
+        @param pos position
+        
+        @return id of center, left and right vertex
+        @return 0 no line found
+        @return -1 on error
+        """
+        returnId = list()
+        # only one object can be selected
+        if len(self.selected['ids']) != 1 or not self._drawSegments:
+            return returnId
+        
+        startId = 1
+        line = self.selected['ids'][0]
+        
+        if not self._validLine(line):
+            return -1
+        ftype = Vect_read_line(self.poMapInfo, self.poPoints, self.poCats, line)
+        
+        minDist = 0.0
+        Gid = -1
+        # find the closest vertex (x, y)
+        DCid = 1
+        points = self.poPoints.contents
+        for idx in range(points.n_points):
+            dist = Vect_points_distance(pos[0], pos[1], 0.0,
+                                        points.x[idx], points.y[idx], points.z[idx], 0)
+            
+            if idx == 0:
+                minDist = dist
+                Gid     = idx
+            else:
+                if minDist > dist:
+                    minDist = dist
+                    Gid = idx
+            
+            vx, vy = self._cell2Pixel(points.x[idx], points.y[idx], points.z[idx])
+            rect = wx.Rect(vx, vy, 0, 0)
+            self.dc.SetIdBounds(DCid, rect)
+            DCid += 2
+        
+        if minDist > self.GetThreshold():
+            return returnId
+        
+        # translate id
+        DCid = Gid * 2 + 1
+        
+        # add selected vertex
+        returnId.append(DCid)
+        # left vertex
+        if DCid == startId:
+            returnId.append(-1)
+        else:
+            returnId.append(DCid - 2)
+        # right vertex
+        if DCid == (points.n_points - 1) * 2 + startId:
+            returnId.append(-1)
+        else:
+            returnId.append(DCid + 2)
+        
+        return returnId
+
+    def GetRegionSelected(self):
+        """!Get minimal region extent of selected features
+	
+        @return n,s,w,e
+        """
+        regionBox = bound_box()
+        lineBox = bound_box()
+        setRegion = True
+        
+        nareas = Vect_get_num_areas(self.poMapInfo)
+        for line in self.selected['ids']:
+            area = Vect_get_centroid_area(self.poMapInfo, line)
+            
+            if area > 0 and area <= nareas:
+                if not Vect_get_area_box(self.poMapInfo, area, byref(lineBox)):
+                    continue
+            else:
+                if not Vect_get_line_box(self.poMapInfo, line, byref(lineBox)):
+                    continue
+                
+            if setRegion:
+                Vect_box_copy(byref(regionBox), byref(lineBox))
+                setRegion = False
+            else:
+                Vect_box_extend(byref(regionBox), byref(lineBox))
+        
+        return regionBox.N, regionBox.S, regionBox.W, regionBox.E
+
+    def DrawSelected(self, flag):
+        """!Draw selected features
+        
+        @param flag True to draw selected features
+        """
+        self._drawSelected = bool(flag)
+        
+    def CloseMap(self):
+        """!Close vector map
+        
+        @return 0 on success
+        @return non-zero on error
+        """
+        ret = 0
+        if self.poMapInfo:
+            # rebuild topology
+            Vect_build_partial(self.poMapInfo, GV_BUILD_NONE)
+            Vect_build(self.poMapInfo)
+
+            # close map and store topo/cidx
+            ret = Vect_close(self.poMapInfo)
+            del self.mapInfo
+            self.poMapInfo = self.mapInfo = None
+        
+        return ret
+    
+    def OpenMap(self, name, mapset, update = True):
+        """!Open vector map by the driver
+        
+        @param name name of vector map to be open
+        @param mapset name of mapset where the vector map lives
+   
+        @return map_info
+        @return None on error
+        """
+        Debug.msg("DisplayDriver.OpenMap(): name=%s mapset=%s updated=%d",
+                  name, mapset, update)
+        if not self.mapInfo:
+            self.mapInfo = Map_info()
+            self.poMapInfo = pointer(self.mapInfo)
+        
+        # open existing map
+        if update:
+            ret = Vect_open_update(self.poMapInfo, name, mapset)
+        else:
+            ret = Vect_open_old(self.poMapInfo, name, mapset)
+        self.is3D = Vect_is_3d(self.poMapInfo)
+        
+        if ret == -1: # error
+            del self.mapInfo
+            self.poMapInfo = self.mapInfo = None
+        elif ret < 2:
+            dlg = wx.MessageDialog(parent = self.window,
+                                   message = _("Topology for vector map <%s> is not available. "
+                                               "Topology is required by digitizer. Do you want to "
+                                               "rebuild topology (takes some time) and open the vector map "
+                                               "for editing?") % name,
+                                   caption=_("Topology missing"),
+                                   style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)
+            ret = dlg.ShowModal()
+            if ret != wx.ID_YES:
+                del self.mapInfo
+                self.poMapInfo = self.mapInfo = None
+            else:
+                Vect_build(self.poMapInfo)
+        
+        return self.poMapInfo
+    
+    def GetMapBoundingBox(self):
+        """!Get bounding box of (opened) vector map layer
+
+        @return (w,s,b,e,n,t)
+        """
+        if not self.poMapInfo:
+            return None
+        
+        bbox = bound_box()
+        Vect_get_map_box(self.poMapInfo, byref(bbox))
+
+        return bbox.W, bbox.S, bbox.B, \
+            bbox.E, bbox.N, bbox.T
+    
+    def UpdateSettings(self, alpha = 255):
+        """!Update display driver settings
+
+        @todo map units
+        
+        @alpha color value for aplha channel
+        """
+        color = dict()
+        for key in self.settings.keys():
+            if key == 'lineWidth':
+                self.settings[key] = int(UserSettings.Get(group = 'vdigit', key = 'lineWidth',
+                                                          subkey = 'value'))
+                continue
+            
+            color = wx.Color(UserSettings.Get(group = 'vdigit', key = 'symbol',
+                                              subkey = [key, 'color'])[0],
+                             UserSettings.Get(group = 'vdigit', key = 'symbol',
+                                              subkey = [key, 'color'])[1],
+                             UserSettings.Get(group = 'vdigit', key = 'symbol',
+                                              subkey = [key, 'color'])[2],
+                             alpha)
+            
+            if key == 'highlight':
+                self.settings[key] = color
+                continue
+            
+            if key == 'highlightDupl':
+                self.settings[key]['enabled'] = bool(UserSettings.Get(group = 'vdigit', key = 'checkForDupl',
+                                                                      subkey = 'enabled'))
+            else:
+                self.settings[key]['enabled'] = bool(UserSettings.Get(group = 'vdigit', key = 'symbol',
+                                                                      subkey = [key, 'enabled']))
+            
+            self.settings[key]['color'] = color
+        
+    def UpdateRegion(self):
+        """!Update geographical region used by display driver
+        """
+        self.region = self.mapObj.GetCurrentRegion()
+        
+    def GetThreshold(self, type = 'snapping', value = None, units = None):
+        """!Return threshold value in map units
+        
+        @param type snapping mode (node, vertex)
+        @param value threshold to be set up
+        @param units units (map, screen)
+
+        @return threshold value
+        """
+        if value is None:
+            value = UserSettings.Get(group = 'vdigit', key = type, subkey = 'value')
+        
+        if units is None:
+            units = UserSettings.Get(group = 'vdigit', key = type, subkey = 'units')
+        
+        if value < 0:
+            value = (self.region['nsres'] + self.region['ewres']) / 2.0
+        
+        if units == "screen pixels":
+            # pixel -> cell
+            res = max(self.region['nsres'], self.region['ewres'])
+            return value * res
+        
+        return value
+    
+    def GetDuplicates(self):
+        """!Return ids of (selected) duplicated vector features
+        """
+        if not self.poMapInfo:
+            return
+        
+        ids = dict()
+        APoints = Vect_new_line_struct()
+        BPoints = Vect_new_line_struct()
+        
+        self.selected['idsDupl'] = list()
+        
+        for i in range(len(self.selected['ids'])):
+            line1 = self.selected['ids'][i]
+            if self._isDuplicated(line1):
+                continue
+            
+            Vect_read_line(self.poMapInfo, APoints, None, line1)
+            
+            for line2 in self.selected['ids']:
+                if line1 == line2 or self._isDuplicated(line2):
+                    continue
+                
+                Vect_read_line(self.poMapInfo, BPoints, None, line2)
+	    
+                if Vect_line_check_duplicate(APoints, BPoints, WITHOUT_Z):
+                    if i not in ids:
+                        ids[i] = list()
+                        ids[i].append((line1, self._getCatString(line1)))
+                        self.selected['idsDupl'].append(line1)
+                    
+                    ids[i].append((line2, self._getCatString(line2)))
+                    self.selected['idsDupl'].append(line2)
+        
+        Vect_destroy_line_struct(APoints)
+        Vect_destroy_line_struct(BPoints)
+
+        return ids
+    
+    def _getCatString(self, line):
+        Vect_read_line(self.poMapInfo, None, self.poCats, line)
+        
+        cats = self.poCats.contents
+        catsDict = dict()
+        for i in range(cats.n_cats):
+            layer = cats.field[i]
+            if layer not in catsDict:
+                catsDict[layer] = list()
+            catsDict[layer].append(cats.cat[i])
+        
+        catsStr = ''
+        for l, c in catsDict.iteritems():
+            catsStr = '%d: (%s)' % (l, ','.join(map(str, c)))
+        
+        return catsStr
+
+    def UnSelect(self, lines):
+        """!Unselect vector features
+
+        @param lines list of feature id(s)
+        """
+        checkForDupl = False
+
+        for line in lines:
+            if self._isSelected(line):
+                self.selected['ids'].remove(line)
+            if self.settings['highlightDupl']['enabled'] and self._isDuplicated(line):
+                checkForDupl = True
+
+        if checkForDupl:
+            self.GetDuplicates()
+        
+        return len(self.selected['ids'])


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

Modified: grass/branches/releasebranch_6_4/gui/wxpython/wxgui.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/wxgui.py	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/wxgui.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -1,1598 +1,38 @@
 """!
- at package wxgui.py
+ at package wxgui
 
- at brief Main Python app for GRASS wxPython GUI. Main menu, layer management
-toolbar, notebook control for display management and access to
-command console.
+ at brief Main Python application for GRASS wxPython GUI
 
 Classes:
- - GMFrame
- - GMApp
+ - wxgui::GMApp
+ - wxgui::Usage
 
 (C) 2006-2011 by the GRASS Development Team
-This program is free software under the GNU General Public
-License (>=v2). Read the file COPYING that comes with GRASS
-for details.
 
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
 @author Michael Barton (Arizona State University)
 @author Jachym Cepicky (Mendel University of Agriculture)
 @author Martin Landa <landa.martin gmail.com>
 @author Vaclav Petras <wenzeslaus gmail.com> (menu customization)
 """
 
-import sys
 import os
-import time
-import string
+import sys
 import getopt
-import platform
-import signal
-import tempfile
 
-### XML
-try:
-    import xml.etree.ElementTree as etree
-except ImportError:
-    import elementtree.ElementTree as etree # Python <= 2.4
-
-### i18N
-import gettext
-gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
-
-from gui_modules import globalvar
+if __name__ == "__main__":
+    sys.path.append(os.path.join(os.getenv('GISBASE'), 'etc', 'wxpython'))
+from core import globalvar
 import wx
-import wx.aui
-import wx.combo
-import wx.html
-import wx.stc
 try:
-    import wx.lib.agw.customtreectrl as CT
-    import wx.lib.agw.flatnotebook   as FN
-except ImportError:
-    import wx.lib.customtreectrl as CT
-    import wx.lib.flatnotebook   as FN
-
-try:
     import wx.lib.agw.advancedsplash as SC
 except ImportError:
     SC = None
 
-sys.path.append(os.path.join(globalvar.ETCDIR, "python"))
-from grass.script import core as grass
+from lmgr.frame import GMFrame
 
-from gui_modules import utils
-from gui_modules import preferences
-from gui_modules import layertree
-from gui_modules import mapdisp
-from gui_modules import menudata
-from gui_modules import menuform
-from gui_modules import histogram
-from gui_modules import profile
-from gui_modules import mcalc_builder as mapcalculator
-from gui_modules import gcmd
-from gui_modules import dbm
-from gui_modules import workspace
-from gui_modules import goutput
-from gui_modules import gdialogs
-from gui_modules import colorrules
-from gui_modules import ogc_services
-from gui_modules import prompt
-from gui_modules import menu
-from gui_modules import gmodeler
-from gui_modules import vclean
-from gui_modules import nviz_tools
-from gui_modules.debug    import Debug
-from gui_modules.ghelp    import MenuTreeWindow, AboutWindow, InstallExtensionWindow, UninstallExtensionWindow
-from gui_modules.toolbars import LMWorkspaceToolbar, LMDataToolbar, LMToolsToolbar, LMMiscToolbar, LMVectorToolbar
-from gui_modules.gpyshell import PyShellWindow
-from icons.icon           import Icons
-
-UserSettings = preferences.globalSettings
-
-class GMFrame(wx.Frame):
-    """!Layer Manager frame with notebook widget for controlling GRASS
-    GIS. Includes command console page for typing GRASS (and other)
-    commands, tree widget page for managing map layers.
-    """
-    def __init__(self, parent, id = wx.ID_ANY, title = _("GRASS GIS Layer Manager"),
-                 workspace = None,
-                 size = globalvar.GM_WINDOW_SIZE, style = wx.DEFAULT_FRAME_STYLE, **kwargs):
-        self.parent    = parent
-        self.baseTitle = title
-        self.iconsize  = (16, 16)
-        
-        wx.Frame.__init__(self, parent = parent, id = id, size = size,
-                          style = style, **kwargs)
-                          
-        self.SetTitle(self.baseTitle)
-        self.SetName("LayerManager")
-
-        self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
-
-        self._auimgr = wx.aui.AuiManager(self)
-
-        # initialize variables
-        self.disp_idx      = 0            # index value for map displays and layer trees
-        self.curr_page     = None         # currently selected page for layer tree notebook
-        self.curr_pagenum  = None         # currently selected page number for layer tree notebook
-        self.workspaceFile = workspace    # workspace file
-        self.workspaceChanged = False     # track changes in workspace
-        self.georectifying = None         # reference to GCP class or None
-        self.gcpmanagement = None         # reference to GCP class or None
-        # list of open dialogs
-        self.dialogs        = dict()
-        self.dialogs['preferences'] = None
-        self.dialogs['atm'] = list()
-        
-        # creating widgets
-        self._createMenuBar()
-        self.statusbar = self.CreateStatusBar(number = 1)
-        self.notebook  = self._createNoteBook()
-        self.toolbars  = { 'workspace' : LMWorkspaceToolbar(parent = self),
-                           'data'      : LMDataToolbar(parent = self),
-                           'tools'     : LMToolsToolbar(parent = self),
-                           'misc'      : LMMiscToolbar(parent = self),
-                           'vector'    : LMVectorToolbar(parent = self) }
-        
-        self._toolbarsData = { 'workspace' : ("toolbarWorkspace",     # name
-                                              _("Workspace Toolbar"), # caption
-                                              1),                     # row
-                               'data'      : ("toolbarData",
-                                              _("Data Toolbar"),
-                                              1),
-                               'misc'      : ("toolbarMisc",
-                                              _("Misc Toolbar"),
-                                              2),
-                               'tools'     : ("toolbarTools",
-                                              _("Tools Toolbar"),
-                                              2),
-                               'vector'    : ("toolbarVector",
-                                              _("Vector Toolbar"),
-                                              2),
-                               }
-        if sys.platform == 'win32':
-            self._toolbarsList = ('workspace', 'data',
-                                  'vector', 'tools', 'misc')
-        else:
-            self._toolbarsList = ('data', 'workspace',
-                                  'misc', 'tools', 'vector')
-        for toolbar in self._toolbarsList:
-            name, caption, row = self._toolbarsData[toolbar]
-            self._auimgr.AddPane(self.toolbars[toolbar],
-                                 wx.aui.AuiPaneInfo().
-                                 Name(name).Caption(caption).
-                                 ToolbarPane().Top().Row(row).
-                                 LeftDockable(False).RightDockable(False).
-                                 BottomDockable(False).TopDockable(True).
-                                 CloseButton(False).Layer(2).
-                                 BestSize((self.toolbars[toolbar].GetBestSize())))
-        
-        # bindings
-        self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
-        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
-
-        # minimal frame size
-        self.SetMinSize((500, 400))
-
-        # AUI stuff
-        self._auimgr.AddPane(self.notebook, wx.aui.AuiPaneInfo().
-                             Left().CentrePane().BestSize((-1,-1)).Dockable(False).
-                             CloseButton(False).DestroyOnClose(True).Row(1).Layer(0))
-
-        self._auimgr.Update()
-
-        wx.CallAfter(self.notebook.SetSelectionByName, 'layers')
-        
-        # use default window layout ?
-        if UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'enabled'):
-            dim = UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'dim')
-            try:
-               x, y = map(int, dim.split(',')[0:2])
-               w, h = map(int, dim.split(',')[2:4])
-               self.SetPosition((x, y))
-               self.SetSize((w, h))
-            except:
-                pass
-        else:
-            self.Centre()
-        
-        self.Layout()
-        self.Show()
-        
-        # load workspace file if requested
-        if self.workspaceFile:
-            # load given workspace file
-            if self.LoadWorkspaceFile(self.workspaceFile):
-                self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.workspaceFile))
-            else:
-                self.workspaceFile = None
-        else:
-            # start default initial display
-            self.NewDisplay(show = False)
-
-        # show map display widnow
-        # -> OnSize() -> UpdateMap()
-        if self.curr_page and not self.curr_page.maptree.mapdisplay.IsShown():
-            self.curr_page.maptree.mapdisplay.Show()
-        
-        # redirect stderr to log area    
-        self.goutput.Redirect()
-        # fix goutput's pane size
-        self.goutput.SetSashPosition(int(self.GetSize()[1] * .60))
-
-        self.workspaceChanged = False
-        
-        # start with layer manager on top
-        if self.curr_page:
-            self.curr_page.maptree.mapdisplay.Raise()
-        wx.CallAfter(self.Raise)
-        
-    def _createMenuBar(self):
-        """!Creates menu bar"""
-        self.menubar = menu.Menu(parent = self, data = menudata.ManagerData())
-        self.SetMenuBar(self.menubar)
-        self.menucmd = self.menubar.GetCmd()
-        
-    def _setCopyingOfSelectedText(self):
-        copy = UserSettings.Get(group = 'manager', key = 'copySelectedTextToClipboard', subkey = 'enabled')
-        self.goutput.SetCopyingOfSelectedText(copy)
-        
-    def _createNoteBook(self):
-        """!Creates notebook widgets"""
-        self.notebook = menuform.GNotebook(parent = self, style = globalvar.FNPageDStyle)
-        # create displays notebook widget and add it to main notebook page
-        cbStyle = globalvar.FNPageStyle
-        if globalvar.hasAgw:
-            self.gm_cb = FN.FlatNotebook(self, id = wx.ID_ANY, agwStyle = cbStyle)
-        else:
-            self.gm_cb = FN.FlatNotebook(self, id = wx.ID_ANY, style = cbStyle)
-        self.gm_cb.SetTabAreaColour(globalvar.FNPageColor)
-        self.notebook.AddPage(page = self.gm_cb, text = _("Map layers"), name = 'layers')
-        
-        # create 'command output' text area
-        self.goutput = goutput.GMConsole(self)
-        self.notebook.AddPage(page = self.goutput, text = _("Command console"), name = 'output')
-        self._setCopyingOfSelectedText()
-        
-        # create 'search module' notebook page
-        if not UserSettings.Get(group = 'manager', key = 'hideTabs', subkey = 'search'):
-            self.search = MenuTreeWindow(parent = self)
-            self.notebook.AddPage(page = self.search, text = _("Search module"), name = 'search')
-        else:
-            self.search = None
-        
-        # create 'python shell' notebook page
-        if not UserSettings.Get(group = 'manager', key = 'hideTabs', subkey = 'pyshell'):
-            self.pyshell = PyShellWindow(parent = self)
-            self.notebook.AddPage(page = self.pyshell, text = _("Python shell"), name = 'pyshell')
-        else:
-            self.pyshell = None
-        
-        # bindings
-        self.gm_cb.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED,    self.OnCBPageChanged)
-        self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
-        self.gm_cb.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CLOSING,    self.OnCBPageClosed)
-        
-        return self.notebook
-            
-    def AddNviz(self):
-        """!Add nviz notebook page"""
-        self.nviz = nviz_tools.NvizToolWindow(parent = self,
-                                              display = self.curr_page.maptree.GetMapDisplay()) 
-        self.notebook.AddPage(page = self.nviz, text = _("3D view"), name = 'nviz')
-        self.notebook.SetSelectionByName('nviz')
-        
-    def RemoveNviz(self):
-        """!Remove nviz notebook page"""
-        # print self.notebook.GetPage(1)
-        self.notebook.RemovePage(self.notebook.GetPageIndexByName('nviz'))
-        del self.nviz
-        self.notebook.SetSelectionByName('layers')
-        
-    def WorkspaceChanged(self):
-        """!Update window title"""
-        if not self.workspaceChanged:
-            self.workspaceChanged = True
-        
-        if self.workspaceFile:
-            self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.workspaceFile) + '*')
-        
-    def OnSettingsChanged(self, event):
-        """!Here can be functions which have to be called after EVT_SETTINGS_CHANGED. 
-        Now only set copying of selected text to clipboard (in goutput).
-        """
-        ### self._createMenuBar() # bug when menu is re-created on the fly
-        self._setCopyingOfSelectedText()
-        
-    def OnGCPManager(self, event):
-        """!Launch georectifier module
-        """
-        from gui_modules import gcpmanager
-        gcpmanager.GCPWizard(self)
-
-    def OnGModeler(self, event):
-        """!Launch Graphical Modeler"""
-        win = gmodeler.ModelFrame(parent = self)
-        win.CentreOnScreen()
-        
-        win.Show()
-        
-    def OnPsMap(self, event):
-        """!Launch Cartographic Composer
-        """
-        try:
-            from gui_modules import psmap
-        except:
-            gcmd.GError(parent = self.parent,
-                        message = _("Hardcopy Map Output Utility is not available. You can install it by %s") % \
-                            'g.extension.py -s svnurl=https://svn.osgeo.org/grass/grass-addons extension=wx.psmap')
-            return
-        
-        win = psmap.PsMapFrame(parent = self)
-        win.CentreOnScreen()
-        
-        win.Show()
-        
-    def OnDone(self, cmd, returncode):
-        """Command execution finised"""
-        if hasattr(self, "model"):
-            self.model.DeleteIntermediateData(log = self.goutput)
-            del self.model
-        self.SetStatusText('')
-        
-    def OnRunModel(self, event):
-        """!Run model"""
-        filename = ''
-        dlg = wx.FileDialog(parent = self, message =_("Choose model to run"),
-                            defaultDir = os.getcwd(),
-                            wildcard = _("GRASS Model File (*.gxm)|*.gxm"))
-        if dlg.ShowModal() == wx.ID_OK:
-            filename = dlg.GetPath()
-        
-        if not filename:
-            dlg.Destroy()
-            return
-        
-        self.model = gmodeler.Model()
-        self.model.LoadModel(filename)
-        self.model.Run(log = self.goutput, onDone = self.OnDone, parent = self)
-        
-        dlg.Destroy()
-        
-    def OnMapsets(self, event):
-        """!Launch mapset access dialog
-        """
-        dlg = preferences.MapsetAccess(parent = self, id = wx.ID_ANY)
-        dlg.CenterOnScreen()
-        
-        if dlg.ShowModal() == wx.ID_OK:
-            ms = dlg.GetMapsets()
-            gcmd.RunCommand('g.mapsets',
-                            parent = self,
-                            mapset = '%s' % ','.join(ms))
-        
-    def OnCBPageChanged(self, event):
-        """!Page in notebook (display) changed"""
-        old_pgnum = event.GetOldSelection()
-        new_pgnum = event.GetSelection()
-        
-        self.curr_page   = self.gm_cb.GetCurrentPage()
-        self.curr_pagenum = self.gm_cb.GetSelection()
-        try:
-            self.curr_page.maptree.mapdisplay.SetFocus()
-            self.curr_page.maptree.mapdisplay.Raise()
-        except:
-            pass
-        
-        event.Skip()
-
-    def OnPageChanged(self, event):
-        """!Page in notebook changed"""
-        page = event.GetSelection()
-        if page == self.notebook.GetPageIndexByName('output'):
-            # remove '(...)'
-            self.notebook.SetPageText(page, _("Command console"))
-            wx.CallAfter(self.goutput.cmd_prompt.SetFocus)
-        self.SetStatusText('', 0)
-        
-        event.Skip()
-
-    def OnCBPageClosed(self, event):
-        """!Page of notebook closed
-        Also close associated map display
-        """
-        if UserSettings.Get(group = 'manager', key = 'askOnQuit', subkey = 'enabled'):
-            maptree = self.curr_page.maptree
-            
-            if self.workspaceFile:
-                message = _("Do you want to save changes in the workspace?")
-            else:
-                message = _("Do you want to store current settings "
-                            "to workspace file?")
-            
-            # ask user to save current settings
-            if maptree.GetCount() > 0:
-                dlg = wx.MessageDialog(self,
-                                       message = message,
-                                       caption = _("Close Map Display %d") % (self.curr_pagenum + 1),
-                                       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.workspaceFile:
-                        self.OnWorkspaceSaveAs()
-                    else:
-                        self.SaveToWorkspaceFile(self.workspaceFile)
-                elif ret == wx.ID_CANCEL:
-                    event.Veto()
-                    dlg.Destroy()
-                    return
-                dlg.Destroy()
-
-        self.gm_cb.GetPage(event.GetSelection()).maptree.Map.Clean()
-        self.gm_cb.GetPage(event.GetSelection()).maptree.Close(True)
-
-        self.curr_page = None
-
-        event.Skip()
-
-    def GetLayerTree(self):
-        """!Get current layer tree"""
-        return self.curr_page.maptree
-    
-    def GetLogWindow(self):
-        """!Get widget for command output"""
-        return self.goutput
-    
-    def GetMenuCmd(self, event):
-        """!Get GRASS command from menu item
-
-        Return command as a list"""
-        layer = None
-        
-        if event:
-            cmd = self.menucmd[event.GetId()]
-        
-        try:
-            cmdlist = cmd.split(' ')
-        except: # already list?
-            cmdlist = cmd
-        
-        # check list of dummy commands for GUI modules that do not have GRASS
-        # bin modules or scripts. 
-        if cmd in ['vcolors', 'r.mapcalc', 'r3.mapcalc']:
-            return cmdlist
-
-        try:
-            layer = self.curr_page.maptree.layer_selected
-            name = self.curr_page.maptree.GetPyData(layer)[0]['maplayer'].name
-            type = self.curr_page.maptree.GetPyData(layer)[0]['type']
-        except:
-            layer = None
-
-        if layer and len(cmdlist) == 1: # only if no paramaters given
-            if (type == 'raster' and cmdlist[0][0] == 'r' and cmdlist[0][1] != '3') or \
-                    (type == 'vector' and cmdlist[0][0] == 'v'):
-                input = menuform.GUI().GetCommandInputMapParamKey(cmdlist[0])
-                if input:
-                    cmdlist.append("%s=%s" % (input, name))
-        
-        return cmdlist
-
-    def RunMenuCmd(self, event = None, cmd = []):
-        """!Run command selected from menu"""
-        if event:
-            cmd = self.GetMenuCmd(event)
-        self.goutput.RunCmd(cmd, switchPage = False)
-
-    def OnMenuCmd(self, event = None, cmd = []):
-        """!Parse command selected from menu"""
-        if event:
-            cmd = self.GetMenuCmd(event)
-        menuform.GUI(parent = self).ParseCommand(cmd)
-        
-    def OnVDigit(self, event):
-        """!Start vector digitizer
-        """
-        if not self.curr_page:
-            self.MsgNoLayerSelected()
-            return
-        
-        tree = self.GetLayerTree()
-        layer = tree.layer_selected
-        # no map layer selected
-        if not layer:
-            self.MsgNoLayerSelected()
-            return
-        
-        # available only for vector map layers
-        try:
-            mapLayer = tree.GetPyData(layer)[0]['maplayer']
-        except:
-            mapLayer = None
-        
-        if not mapLayer or mapLayer.GetType() != 'vector':
-            gcmd.GMessage(parent = self,
-                          message = _("Selected map layer is not vector."))
-            return
-        
-        if mapLayer.GetMapset() != grass.gisenv()['MAPSET']:
-            gcmd.GMessage(parent = self,
-                          message = _("Editing is allowed only for vector maps from the "
-                                      "current mapset."))
-            return
-        
-        if not tree.GetPyData(layer)[0]:
-            return
-        dcmd = tree.GetPyData(layer)[0]['cmd']
-        if not dcmd:
-            return
-        
-        tree.OnStartEditing(None)
-        
-    def OnRunScript(self, event):
-        """!Run script"""
-        # open dialog and choose script file
-        dlg = wx.FileDialog(parent = self, message = _("Choose script file to run"),
-                            defaultDir = os.getcwd(),
-                            wildcard = _("Python script (*.py)|*.py|Bash script (*.sh)|*.sh"))
-        
-        filename = None
-        if dlg.ShowModal() == wx.ID_OK:
-            filename = dlg.GetPath()
-        
-        if not filename:
-            return False
-
-        if not os.path.exists(filename):
-            gcmd.GError(parent = self,
-                        message = _("Script file '%s' doesn't exist. "
-                                    "Operation cancelled.") % filename)
-            return
-        
-        self.goutput.WriteCmdLog(_("Launching script '%s'...") % filename)
-        self.goutput.RunCmd([filename], switchPage = True)
-        
-    def OnChangeLocation(self, event):
-        """Change current location"""
-        dlg = gdialogs.LocationDialog(parent = self)
-        if dlg.ShowModal() == wx.ID_OK:
-            location, mapset = dlg.GetValues()
-            if location and mapset:
-                ret = gcmd.RunCommand("g.gisenv",
-                                      set = "LOCATION_NAME=%s" % location)
-                ret += gcmd.RunCommand("g.gisenv",
-                                       set = "MAPSET=%s" % mapset)
-                if ret > 0:
-                    wx.MessageBox(parent = self,
-                                  message = _("Unable to switch to location <%(loc)s> mapset <%(mapset)s>.") % \
-                                      { 'loc' : location, 'mapset' : mapset },
-                                  caption = _("Error"), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
-                else:
-                    # close workspace
-                    self.OnWorkspaceClose()
-                    self.OnWorkspaceNew()
-                    wx.MessageBox(parent = self,
-                                  message = _("Current location is <%(loc)s>.\n"
-                                              "Current mapset is <%(mapset)s>.") % \
-                                      { 'loc' : location, 'mapset' : mapset },
-                                  caption = _("Info"), style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
-                    
-    def OnChangeMapset(self, event):
-        """Change current mapset"""
-        dlg = gdialogs.MapsetDialog(parent = self)
-        if dlg.ShowModal() == wx.ID_OK:
-            mapset = dlg.GetMapset()
-            if mapset:
-                if gcmd.RunCommand("g.gisenv",
-                                   set = "MAPSET=%s" % mapset) != 0:
-                    wx.MessageBox(parent = self,
-                                  message = _("Unable to switch to mapset <%s>.") % mapset,
-                                  caption = _("Error"), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
-                else:
-                    wx.MessageBox(parent = self,
-                                  message = _("Current mapset is <%s>.") % mapset,
-                                  caption = _("Info"), style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
-        
-    def OnNewVector(self, event):
-        """!Create new vector map layer"""
-        dlg = gdialogs.CreateNewVector(self, log = self.goutput,
-                                       cmd = (('v.edit',
-                                               { 'tool' : 'create' },
-                                               'map')))
-        
-        if not dlg:
-            return
-        
-        name = dlg.GetName(full = True)
-        if name and dlg.IsChecked('add'):
-            # add layer to map layer tree
-            self.curr_page.maptree.AddLayer(ltype = 'vector',
-                                            lname = name,
-                                            lcmd = ['d.vect', 'map=%s' % name])
-        
-        # create table ?
-        if dlg.IsChecked('table'):
-            self.OnShowAttributeTable(None, selection = 1)
-        
-        dlg.Destroy()
-        
-    def OnAboutGRASS(self, event):
-        """!Display 'About GRASS' dialog"""
-        win = AboutWindow(self)
-        win.CentreOnScreen()
-        win.Show(True)  
-        
-    def _popupMenu(self, data):
-        """!Create popup menu
-        """
-        point = wx.GetMousePosition()
-        menu = wx.Menu()
-        
-        for key, handler in data:
-            if key is None:
-                menu.AppendSeparator()
-                continue
-            item = wx.MenuItem(menu, wx.ID_ANY, Icons['layerManager'][key].GetLabel())
-            item.SetBitmap(Icons['layerManager'][key].GetBitmap(self.iconsize))
-            menu.AppendItem(item)
-            self.Bind(wx.EVT_MENU, handler, item)
-        
-        # create menu
-        self.PopupMenu(menu)
-        menu.Destroy()
-
-    def OnImportMenu(self, event):
-        """!Import maps menu (import, link)
-        """
-        self._popupMenu((('rastImport',    self.OnImportGdalLayers),
-                         ('vectImport',    self.OnImportOgrLayers)))
-        
-    def OnWorkspaceNew(self, event = None):
-        """!Create new workspace file
-
-        Erase current workspace settings first
-        """
-        Debug.msg(4, "GMFrame.OnWorkspaceNew():")
-        
-        # start new map display if no display is available
-        if not self.curr_page:
-            self.NewDisplay()
-        
-        maptree = self.curr_page.maptree
-        
-        # ask user to save current settings
-        if self.workspaceFile and self.workspaceChanged:
-            self.OnWorkspaceSave()
-        elif self.workspaceFile is None and maptree.GetCount() > 0:
-             dlg = wx.MessageDialog(self, message = _("Current workspace is not empty. "
-                                                    "Do you want to store current settings "
-                                                    "to workspace file?"),
-                                    caption = _("Create new workspace?"),
-                                    style = wx.YES_NO | wx.YES_DEFAULT | \
-                                        wx.CANCEL | wx.ICON_QUESTION)
-             ret = dlg.ShowModal()
-             if ret == wx.ID_YES:
-                 self.OnWorkspaceSaveAs()
-             elif ret == wx.ID_CANCEL:
-                 dlg.Destroy()
-                 return
-             
-             dlg.Destroy()
-        
-        # delete all items
-        maptree.DeleteAllItems()
-        
-        # add new root element
-        maptree.root = maptree.AddRoot("Map Layers")
-        self.curr_page.maptree.SetPyData(maptree.root, (None,None))
-        
-        # no workspace file loaded
-        self.workspaceFile = None
-        self.workspaceChanged = False
-        self.SetTitle(self.baseTitle)
-        
-    def OnWorkspaceOpen(self, event = None):
-        """!Open file with workspace definition"""
-        dlg = wx.FileDialog(parent = self, message = _("Choose workspace file"),
-                            defaultDir = os.getcwd(), wildcard = _("GRASS Workspace File (*.gxw)|*.gxw"))
-
-        filename = ''
-        if dlg.ShowModal() == wx.ID_OK:
-            filename = dlg.GetPath()
-
-        if filename == '':
-            return
-
-        Debug.msg(4, "GMFrame.OnWorkspaceOpen(): filename=%s" % filename)
-
-        # delete current layer tree content
-        self.OnWorkspaceClose()
-        
-        self.LoadWorkspaceFile(filename)
-
-        self.workspaceFile = filename
-        self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.workspaceFile))
-
-    def LoadWorkspaceFile(self, filename):
-        """!Load layer tree definition stored in GRASS Workspace XML file (gxw)
-
-        @todo Validate against DTD
-        
-        @return True on success
-        @return False on error
-        """
-        # dtd
-        dtdFilename = os.path.join(globalvar.ETCWXDIR, "xml", "grass-gxw.dtd")
-        
-        # parse workspace file
-        try:
-            gxwXml = workspace.ProcessWorkspaceFile(etree.parse(filename))
-        except Exception, e:
-            gcmd.GError(parent = self,
-                        message = _("Reading workspace file <%s> failed.\n"
-                                    "Invalid file, unable to parse XML document.") % filename)
-            return
-        
-        busy = wx.BusyInfo(message = _("Please wait, loading workspace..."),
-                           parent = self)
-        wx.Yield()
-
-        #
-        # load layer manager window properties
-        #
-        if UserSettings.Get(group = 'workspace', key = 'posManager', subkey = 'enabled') is False:
-            if gxwXml.layerManager['pos']:
-                self.SetPosition(gxwXml.layerManager['pos'])
-            if gxwXml.layerManager['size']:
-                self.SetSize(gxwXml.layerManager['size'])
-        
-        #
-        # start map displays first (list of layers can be empty)
-        #
-        displayId = 0
-        mapdisplay = list()
-        for display in gxwXml.displays:
-            mapdisp = self.NewDisplay(show = False)
-            mapdisplay.append(mapdisp)
-            maptree = self.gm_cb.GetPage(displayId).maptree
-            
-            # set windows properties
-            mapdisp.SetProperties(render = display['render'],
-                                  mode = display['mode'],
-                                  showCompExtent = display['showCompExtent'],
-                                  constrainRes = display['constrainRes'],
-                                  projection = display['projection']['enabled'])
-
-            if display['projection']['enabled']:
-                if display['projection']['epsg']:
-                    UserSettings.Set(group = 'display', key = 'projection', subkey = 'epsg',
-                                     value = display['projection']['epsg'])
-                    if display['projection']['proj']:
-                        UserSettings.Set(group = 'display', key = 'projection', subkey = 'proj4',
-                                         value = display['projection']['proj'])
-            
-            # set position and size of map display
-            if UserSettings.Get(group = 'workspace', key = 'posDisplay', subkey = 'enabled') is False:
-                if display['pos']:
-                    mapdisp.SetPosition(display['pos'])
-                if display['size']:
-                    mapdisp.SetSize(display['size'])
-                    
-            # set extent if defined
-            if display['extent']:
-                w, s, e, n = display['extent']
-                region = maptree.Map.region = maptree.Map.GetRegion(w = w, s = s, e = e, n = n)
-                mapdisp.GetWindow().ResetZoomHistory()
-                mapdisp.GetWindow().ZoomHistory(region['n'],
-                                                region['s'],
-                                                region['e'],
-                                                region['w'])
-                
-            mapdisp.Show()
-            
-            displayId += 1
-    
-        maptree = None 
-        selected = [] # list of selected layers
-        # 
-        # load list of map layers
-        #
-        for layer in gxwXml.layers:
-            display = layer['display']
-            maptree = self.gm_cb.GetPage(display).maptree
-            
-            newItem = maptree.AddLayer(ltype = layer['type'],
-                                       lname = layer['name'],
-                                       lchecked = layer['checked'],
-                                       lopacity = layer['opacity'],
-                                       lcmd = layer['cmd'],
-                                       lgroup = layer['group'],
-                                       lnviz = layer['nviz'],
-                                       lvdigit = layer['vdigit'])
-            
-            if layer.has_key('selected'):
-                if layer['selected']:
-                    selected.append((maptree, newItem))
-                else:
-                    maptree.SelectItem(newItem, select = False)
-            
-        for maptree, layer in selected:
-            if not maptree.IsSelected(layer):
-                maptree.SelectItem(layer, select = True)
-                maptree.layer_selected = layer
-                
-        busy.Destroy()
-            
-        for mdisp in mapdisplay:
-            mdisp.MapWindow2D.UpdateMap()
-
-        return True
-    
-    def OnWorkspaceLoadGrcFile(self, event):
-        """!Load map layers from GRC file (Tcl/Tk GUI) into map layer tree"""
-        dlg = wx.FileDialog(parent = self, message = _("Choose GRC file to load"),
-                            defaultDir = os.getcwd(), wildcard = _("Old GRASS Workspace File (*.grc)|*.grc"))
-
-        filename = ''
-        if dlg.ShowModal() == wx.ID_OK:
-            filename = dlg.GetPath()
-
-        if filename == '':
-            return
-
-        Debug.msg(4, "GMFrame.OnWorkspaceLoadGrcFile(): filename=%s" % filename)
-
-        # start new map display if no display is available
-        if not self.curr_page:
-            self.NewDisplay()
-
-        busy = wx.BusyInfo(message = _("Please wait, loading workspace..."),
-                           parent = self)
-        wx.Yield()
-
-        maptree = None
-        for layer in workspace.ProcessGrcFile(filename).read(self):
-            maptree = self.gm_cb.GetPage(layer['display']).maptree
-            newItem = maptree.AddLayer(ltype = layer['type'],
-                                       lname = layer['name'],
-                                       lchecked = layer['checked'],
-                                       lopacity = layer['opacity'],
-                                       lcmd = layer['cmd'],
-                                       lgroup = layer['group'])
-
-            busy.Destroy()
-            
-        if maptree:
-            # reverse list of map layers
-            maptree.Map.ReverseListOfLayers()
-
-    def OnWorkspaceSaveAs(self, event = None):
-        """!Save workspace definition to selected file"""
-        dlg = wx.FileDialog(parent = self, message = _("Choose file to save current workspace"),
-                            defaultDir = os.getcwd(), wildcard = _("GRASS Workspace File (*.gxw)|*.gxw"), style = wx.FD_SAVE)
-
-        filename = ''
-        if dlg.ShowModal() == wx.ID_OK:
-            filename = dlg.GetPath()
-
-        if filename == '':
-            return False
-
-        # check for extension
-        if filename[-4:] != ".gxw":
-            filename += ".gxw"
-
-        if os.path.exists(filename):
-            dlg = wx.MessageDialog(self, message = _("Workspace file <%s> already exists. "
-                                                     "Do you want to overwrite this file?") % filename,
-                                   caption = _("Save workspace"), style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
-            if dlg.ShowModal() != wx.ID_YES:
-                dlg.Destroy()
-                return False
-
-        Debug.msg(4, "GMFrame.OnWorkspaceSaveAs(): filename=%s" % filename)
-
-        self.SaveToWorkspaceFile(filename)
-        self.workspaceFile = filename
-        self.SetTitle(self.baseTitle + " - " + os.path.basename(self.workspaceFile))
-
-    def OnWorkspaceSave(self, event = None):
-        """!Save file with workspace definition"""
-        if self.workspaceFile:
-            dlg = wx.MessageDialog(self, message = _("Workspace file <%s> already exists. "
-                                                   "Do you want to overwrite this file?") % \
-                                       self.workspaceFile,
-                                   caption = _("Save workspace"), style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
-            if dlg.ShowModal() == wx.ID_NO:
-                dlg.Destroy()
-            else:
-                Debug.msg(4, "GMFrame.OnWorkspaceSave(): filename=%s" % self.workspaceFile)
-                self.SaveToWorkspaceFile(self.workspaceFile)
-                self.SetTitle(self.baseTitle + " - " + os.path.basename(self.workspaceFile))
-                self.workspaceChanged = False
-        else:
-            self.OnWorkspaceSaveAs()
-
-    def SaveToWorkspaceFile(self, filename):
-        """!Save layer tree layout to workspace file
-        
-        Return True on success, False on error
-        """
-        tmpfile = tempfile.TemporaryFile(mode = 'w+b')
-        try:
-            workspace.WriteWorkspaceFile(lmgr = self, file = tmpfile)
-        except StandardError, e:
-            gcmd.GError(parent = self,
-                        message = _("Writing current settings to workspace file "
-                                    "failed."))
-            return False
-        
-        try:
-            mfile = open(filename, "w")
-            tmpfile.seek(0)
-            for line in tmpfile.readlines():
-                mfile.write(line)
-        except IOError:
-            gcmd.GError(parent = self,
-                        message = _("Unable to open file <%s> for writing.") % filename)
-            return False
-        
-        mfile.close()
-        
-        return True
-    
-    def OnWorkspaceClose(self, event = None):
-        """!Close file with workspace definition
-        
-        If workspace has been modified ask user to save the changes.
-        """
-        Debug.msg(4, "GMFrame.OnWorkspaceClose(): file=%s" % self.workspaceFile)
-        
-        self.OnDisplayCloseAll()
-        self.workspaceFile = None
-        self.workspaceChanged = False
-        self.SetTitle(self.baseTitle)
-        self.disp_idx = 0
-        self.curr_page = None
-        
-    def OnDisplayClose(self, event = None):
-        """!Close current map display window
-        """
-        if self.curr_page and self.curr_page.maptree.mapdisplay:
-            self.curr_page.maptree.mapdisplay.OnCloseWindow(event)
-        
-    def OnDisplayCloseAll(self, event = None):
-        """!Close all open map display windows
-        """
-        displays = list()
-        for page in range(0, self.gm_cb.GetPageCount()):
-            displays.append(self.gm_cb.GetPage(page).maptree.mapdisplay)
-        
-        for display in displays:
-            display.OnCloseWindow(event)
-        
-    def RulesCmd(self, event):
-        """!Launches dialog for commands that need rules input and
-        processes rules
-        """
-        cmd = self.GetMenuCmd(event)
-        
-        if cmd[0] == 'r.colors':
-            ctable = colorrules.ColorTable(self, raster = True)
-        else:
-            ctable = colorrules.ColorTable(self, raster = False)
-        ctable.CentreOnScreen()
-        ctable.Show()
-        
-    def OnXTermNoXMon(self, event):
-        """!
-        Run commands that need xterm
-        """
-        self.OnXTerm(event, need_xmon = False)
-        
-    def OnXTerm(self, event, need_xmon = True):
-        """!
-        Run commands that need interactive xmon
-
-        @param need_xmon True to start X monitor
-        """
-        # unset display mode
-        del os.environ['GRASS_RENDER_IMMEDIATE']
-        
-        if need_xmon:
-            # open next available xmon
-            xmonlist = []
-            
-            # make list of xmons that are not running
-            ret = gcmd.RunCommand('d.mon',
-                                  flags = 'L',
-                                  read = True)
-            
-            for line in ret.split('\n'):               
-                line = line.strip()
-                if line.startswith('x') and 'not running' in line:
-                    xmonlist.append(line[0:2])
-            
-            # find available xmon
-            xmon = xmonlist[0]
-            
-            # bring up the xmon
-            cmdlist = ['d.mon', xmon]
-            p = gcmd.Command(cmdlist, wait=False)
-        
-        # run the command        
-        command = self.GetMenuCmd(event)
-        command = ' '.join(command)
-        
-        gisbase = os.environ['GISBASE']
-        
-        if sys.platform == "win32":
-            runbat = os.path.join(gisbase,'etc','grass-run.bat')
-            cmdlist = ["start", runbat, runbat, command]
-        else:
-            if sys.platform == "darwin":
-                xtermwrapper = os.path.join(gisbase,'etc','grass-xterm-mac')
-            else:
-                xtermwrapper = os.path.join(gisbase,'etc','grass-xterm-wrapper')
-            
-            grassrun = os.path.join(gisbase,'etc','grass-run.sh')
-            cmdlist = [xtermwrapper, '-e', grassrun, command]
-        
-        p = gcmd.Command(cmdlist, wait=False)
-        
-        # reset display mode
-        os.environ['GRASS_RENDER_IMMEDIATE'] = 'TRUE'
-        
-    def OnInstallExtension(self, event):
-        """!Install extension from GRASS Addons SVN repository"""
-        win = InstallExtensionWindow(self, size = (650, 550))
-        win.CentreOnScreen()
-        win.Show()
-
-    def OnUninstallExtension(self, event):
-        """!Uninstall extension"""
-        win = UninstallExtensionWindow(self, size = (650, 300))
-        win.CentreOnScreen()
-        win.Show()
-
-    def OnPreferences(self, event):
-        """!General GUI preferences/settings
-        """
-        if not self.dialogs['preferences']:
-            dlg = preferences.PreferencesDialog(parent = self)
-            self.dialogs['preferences'] = dlg
-            self.dialogs['preferences'].CenterOnScreen()
-            
-            dlg.Bind(preferences.EVT_SETTINGS_CHANGED, self.OnSettingsChanged)
-        
-        self.dialogs['preferences'].ShowModal()
-        
-    def OnHelp(self, event):
-        """!Show help
-        """
-        self.goutput.RunCmd(['g.manual','-i'])
-        
-    def DispHistogram(self, event):
-        """
-        Init histogram display canvas and tools
-        """
-        self.histogram = histogram.HistFrame(self,
-                                             id = wx.ID_ANY, pos = wx.DefaultPosition, size = (400,300),
-                                             style = wx.DEFAULT_FRAME_STYLE)
-
-        #show new display
-        self.histogram.Show()
-        self.histogram.Refresh()
-        self.histogram.Update()
-
-    def DispProfile(self, event):
-        """
-        Init profile canvas and tools
-        """
-        self.profile = profile.ProfileFrame(self,
-                                           id = wx.ID_ANY, pos = wx.DefaultPosition, size = (400,300),
-                                           style = wx.DEFAULT_FRAME_STYLE)
-        self.profile.Show()
-        self.profile.Refresh()
-        self.profile.Update()
-        
-    def OnMapCalculator(self, event, cmd = ''):
-        """!Init map calculator for interactive creation of mapcalc statements
-        """
-        if event:
-            try:
-                cmd = self.GetMenuCmd(event)
-            except KeyError:
-                cmd = ['r.mapcalc']
-        
-        win = mapcalculator.MapCalcFrame(parent = self,
-                                         cmd = cmd[0])
-        win.CentreOnScreen()
-        win.Show()
-        
-    def OnVectorCleaning(self, event, cmd = ''):
-        """!Init interactive vector cleaning
-        """
-        
-        if event:
-            cmd = self.GetMenuCmd(event)
-
-        win = vclean.VectorCleaningFrame(parent = self, cmd = cmd[0])
-        win.CentreOnScreen()
-        win.Show()
-        
-    def OnImportDxfFile(self, event, cmd = None):
-        """!Convert multiple DXF layers to GRASS vector map layers"""
-        dlg = gdialogs.DxfImportDialog(parent = self)
-        dlg.CentreOnScreen()
-        dlg.Show()
-
-    def OnImportGdalLayers(self, event, cmd = None):
-        """!Convert multiple GDAL layers to GRASS raster map layers"""
-        dlg = gdialogs.GdalImportDialog(parent = self)
-        dlg.CentreOnScreen()
-        dlg.Show()
-
-    def OnLinkGdalLayers(self, event, cmd = None):
-        """!Link multiple GDAL layers to GRASS raster map layers"""
-        dlg = gdialogs.GdalImportDialog(parent = self, link = True)
-        dlg.CentreOnScreen()
-        dlg.Show()
-        
-    def OnImportOgrLayers(self, event, cmd = None):
-        """!Convert multiple OGR layers to GRASS vector map layers"""
-        dlg = gdialogs.GdalImportDialog(parent = self, ogr = True)
-        dlg.CentreOnScreen()
-        dlg.Show()
-        
-    def OnLinkOgrLayers(self, event, cmd = None):
-        """!Links multiple OGR layers to GRASS vector map layers"""
-        dlg = gdialogs.GdalImportDialog(parent = self, ogr = True, link = True)
-        dlg.CentreOnScreen()
-        dlg.Show()
-        
-    def OnImportWMS(self, event):
-        """!Import data from OGC WMS server"""
-        dlg = ogc_services.WMSDialog(parent = self, service = 'wms')
-        dlg.CenterOnScreen()
-        
-        if dlg.ShowModal() == wx.ID_OK: # -> import layers
-            layers = dlg.GetLayers()
-            
-            if len(layers.keys()) > 0:
-                for layer in layers.keys():
-                    cmd = ['r.in.wms',
-                           'mapserver=%s' % dlg.GetSettings()['server'],
-                           'layers=%s' % layer,
-                           'output=%s' % layer,
-                           'format=png',
-                           '--overwrite']
-                    styles = ','.join(layers[layer])
-                    if styles:
-                        cmd.append('styles=%s' % styles)
-                    self.goutput.RunCmd(cmd, switchPage = True)
-
-                    self.curr_page.maptree.AddLayer(ltype = 'raster',
-                                                    lname = layer,
-                                                    lcmd = ['d.rast', 'map=%s' % layer],
-                                                    multiple = False)
-            else:
-                self.goutput.WriteWarning(_("Nothing to import. No WMS layer selected."))
-                
-                
-        dlg.Destroy()
-        
-    def OnShowAttributeTable(self, event, selection = 0):
-        """!Show attribute table of the given vector map layer
-        """
-        if not self.curr_page:
-            self.MsgNoLayerSelected()
-            return
-        
-        tree = self.GetLayerTree()
-        layer = tree.layer_selected
-        # no map layer selected
-        if not layer:
-            self.MsgNoLayerSelected()
-            return
-        
-        # available only for vector map layers
-        try:
-            maptype = tree.GetPyData(layer)[0]['maplayer'].type
-        except:
-            maptype = None
-        
-        if not maptype or maptype != 'vector':
-            gcmd.GMessage(parent = self,
-                          message = _("Selected map layer is not vector."))
-            return
-        
-        if not tree.GetPyData(layer)[0]:
-            return
-        dcmd = tree.GetPyData(layer)[0]['cmd']
-        if not dcmd:
-            return
-        
-        busy = wx.BusyInfo(message = _("Please wait, loading attribute data..."),
-                           parent = self)
-        wx.Yield()
-        
-        dbmanager = dbm.AttributeManager(parent = self, id = wx.ID_ANY,
-                                         size = wx.Size(500, 300),
-                                         item = layer, log = self.goutput,
-                                         selection = selection)
-        
-        busy.Destroy()
-        
-        # register ATM dialog
-        self.dialogs['atm'].append(dbmanager)
-        
-        # show ATM window
-        dbmanager.Show()
-        
-    def OnNewDisplay(self, event = None):
-        """!Create new layer tree and map display instance"""
-        self.NewDisplay()
-
-    def NewDisplay(self, show = True):
-        """!Create new layer tree, which will
-        create an associated map display frame
-
-        @param show show map display window if True
-
-        @return reference to mapdisplay intance
-        """
-        Debug.msg(1, "GMFrame.NewDisplay(): idx=%d" % self.disp_idx)
-        
-        # make a new page in the bookcontrol for the layer tree (on page 0 of the notebook)
-        self.pg_panel = wx.Panel(self.gm_cb, id = wx.ID_ANY, style = wx.EXPAND)
-        self.gm_cb.AddPage(self.pg_panel, text = "Display "+ str(self.disp_idx + 1), select = True)
-        self.curr_page = self.gm_cb.GetCurrentPage()
-        
-        # create layer tree (tree control for managing GIS layers)  and put on new notebook page
-        self.curr_page.maptree = layertree.LayerTree(self.curr_page, id = wx.ID_ANY, pos = wx.DefaultPosition,
-                                                     size = wx.DefaultSize, style = wx.TR_HAS_BUTTONS |
-                                                     wx.TR_LINES_AT_ROOT| wx.TR_HIDE_ROOT |
-                                                     wx.TR_DEFAULT_STYLE| wx.NO_BORDER | wx.FULL_REPAINT_ON_RESIZE,
-                                                     idx = self.disp_idx, lmgr = self, notebook = self.gm_cb,
-                                                     auimgr = self._auimgr, showMapDisplay = show)
-        
-        # layout for controls
-        cb_boxsizer = wx.BoxSizer(wx.VERTICAL)
-        cb_boxsizer.Add(self.curr_page.maptree, proportion = 1, flag = wx.EXPAND, border = 1)
-        self.curr_page.SetSizer(cb_boxsizer)
-        cb_boxsizer.Fit(self.curr_page.maptree)
-        self.curr_page.Layout()
-        self.curr_page.maptree.Layout()
-        
-        # use default window layout
-        if UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'enabled'):
-            dim = UserSettings.Get(group = 'general', key = 'defWindowPos', subkey = 'dim')
-            idx = 4 + self.disp_idx * 4
-            try:
-                x, y = map(int, dim.split(',')[idx:idx + 2])
-                w, h = map(int, dim.split(',')[idx + 2:idx + 4])
-                self.curr_page.maptree.mapdisplay.SetPosition((x, y))
-                self.curr_page.maptree.mapdisplay.SetSize((w, h))
-            except:
-                pass
-        
-        self.disp_idx += 1
-        
-        return self.curr_page.maptree.mapdisplay
-    
-    def OnAddMaps(self, event = None):
-        """!Add selected map layers into layer tree"""
-        dialog = gdialogs.AddMapLayersDialog(parent = self, title = _("Add selected map layers into layer tree"))
-
-        if dialog.ShowModal() == wx.ID_OK:
-            # start new map display if no display is available
-            if not self.curr_page:
-                self.NewDisplay()
-
-            maptree = self.curr_page.maptree
-            busy = wx.BusyInfo(message = _("Please wait, loading workspace..."),
-                               parent = self)
-            wx.Yield()
-            
-            for layerName in dialog.GetMapLayers():
-                if dialog.GetLayerType() == 'raster':
-                    cmd = ['d.rast', 'map=%s' % layerName]
-                elif dialog.GetLayerType() == 'vector':
-                    cmd = ['d.vect', 'map=%s' % layerName]
-                newItem = maptree.AddLayer(ltype = dialog.GetLayerType(),
-                                           lname = layerName,
-                                           lchecked = False,
-                                           lopacity = 1.0,
-                                           lcmd = cmd,
-                                           lgroup = None)
-
-            busy.Destroy()
-    
-    def OnAddRaster(self, event):
-        """!Add raster map layer"""
-        # start new map display if no display is available
-        if not self.curr_page:
-            self.NewDisplay(show = True)
-        
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('raster')
-        
-    def OnAddRaster3D(self, event):
-        """!Add 3D raster map layer"""
-        # start new map display if no display is available
-        if not self.curr_page:
-            self.NewDisplay(show = True)
-        
-        self.AddRaster3D(event)
-        
-    def OnAddRasterMisc(self, event):
-        """!Create misc raster popup-menu"""
-        # start new map display if no display is available
-        if not self.curr_page:
-            self.NewDisplay(show = True)
-        
-        self._popupMenu((('addRast3d', self.OnAddRaster3D),
-                         (None, None),
-                         ('addRgb',    self.OnAddRasterRGB),
-                         ('addHis',    self.OnAddRasterHIS),
-                         (None, None),
-                         ('addShaded', self.OnAddRasterShaded),
-                         (None, None),
-                         ('addRArrow', self.OnAddRasterArrow),
-                         ('addRNum',   self.OnAddRasterNum)))
-        
-        # show map display
-        self.curr_page.maptree.mapdisplay.Show()
-        
-    def OnAddVector(self, event):
-        """!Add vector map to the current layer tree"""
-        # start new map display if no display is available
-        if not self.curr_page:
-            self.NewDisplay(show = True)
-        
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('vector')
-
-    def OnAddVectorMisc(self, event):
-        """!Create misc vector popup-menu"""
-        # start new map display if no display is available
-        if not self.curr_page:
-            self.NewDisplay(show = True)
-
-        self._popupMenu((('addThematic', self.OnAddVectorTheme),
-                         ('addChart',    self.OnAddVectorChart)))
-        
-        # show map display
-        self.curr_page.maptree.mapdisplay.Show()
-
-    def OnAddVectorTheme(self, event):
-        """!Add thematic vector map to the current layer tree"""
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('thememap')
-
-    def OnAddVectorChart(self, event):
-        """!Add chart vector map to the current layer tree"""
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('themechart')
-
-    def OnAddOverlay(self, event):
-        """!Create decoration overlay menu""" 
-        # start new map display if no display is available
-        if not self.curr_page:
-            self.NewDisplay(show = True)
-
-        self._popupMenu((('addGrid',     self.OnAddGrid),
-                         ('addLabels',   self.OnAddLabels),
-                         ('addGeodesic', self.OnAddGeodesic),
-                         ('addRhumb',    self.OnAddRhumb),
-                         (None, None),
-                         ('addCmd',      self.OnAddCommand)))
-        
-        # show map display
-        self.curr_page.maptree.mapdisplay.Show()
-        
-    def OnAddRaster3D(self, event):
-        """!Add 3D raster map to the current layer tree"""
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('3d-raster')
-
-    def OnAddRasterRGB(self, event):
-        """!Add RGB raster map to the current layer tree"""
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('rgb')
-
-    def OnAddRasterHIS(self, event):
-        """!Add HIS raster map to the current layer tree"""
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('his')
-
-    def OnAddRasterShaded(self, event):
-        """!Add shaded relief raster map to the current layer tree"""
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('shaded')
-
-    def OnAddRasterArrow(self, event):
-        """!Add flow arrows raster map to the current layer tree"""
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('rastarrow')
-
-    def OnAddRasterNum(self, event):
-        """!Add cell number raster map to the current layer tree"""
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('rastnum')
-
-    def OnAddCommand(self, event):
-        """!Add command line map layer to the current layer tree"""
-        # start new map display if no display is available
-        if not self.curr_page:
-            self.NewDisplay(show = True)
-
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('command')
-
-        # show map display
-        self.curr_page.maptree.mapdisplay.Show()
-
-    def OnAddGroup(self, event):
-        """!Add layer group"""
-        # start new map display if no display is available
-        if not self.curr_page:
-            self.NewDisplay(show = True)
-
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('group')
-
-        # show map display
-        self.curr_page.maptree.mapdisplay.Show()
-
-    def OnAddGrid(self, event):
-        """!Add grid map layer to the current layer tree"""
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('grid')
-
-    def OnAddGeodesic(self, event):
-        """!Add geodesic line map layer to the current layer tree"""
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('geodesic')
-
-    def OnAddRhumb(self, event):
-        """!Add rhumb map layer to the current layer tree"""
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('rhumb')
-
-    def OnAddLabels(self, event):
-        """!Add vector labels map layer to the current layer tree"""
-        # start new map display if no display is available
-        if not self.curr_page:
-            self.NewDisplay(show = True)
-
-        self.notebook.SetSelectionByName('layers')
-        self.curr_page.maptree.AddLayer('labels')
-
-        # show map display
-        self.curr_page.maptree.mapdisplay.Show()
-
-    def OnDeleteLayer(self, event):
-        """!Remove selected map layer from the current layer Tree
-        """
-        if not self.curr_page or not self.curr_page.maptree.layer_selected:
-            self.MsgNoLayerSelected()
-            return
-
-        if UserSettings.Get(group = 'manager', key = 'askOnRemoveLayer', subkey = 'enabled'):
-            layerName = ''
-            for item in self.curr_page.maptree.GetSelections():
-                name = str(self.curr_page.maptree.GetItemText(item))
-                idx = name.find('(opacity')
-                if idx > -1:
-                    layerName += '<' + name[:idx].strip(' ') + '>,\n'
-                else:
-                    layerName += '<' + name + '>,\n'
-            layerName = layerName.rstrip(',\n')
-            
-            if len(layerName) > 2: # <>
-                message = _("Do you want to remove map layer(s)\n%s\n"
-                            "from layer tree?") % layerName
-            else:
-                message = _("Do you want to remove selected map layer(s) "
-                            "from layer tree?")
-
-            dlg = wx.MessageDialog (parent = self, message = message,
-                                    caption = _("Remove map layer"),
-                                    style  =  wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
-
-            if dlg.ShowModal() != wx.ID_YES:
-                dlg.Destroy()
-                return
-            
-            dlg.Destroy()
-
-        for layer in self.curr_page.maptree.GetSelections():
-            if self.curr_page.maptree.GetPyData(layer)[0]['type'] == 'group':
-                self.curr_page.maptree.DeleteChildren(layer)
-            self.curr_page.maptree.Delete(layer)
-        
-    def OnKeyDown(self, event):
-        """!Key pressed"""
-        kc = event.GetKeyCode()
-        
-        if event.ControlDown():
-            if kc == wx.WXK_TAB:
-                # switch layer list / command output
-                if self.notebook.GetSelection() == self.notebook.GetPageIndexByName('layers'):
-                    self.notebook.SetSelectionByName('output')
-                else:
-                    self.notebook.SetSelectionByName('layers')
-        
-        try:
-            ckc = chr(kc)
-        except ValueError:
-            event.Skip()
-            return
-        
-        if event.CtrlDown():
-            if kc == 'R':
-                self.OnAddRaster(None)
-            elif kc == 'V':
-                self.OnAddVector(None)
-        
-        event.Skip()
-
-    def OnCloseWindow(self, event):
-        """!Cleanup when wxGUI is quitted"""
-        if not self.curr_page:
-            self._auimgr.UnInit()
-            self.Destroy()
-            return
-        
-        maptree = self.curr_page.maptree
-        if self.workspaceChanged and \
-                UserSettings.Get(group = 'manager', key = 'askOnQuit', subkey = 'enabled'):
-            if self.workspaceFile:
-                message = _("Do you want to save changes in the workspace?")
-            else:
-                message = _("Do you want to store current settings "
-                            "to workspace file?")
-            
-            # ask user to save current settings
-            if maptree.GetCount() > 0:
-                dlg = wx.MessageDialog(self,
-                                       message = message,
-                                       caption = _("Quit GRASS GUI"),
-                                       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.workspaceFile:
-                        self.OnWorkspaceSaveAs()
-                    else:
-                        self.SaveToWorkspaceFile(self.workspaceFile)
-                elif ret == wx.ID_CANCEL:
-                    event.Veto()
-                    dlg.Destroy()
-                    return
-                dlg.Destroy()
-        
-        # don't ask any more...
-        UserSettings.Set(group = 'manager', key = 'askOnQuit', subkey = 'enabled',
-                         value = False)
-        
-        self.OnDisplayCloseAll()
-        
-        self.gm_cb.DeleteAllPages()
-        
-        self._auimgr.UnInit()
-        self.Destroy()
-        
-    def MsgNoLayerSelected(self):
-        """!Show dialog message 'No layer selected'"""
-        wx.MessageBox(parent = self,
-                      message = _("No map layer selected. Operation cancelled."),
-                      caption = _("Message"),
-                      style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
-    
 class GMApp(wx.App):
     def __init__(self, workspace = None):
         """!Main GUI class.
@@ -1631,27 +71,6 @@
         
         wx.Yield()
         
-        ### TODO: adjust initial window layout if necessary
-        w, h = wx.GetDisplaySize()
-        # only neccessary if one of the windows is falling out of
-        # the current display size
-        
-        # check if settings file exists
-        # if settings file exists, check if we should use the stored settings
-        #     if we should use stored settings, use stored settings
-        #     else use default settings
-        # else if settings file does not exist, use default settings
-        # check if any of the windows is falling out of the current display
-        # if yes, pull it in
-        #   falling out to the right
-        #   x pos = display width - window width
-        #   falling out to the bottom
-        #   y pos = 0
-        # update settings
-        # if settings file exists, update settings but keep settings for
-        # additional map display windows, or update them too
-        # do not erase settings for additional map display windows !
-        
         # create and show main frame
         mainframe = GMFrame(parent = None, id = wx.ID_ANY,
                             workspace = self.workspaceFile)
@@ -1689,9 +108,9 @@
     return (workspaceFile,)
 
 def main(argv = None):
-    #
-    # process command-line arguments
-    #
+    import gettext
+    gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
+    
     if argv is None:
         argv = sys.argv
     try:
@@ -1700,22 +119,19 @@
                                        ["help", "workspace"])
         except getopt.error, msg:
             raise Usage(msg)
-
+    
     except Usage, err:
         print >> sys.stderr, err.msg
         print >> sys.stderr, "for help use --help"
         printHelp()
-
+    
     workspaceFile = process_opt(opts, args)[0]
-
-    #
-    # run application
-    #
+    
     app = GMApp(workspaceFile)
     # suppress wxPython logs
     q = wx.LogNull()
-
+    
     app.MainLoop()
-
+    
 if __name__ == "__main__":
     sys.exit(main())

Added: grass/branches/releasebranch_6_4/gui/wxpython/wxplot/base.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/wxplot/base.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/wxplot/base.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,543 @@
+"""!
+ at package wxplot.base
+
+ at brief Base classes for iinteractive plotting using PyPlot
+
+Classes:
+ - base::PlotIcons
+ - base::BasePlotFrame
+
+(C) 2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton, Arizona State University
+"""
+
+import os
+import sys
+
+import wx
+import wx.lib.plot as plot
+
+from core.globalvar    import ETCICONDIR
+from core.settings     import UserSettings
+from wxplot.dialogs    import TextDialog, OptDialog
+from core.render       import Map
+from icons.icon        import MetaIcon
+from gui_core.toolbars import BaseIcons
+
+import grass.script as grass
+
+PlotIcons = {
+    'draw'         : MetaIcon(img = 'show',
+                              label = _('Draw/re-draw plot')),
+    'transect'     : MetaIcon(img = 'layer-raster-profile',
+                              label = _('Draw transect in map display window to profile')),
+    'options'      : MetaIcon(img = 'settings',
+                              label = _('Plot options')),
+    'statistics'   : MetaIcon(img = 'check',
+                              label = _('Plot statistics')),
+    'save'         : MetaIcon(img = 'save',
+                              label = _('Save profile data to CSV file')),
+    'quit'         : BaseIcons['quit'].SetLabel(_('Quit plot tool')),
+    }
+
+class BasePlotFrame(wx.Frame):
+    """!Abstract PyPlot display frame class"""
+    def __init__(self, parent = None, id = wx.ID_ANY, size = wx.Size(700, 400),
+                 style = wx.DEFAULT_FRAME_STYLE, rasterList = [],  **kwargs):
+
+        wx.Frame.__init__(self, parent, id, size = size, style = style, **kwargs)
+        
+        self.parent = parent            # MapFrame
+        self.mapwin = self.parent.MapWindow
+        self.Map    = Map()             # instance of render.Map to be associated with display
+        self.rasterList = rasterList    #list of rasters to plot
+        self.raster = {}    # dictionary of raster maps and their plotting parameters
+        self.plottype = ''
+        
+        self.linestyledict = { 'solid' : wx.SOLID,
+                            'dot' : wx.DOT,
+                            'long-dash' : wx.LONG_DASH,
+                            'short-dash' : wx.SHORT_DASH,
+                            'dot-dash' : wx.DOT_DASH }
+
+        self.ptfilldict = { 'transparent' : wx.TRANSPARENT,
+                            'solid' : wx.SOLID }
+
+        #
+        # Icon
+        #
+        self.SetIcon(wx.Icon(os.path.join(ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
+                
+        #
+        # Add statusbar
+        #
+        self.statusbar = self.CreateStatusBar(number = 2, style = 0)
+        self.statusbar.SetStatusWidths([-2, -1])
+
+        #
+        # Define canvas and settings
+        #
+        # 
+        self.client = plot.PlotCanvas(self)
+
+        #define the function for drawing pointLabels
+        self.client.SetPointLabelFunc(self.DrawPointLabel)
+
+        # Create mouse event for showing cursor coords in status bar
+        self.client.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
+
+        # Show closest point when enabled
+        self.client.canvas.Bind(wx.EVT_MOTION, self.OnMotion)
+
+        self.plotlist = []      # list of things to plot
+        self.plot = None        # plot draw object
+        self.ptitle = ""        # title of window
+        self.xlabel = ""        # default X-axis label
+        self.ylabel = ""        # default Y-axis label
+
+        #
+        # Bind various events
+        #
+        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+        
+        self.CentreOnScreen()
+        
+        self._createColorDict()
+
+
+    def _createColorDict(self):
+        """!Create color dictionary to return wx.Color tuples
+        for assigning colors to images in imagery groups"""
+                
+        self.colorDict = {}
+        for clr in grass.named_colors.iterkeys():
+            if clr == 'white' or clr == 'black': continue
+            r = grass.named_colors[clr][0] * 255
+            g = grass.named_colors[clr][1] * 255
+            b = grass.named_colors[clr][2] * 255
+            self.colorDict[clr] = (r,g,b,255)
+
+    def InitPlotOpts(self, plottype):
+        """!Initialize options for entire plot
+        """
+        
+        self.plottype = plottype                # histogram, profile, or scatter
+
+        self.properties = {}                    # plot properties
+        self.properties['font'] = {}
+        self.properties['font']['prop'] = UserSettings.Get(group = self.plottype, key = 'font')
+        self.properties['font']['wxfont'] = wx.Font(11, wx.FONTFAMILY_SWISS,
+                                                    wx.FONTSTYLE_NORMAL,
+                                                    wx.FONTWEIGHT_NORMAL)
+        
+        if self.plottype == 'profile':
+            self.properties['marker'] = UserSettings.Get(group = self.plottype, key = 'marker')
+            # changing color string to tuple for markers/points
+            colstr = str(self.properties['marker']['color'])
+            self.properties['marker']['color'] = tuple(int(colval) for colval in colstr.strip('()').split(','))
+
+        self.properties['grid'] = UserSettings.Get(group = self.plottype, key = 'grid')        
+        colstr = str(self.properties['grid']['color']) # changing color string to tuple        
+        self.properties['grid']['color'] = tuple(int(colval) for colval in colstr.strip('()').split(','))
+                
+        self.properties['x-axis'] = {}
+        self.properties['x-axis']['prop'] = UserSettings.Get(group = self.plottype, key = 'x-axis')
+        self.properties['x-axis']['axis'] = None
+
+        self.properties['y-axis'] = {}
+        self.properties['y-axis']['prop'] = UserSettings.Get(group = self.plottype, key = 'y-axis')
+        self.properties['y-axis']['axis'] = None
+        
+        self.properties['legend'] = UserSettings.Get(group = self.plottype, key = 'legend')
+
+        self.zoom = False  # zooming disabled
+        self.drag = False  # draging disabled
+        self.client.SetShowScrollbars(True) # vertical and horizontal scrollbars
+
+        # x and y axis set to normal (non-log)
+        self.client.setLogScale((False, False))
+        if self.properties['x-axis']['prop']['type']:
+            self.client.SetXSpec(self.properties['x-axis']['prop']['type'])
+        else:
+            self.client.SetXSpec('auto')
+        
+        if self.properties['y-axis']['prop']['type']:
+            self.client.SetYSpec(self.properties['y-axis']['prop']['type'])
+        else:
+            self.client.SetYSpec('auto')
+        
+    def InitRasterOpts(self, rasterList, plottype):
+        """!Initialize or update raster dictionary for plotting
+        """
+
+        rdict = {} # initialize a dictionary
+
+        for r in rasterList:
+            idx = rasterList.index(r)
+            
+            try:
+                ret = grass.raster_info(r)
+            except:
+                continue
+                # if r.info cannot parse map, skip it
+               
+            self.raster[r] = UserSettings.Get(group = plottype, key = 'raster') # some default settings
+            rdict[r] = {} # initialize sub-dictionaries for each raster in the list
+
+            
+            rdict[r]['units'] = ''
+            if ret['units'] not in ('(none)', '"none"', '', None):
+                rdict[r]['units'] = ret['units']
+            
+            rdict[r]['plegend'] = r.split('@')[0]
+            rdict[r]['datalist'] = [] # list of cell value,frequency pairs for plotting histogram
+            rdict[r]['pline'] = None
+            rdict[r]['datatype'] = ret['datatype']
+            rdict[r]['pwidth'] = 1
+            rdict[r]['pstyle'] = 'solid'
+            
+            if idx <= len(self.colorList):
+                rdict[r]['pcolor'] = self.colorDict[self.colorList[idx]]
+            else:
+                r = randint(0, 255)
+                b = randint(0, 255)
+                g = randint(0, 255)
+                rdict[r]['pcolor'] = ((r,g,b,255))
+        
+        return rdict
+            
+    def InitRasterPairs(self, rasterList, plottype):
+        """!Initialize or update raster dictionary with raster pairs for
+            bivariate scatterplots
+        """
+        
+        if len(rasterList) == 0: return
+
+        rdict = {} # initialize a dictionary
+        for rpair in rasterList:
+            idx = rasterList.index(rpair)
+            
+            try:
+                ret0 = grass.raster_info(rpair[0])
+                ret1 = grass.raster_info(rpair[1])
+
+            except:
+                continue
+                # if r.info cannot parse map, skip it
+
+            self.raster[rpair] = UserSettings.Get(group = plottype, key = 'rasters') # some default settings
+            rdict[rpair] = {} # initialize sub-dictionaries for each raster in the list
+            rdict[rpair][0] = {}
+            rdict[rpair][1] = {}
+            rdict[rpair][0]['units'] = ''
+            rdict[rpair][1]['units'] = ''
+
+            if ret0['units'] not in ('(none)', '"none"', '', None):
+                rdict[rpair][0]['units'] = ret0['units']
+            if ret1['units'] not in ('(none)', '"none"', '', None):
+                rdict[rpair][1]['units'] = ret1['units']
+                
+            rdict[rpair]['plegend'] = rpair[0].split('@')[0] + ' vs ' + rpair[1].split('@')[0]
+            rdict[rpair]['datalist'] = [] # list of cell value,frequency pairs for plotting histogram
+            rdict[rpair]['ptype'] = 'dot'
+            rdict[rpair][0]['datatype'] = ret0['datatype']
+            rdict[rpair][1]['datatype'] = ret1['datatype']
+            rdict[rpair]['psize'] = 1
+            rdict[rpair]['pfill'] = 'solid'
+            
+            if idx <= len(self.colorList):
+                rdict[rpair]['pcolor'] = self.colorDict[self.colorList[idx]]
+            else:
+                r = randint(0, 255)
+                b = randint(0, 255)
+                g = randint(0, 255)
+                rdict[rpair]['pcolor'] = ((r,g,b,255))
+            
+        return rdict
+
+    def SetGraphStyle(self):
+        """!Set plot and text options
+        """
+        self.client.SetFont(self.properties['font']['wxfont'])
+        self.client.SetFontSizeTitle(self.properties['font']['prop']['titleSize'])
+        self.client.SetFontSizeAxis(self.properties['font']['prop']['axisSize'])
+
+        self.client.SetEnableZoom(self.zoom)
+        self.client.SetEnableDrag(self.drag)
+        
+        #
+        # axis settings
+        #
+        if self.properties['x-axis']['prop']['type'] == 'custom':
+            self.client.SetXSpec('min')
+        else:
+            self.client.SetXSpec(self.properties['x-axis']['prop']['type'])
+
+        if self.properties['y-axis']['prop']['type'] == 'custom':
+            self.client.SetYSpec('min')
+        else:
+            self.client.SetYSpec(self.properties['y-axis']['prop'])
+
+        if self.properties['x-axis']['prop']['type'] == 'custom' and \
+               self.properties['x-axis']['prop']['min'] < self.properties['x-axis']['prop']['max']:
+            self.properties['x-axis']['axis'] = (self.properties['x-axis']['prop']['min'],
+                                                 self.properties['x-axis']['prop']['max'])
+        else:
+            self.properties['x-axis']['axis'] = None
+
+        if self.properties['y-axis']['prop']['type'] == 'custom' and \
+                self.properties['y-axis']['prop']['min'] < self.properties['y-axis']['prop']['max']:
+            self.properties['y-axis']['axis'] = (self.properties['y-axis']['prop']['min'],
+                                                 self.properties['y-axis']['prop']['max'])
+        else:
+            self.properties['y-axis']['axis'] = None
+
+        self.client.SetEnableGrid(self.properties['grid']['enabled'])
+        
+        self.client.SetGridColour(wx.Color(self.properties['grid']['color'][0],
+                                           self.properties['grid']['color'][1],
+                                           self.properties['grid']['color'][2],
+                                           255))
+
+        self.client.SetFontSizeLegend(self.properties['font']['prop']['legendSize'])
+        self.client.SetEnableLegend(self.properties['legend']['enabled'])
+
+        if self.properties['x-axis']['prop']['log'] == True:
+            self.properties['x-axis']['axis'] = None
+            self.client.SetXSpec('min')
+        if self.properties['y-axis']['prop']['log'] == True:
+            self.properties['y-axis']['axis'] = None
+            self.client.SetYSpec('min')
+            
+        self.client.setLogScale((self.properties['x-axis']['prop']['log'],
+                                 self.properties['y-axis']['prop']['log']))
+
+    def DrawPlot(self, plotlist):
+        """!Draw line and point plot from list plot elements.
+        """
+        self.plot = plot.PlotGraphics(plotlist,
+                                         self.ptitle,
+                                         self.xlabel,
+                                         self.ylabel)
+
+        if self.properties['x-axis']['prop']['type'] == 'custom':
+            self.client.SetXSpec('min')
+        else:
+            self.client.SetXSpec(self.properties['x-axis']['prop']['type'])
+
+        if self.properties['y-axis']['prop']['type'] == 'custom':
+            self.client.SetYSpec('min')
+        else:
+            self.client.SetYSpec(self.properties['y-axis']['prop']['type'])
+
+        self.client.Draw(self.plot, self.properties['x-axis']['axis'],
+                         self.properties['y-axis']['axis'])
+                
+    def DrawPointLabel(self, dc, mDataDict):
+        """!This is the fuction that defines how the pointLabels are
+            plotted dc - DC that will be passed mDataDict - Dictionary
+            of data that you want to use for the pointLabel
+
+            As an example I have decided I want a box at the curve
+            point with some text information about the curve plotted
+            below.  Any wxDC method can be used.
+        """
+        dc.SetPen(wx.Pen(wx.BLACK))
+        dc.SetBrush(wx.Brush( wx.BLACK, wx.SOLID ) )
+
+        sx, sy = mDataDict["scaledXY"] #scaled x,y of closest point
+        dc.DrawRectangle( sx-5,sy-5, 10, 10)  #10by10 square centered on point
+        px,py = mDataDict["pointXY"]
+        cNum = mDataDict["curveNum"]
+        pntIn = mDataDict["pIndex"]
+        legend = mDataDict["legend"]
+        #make a string to display
+        s = "Crv# %i, '%s', Pt. (%.2f,%.2f), PtInd %i" %(cNum, legend, px, py, pntIn)
+        dc.DrawText(s, sx , sy+1)
+
+    def OnZoom(self, event):
+        """!Enable zooming and disable dragging
+        """
+        self.zoom = True
+        self.drag = False
+        self.client.SetEnableZoom(self.zoom)
+        self.client.SetEnableDrag(self.drag)
+
+    def OnDrag(self, event):
+        """!Enable dragging and disable zooming
+        """
+        self.zoom = False
+        self.drag = True
+        self.client.SetEnableDrag(self.drag)
+        self.client.SetEnableZoom(self.zoom)
+
+    def OnRedraw(self, event):
+        """!Redraw the plot window. Unzoom to original size
+        """
+        self.client.Reset()
+        self.client.Redraw()
+       
+    def OnErase(self, event):
+        """!Erase the plot window
+        """
+        self.client.Clear()
+        self.mapwin.ClearLines(self.mapwin.pdc)
+        self.mapwin.ClearLines(self.mapwin.pdcTmp)
+        self.mapwin.polycoords = []
+        self.mapwin.Refresh()
+
+    def SaveToFile(self, event):
+        """!Save plot to graphics file
+        """
+        self.client.SaveFile()
+
+    def OnMouseLeftDown(self,event):
+        self.SetStatusText(_("Left Mouse Down at Point:") + \
+                               " (%.4f, %.4f)" % self.client._getXY(event))
+        event.Skip() # allows plotCanvas OnMouseLeftDown to be called
+
+    def OnMotion(self, event):
+        """!Indicate when mouse is outside the plot area
+        """
+        if self.client.OnLeave(event): print 'out of area'
+        #show closest point (when enbled)
+        if self.client.GetEnablePointLabel() == True:
+            #make up dict with info for the pointLabel
+            #I've decided to mark the closest point on the closest curve
+            dlst =  self.client.GetClosetPoint( self.client._getXY(event), pointScaled =  True)
+            if dlst != []:      #returns [] if none
+                curveNum, legend, pIndex, pointXY, scaledXY, distance = dlst
+                #make up dictionary to pass to my user function (see DrawPointLabel)
+                mDataDict =  {"curveNum":curveNum, "legend":legend, "pIndex":pIndex,\
+                    "pointXY":pointXY, "scaledXY":scaledXY}
+                #pass dict to update the pointLabel
+                self.client.UpdatePointLabel(mDataDict)
+        event.Skip()           #go to next handler        
+ 
+ 
+    def PlotOptionsMenu(self, event):
+        """!Popup menu for plot and text options
+        """
+        point = wx.GetMousePosition()
+        popt = wx.Menu()
+        # Add items to the menu
+        settext = wx.MenuItem(popt, wx.ID_ANY, _('Text settings'))
+        popt.AppendItem(settext)
+        self.Bind(wx.EVT_MENU, self.PlotText, settext)
+
+        setgrid = wx.MenuItem(popt, wx.ID_ANY, _('Plot settings'))
+        popt.AppendItem(setgrid)
+        self.Bind(wx.EVT_MENU, self.PlotOptions, setgrid)
+
+        # Popup the menu.  If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.PopupMenu(popt)
+        popt.Destroy()
+
+    def NotFunctional(self):
+        """!Creates a 'not functional' message dialog
+        """
+        dlg = wx.MessageDialog(parent = self,
+                               message = _('This feature is not yet functional'),
+                               caption = _('Under Construction'),
+                               style = wx.OK | wx.ICON_INFORMATION)
+        dlg.ShowModal()
+        dlg.Destroy()
+
+    def OnPlotText(self, dlg):
+        """!Custom text settings for histogram plot.
+        """
+        self.ptitle = dlg.ptitle
+        self.xlabel = dlg.xlabel
+        self.ylabel = dlg.ylabel
+        dlg.UpdateSettings()
+
+        self.client.SetFont(self.properties['font']['wxfont'])
+        self.client.SetFontSizeTitle(self.properties['font']['prop']['titleSize'])
+        self.client.SetFontSizeAxis(self.properties['font']['prop']['axisSize'])
+
+        if self.plot:
+            self.plot.setTitle(dlg.ptitle)
+            self.plot.setXLabel(dlg.xlabel)
+            self.plot.setYLabel(dlg.ylabel)
+        
+        self.OnRedraw(event = None)
+    
+    def PlotText(self, event):
+        """!Set custom text values for profile title and axis labels.
+        """
+        dlg = TextDialog(parent = self, id = wx.ID_ANY, 
+                                 plottype = self.plottype, 
+                                 title = _('Histogram text settings'))
+
+        if dlg.ShowModal() == wx.ID_OK:
+            self.OnPlotText(dlg)
+
+        dlg.Destroy()
+
+    def PlotOptions(self, event):
+        """!Set various profile options, including: line width, color,
+        style; marker size, color, fill, and style; grid and legend
+        options.  Calls OptDialog class.
+        """
+        dlg = OptDialog(parent = self, id = wx.ID_ANY, 
+                        plottype = self.plottype, 
+                        title = _('Plot settings'))
+        btnval = dlg.ShowModal()
+
+        if btnval == wx.ID_SAVE:
+            dlg.UpdateSettings()            
+            self.SetGraphStyle()            
+            dlg.Destroy()            
+        elif btnval == wx.ID_CANCEL:
+            dlg.Destroy()
+
+    def PrintMenu(self, event):
+        """!Print options and output menu
+        """
+        point = wx.GetMousePosition()
+        printmenu = wx.Menu()
+        for title, handler in ((_("Page setup"), self.OnPageSetup),
+                               (_("Print preview"), self.OnPrintPreview),
+                               (_("Print display"), self.OnDoPrint)):
+            item = wx.MenuItem(printmenu, wx.ID_ANY, title)
+            printmenu.AppendItem(item)
+            self.Bind(wx.EVT_MENU, handler, item)
+        
+        # Popup the menu.  If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.PopupMenu(printmenu)
+        printmenu.Destroy()
+
+    def OnPageSetup(self, event):
+        self.client.PageSetup()
+
+    def OnPrintPreview(self, event):
+        self.client.PrintPreview()
+
+    def OnDoPrint(self, event):
+        self.client.Printout()
+
+    def OnQuit(self, event):
+        self.Close(True)
+
+    def OnCloseWindow(self, event):
+        """!Close plot window and clean up
+        """
+        try:
+            self.mapwin.ClearLines()
+            self.mapwin.mouse['begin'] = self.mapwin.mouse['end'] = (0.0, 0.0)
+            self.mapwin.mouse['use'] = 'pointer'
+            self.mapwin.mouse['box'] = 'point'
+            self.mapwin.polycoords = []
+            self.mapwin.UpdateMap(render = False, renderVector = False)
+        except:
+            pass
+        
+        self.mapwin.SetCursor(self.Parent.cursors["default"])
+        self.Destroy()
+        


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/wxplot/dialogs.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/wxplot/dialogs.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/wxplot/dialogs.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,796 @@
+"""!
+ at package wxplot.dialogs
+
+ at brief Dialogs for different plotting routines
+
+Classes:
+ - dialogs::ProfileRasterDialog
+ - dialogs::PlotStatsFrame
+ - dialogs::TextDialog
+ - dialogs::OptDialog
+
+(C) 2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton, Arizona State University
+"""
+
+import wx
+import wx.lib.colourselect  as csel
+import wx.lib.scrolledpanel as scrolled
+
+from core             import globalvar
+from core.settings    import UserSettings
+from gui_core.gselect import Select
+
+from grass.script import core  as grass
+
+class ProfileRasterDialog(wx.Dialog):
+    def __init__(self, parent, id = wx.ID_ANY, 
+                 title = _("Select raster maps to profile"),
+                 style = wx.DEFAULT_DIALOG_STYLE, **kwargs):
+        """!Dialog to select raster maps to profile.
+        """
+
+        wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
+
+
+        self.parent = parent
+        self.colorList = ["blue", "red", "green", "yellow", "magenta", "cyan", \
+                    "aqua", "black", "grey", "orange", "brown", "purple", "violet", \
+                    "indigo"]
+
+        self.rasterList = self.parent.rasterList
+        
+        self._do_layout()
+        
+    def _do_layout(self):
+
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        box = wx.GridBagSizer (hgap = 3, vgap = 3)
+        
+        rastText = ''
+        for r in self.rasterList:
+            rastText += '%s,' % r
+            
+        rastText = rastText.rstrip(',')
+        
+        txt = _("Select raster map(s) to profile:")
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = txt)
+        box.Add(item = label,
+                flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
+        
+        selection = Select(self, id = wx.ID_ANY,
+                           size = globalvar.DIALOG_GSELECT_SIZE,
+                           type = 'cell', multiple=True)
+        selection.SetValue(rastText)
+        selection.Bind(wx.EVT_TEXT, self.OnSelection)
+        
+        box.Add(item = selection, pos = (0, 1))
+            
+        sizer.Add(item = box, proportion = 0,
+                  flag = wx.ALL, border = 10)
+
+        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.SetDefault()
+        btnsizer.AddButton(btn)
+
+        btn = wx.Button(self, 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):
+        """!Choose maps to profile. Convert these into a list
+        """
+        self.rasterList = []
+        self.rasterList = event.GetString().split(',')
+   
+class PlotStatsFrame(wx.Frame):
+    def __init__(self, parent, id, message = '', title = '',
+                 style = wx.DEFAULT_FRAME_STYLE, **kwargs):
+        """!Dialog to display and save statistics for plots
+        """
+        wx.Frame.__init__(self, parent, id, style = style, **kwargs)
+        self.SetLabel(_("Statistics"))
+        
+        sp = scrolled.ScrolledPanel(self, -1, size=(400, 400),
+                                    style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER, name="Statistics" )
+                
+
+        #
+        # initialize variables
+        #
+        self.parent = parent
+        self.message = message 
+        self.title = title   
+        self.CenterOnParent()  
+        
+        #
+        # Display statistics
+        #
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        txtSizer = wx.BoxSizer(wx.VERTICAL)
+
+        statstitle = wx.StaticText(parent = self, id = wx.ID_ANY, label = self.title)
+        sizer.Add(item = statstitle, proportion = 0,
+                  flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 3)
+        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.ALL, border = 3)
+        for stats in self.message:
+            statstxt = wx.StaticText(parent = sp, id = wx.ID_ANY, label = stats)
+            statstxt.SetBackgroundColour("WHITE")
+            txtSizer.Add(item = statstxt, proportion = 1, 
+                         flag = wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, border = 3)
+            line = wx.StaticLine(parent = sp, id = wx.ID_ANY, size = (20, -1), style = wx.LI_HORIZONTAL) 
+            txtSizer.Add(item = line, proportion = 0,
+                         flag = wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 3)      
+
+        sp.SetSizer(txtSizer)
+        sp.SetAutoLayout(1)
+        sp.SetupScrolling() 
+
+        sizer.Add(item = sp, proportion = 1, 
+                  flag = wx.GROW | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 3)
+
+        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 = 3)
+
+        #
+        # buttons
+        #
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+        btn_clipboard = wx.Button(self, id = wx.ID_COPY, label = _('C&opy'))
+        btn_clipboard.SetToolTipString(_("Copy regression statistics the clipboard (Ctrl+C)"))
+        btnSizer.Add(item = btn_clipboard, proportion = 0, flag = wx.ALIGN_LEFT | wx.ALL, border = 5)
+        
+        btnCancel = wx.Button(self, wx.ID_CLOSE)
+        btnCancel.SetDefault()
+        btnSizer.Add(item = btnCancel, proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)        
+
+        sizer.Add(item = btnSizer, proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+
+        # bindings
+        btnCancel.Bind(wx.EVT_BUTTON, self.OnClose)
+        btn_clipboard.Bind(wx.EVT_BUTTON, self.OnCopy)
+        
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+
+    def OnCopy(self, event):
+        """!Copy the regression stats to the clipboard
+        """
+        str = self.title + '\n'
+        for item in self.message:
+            str += item
+            
+        rdata = wx.TextDataObject()
+        rdata.SetText(str)
+        
+        if wx.TheClipboard.Open():
+            wx.TheClipboard.SetData(rdata)
+            wx.TheClipboard.Close()
+            wx.MessageBox(_("Regression statistics copied to clipboard"))
+        
+    def OnClose(self, event):
+        """!Button 'Close' pressed
+        """
+        self.Close(True)
+
+class TextDialog(wx.Dialog):
+    def __init__(self, parent, id, title, 
+                 style = wx.DEFAULT_DIALOG_STYLE, **kwargs):
+        """!Dialog to set profile text options: font, title
+        and font size, axis labels and font size
+        """
+        wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
+        #
+        # initialize variables
+        #
+        # combo box entry lists
+        self.ffamilydict = { 'default' : wx.FONTFAMILY_DEFAULT,
+                             'decorative' : wx.FONTFAMILY_DECORATIVE,
+                             'roman' : wx.FONTFAMILY_ROMAN,
+                             'script' : wx.FONTFAMILY_SCRIPT,
+                             'swiss' : wx.FONTFAMILY_SWISS,
+                             'modern' : wx.FONTFAMILY_MODERN,
+                             'teletype' : wx.FONTFAMILY_TELETYPE }
+
+        self.fstyledict = { 'normal' : wx.FONTSTYLE_NORMAL,
+                            'slant' : wx.FONTSTYLE_SLANT,
+                            'italic' : wx.FONTSTYLE_ITALIC }
+
+        self.fwtdict = { 'normal' : wx.FONTWEIGHT_NORMAL,
+                         'light' : wx.FONTWEIGHT_LIGHT,
+                         'bold' : wx.FONTWEIGHT_BOLD }
+
+        self.parent = parent
+
+        self.ptitle = self.parent.ptitle
+        self.xlabel = self.parent.xlabel
+        self.ylabel = self.parent.ylabel
+
+        self.properties = self.parent.properties # read-only
+        
+        # font size
+        self.fontfamily = self.properties['font']['wxfont'].GetFamily()
+        self.fontstyle = self.properties['font']['wxfont'].GetStyle()
+        self.fontweight = self.properties['font']['wxfont'].GetWeight()
+
+        self._do_layout()
+        
+    def _do_layout(self):
+        """!Do layout"""
+        # dialog layout
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                           label = " %s " % _("Text settings"))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+
+        #
+        # profile title
+        #
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Profile title:"))
+        gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
+        self.ptitleentry = wx.TextCtrl(parent = self, id = wx.ID_ANY, value = "", size = (250,-1))
+        # self.ptitleentry.SetFont(self.font)
+        self.ptitleentry.SetValue(self.ptitle)
+        gridSizer.Add(item = self.ptitleentry, pos = (0, 1))
+
+        #
+        # title font
+        #
+        tlabel = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Title font size (pts):"))
+        gridSizer.Add(item = tlabel, flag = wx.ALIGN_CENTER_VERTICAL, pos = (1, 0))
+        self.ptitlesize = wx.SpinCtrl(parent = self, id = wx.ID_ANY, value = "", pos = (30, 50),
+                                      size = (50,-1), style = wx.SP_ARROW_KEYS)
+        self.ptitlesize.SetRange(5,100)
+        self.ptitlesize.SetValue(int(self.properties['font']['prop']['titleSize']))
+        gridSizer.Add(item = self.ptitlesize, pos = (1, 1))
+
+        #
+        # x-axis label
+        #
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("X-axis label:"))
+        gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (2, 0))
+        self.xlabelentry = wx.TextCtrl(parent = self, id = wx.ID_ANY, value = "", size = (250,-1))
+        # self.xlabelentry.SetFont(self.font)
+        self.xlabelentry.SetValue(self.xlabel)
+        gridSizer.Add(item = self.xlabelentry, pos = (2, 1))
+
+        #
+        # y-axis label
+        #
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Y-axis label:"))
+        gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (3, 0))
+        self.ylabelentry = wx.TextCtrl(parent = self, id = wx.ID_ANY, value = "", size = (250,-1))
+        # self.ylabelentry.SetFont(self.font)
+        self.ylabelentry.SetValue(self.ylabel)
+        gridSizer.Add(item = self.ylabelentry, pos = (3, 1))
+
+        #
+        # font size
+        #
+        llabel = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Label font size (pts):"))
+        gridSizer.Add(item = llabel, flag = wx.ALIGN_CENTER_VERTICAL, pos = (4, 0))
+        self.axislabelsize = wx.SpinCtrl(parent = self, id = wx.ID_ANY, value = "", pos = (30, 50),
+                                         size = (50, -1), style = wx.SP_ARROW_KEYS)
+        self.axislabelsize.SetRange(5, 100) 
+        self.axislabelsize.SetValue(int(self.properties['font']['prop']['axisSize']))
+        gridSizer.Add(item = self.axislabelsize, pos = (4,1))
+
+        boxSizer.Add(item = gridSizer)
+        sizer.Add(item = boxSizer, flag = wx.ALL | wx.EXPAND, border = 3)
+
+        #
+        # font settings
+        #
+        box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                           label = " %s " % _("Font settings"))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        gridSizer.AddGrowableCol(1)
+
+        #
+        # font family
+        #
+        label1 = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Font family:"))
+        gridSizer.Add(item = label1, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
+        self.ffamilycb = wx.ComboBox(parent = self, id = wx.ID_ANY, size = (250, -1),
+                                     choices = self.ffamilydict.keys(), style = wx.CB_DROPDOWN)
+        self.ffamilycb.SetStringSelection('swiss')
+        for item in self.ffamilydict.items():
+            if self.fontfamily == item[1]:
+                self.ffamilycb.SetStringSelection(item[0])
+                break
+        gridSizer.Add(item = self.ffamilycb, pos = (0, 1), flag = wx.ALIGN_RIGHT)
+
+        #
+        # font style
+        #
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Style:"))
+        gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (1, 0))
+        self.fstylecb = wx.ComboBox(parent = self, id = wx.ID_ANY, size = (250, -1),
+                                    choices = self.fstyledict.keys(), style = wx.CB_DROPDOWN)
+        self.fstylecb.SetStringSelection('normal')
+        for item in self.fstyledict.items():
+            if self.fontstyle == item[1]:
+                self.fstylecb.SetStringSelection(item[0])
+                break
+        gridSizer.Add(item = self.fstylecb, pos = (1, 1), flag = wx.ALIGN_RIGHT)
+
+        #
+        # font weight
+        #
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Weight:"))
+        gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (2, 0))
+        self.fwtcb = wx.ComboBox(parent = self, size = (250, -1),
+                                 choices = self.fwtdict.keys(), style = wx.CB_DROPDOWN)
+        self.fwtcb.SetStringSelection('normal')
+        for item in self.fwtdict.items():
+            if self.fontweight == item[1]:
+                self.fwtcb.SetStringSelection(item[0])
+                break
+
+        gridSizer.Add(item = self.fwtcb, pos = (2, 1), flag = wx.ALIGN_RIGHT)
+                      
+        boxSizer.Add(item = gridSizer, flag = wx.EXPAND)
+        sizer.Add(item = boxSizer, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+
+        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 = 3)
+
+        #
+        # buttons
+        #
+        btnSave = wx.Button(self, wx.ID_SAVE)
+        btnApply = wx.Button(self, wx.ID_APPLY)
+        btnOk = wx.Button(self, wx.ID_OK)
+        btnCancel = wx.Button(self, wx.ID_CANCEL)
+        btnOk.SetDefault()
+
+        # bindigs
+        btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+        btnApply.SetToolTipString(_("Apply changes for the current session"))
+        btnOk.Bind(wx.EVT_BUTTON, self.OnOk)
+        btnOk.SetToolTipString(_("Apply changes for the current session and close dialog"))
+        btnOk.SetDefault()
+        btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
+        btnSave.SetToolTipString(_("Apply and save changes to user settings file (default for next sessions)"))
+        btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+        btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
+
+        # sizers
+        btnStdSizer = wx.StdDialogButtonSizer()
+        btnStdSizer.AddButton(btnOk)
+        btnStdSizer.AddButton(btnApply)
+        btnStdSizer.AddButton(btnCancel)
+        btnStdSizer.Realize()
+        
+        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+        btnSizer.Add(item = btnSave, proportion = 0, flag = wx.ALIGN_LEFT | wx.ALL, border = 5)
+        btnSizer.Add(item = btnStdSizer, proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+        sizer.Add(item = btnSizer, proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+
+        #
+        # bindings
+        #
+        self.ptitleentry.Bind(wx.EVT_TEXT, self.OnTitle)
+        self.xlabelentry.Bind(wx.EVT_TEXT, self.OnXLabel)
+        self.ylabelentry.Bind(wx.EVT_TEXT, self.OnYLabel)
+
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+
+    def OnTitle(self, event):
+        self.ptitle = event.GetString()
+
+    def OnXLabel(self, event):
+        self.xlabel = event.GetString()
+
+    def OnYLabel(self, event):
+        self.ylabel = event.GetString()
+
+    def UpdateSettings(self):
+        self.properties['font']['prop']['titleSize'] = self.ptitlesize.GetValue()
+        self.properties['font']['prop']['axisSize'] = self.axislabelsize.GetValue()
+
+        family = self.ffamilydict[self.ffamilycb.GetStringSelection()]
+        self.properties['font']['wxfont'].SetFamily(family)
+        style = self.fstyledict[self.fstylecb.GetStringSelection()]
+        self.properties['font']['wxfont'].SetStyle(style)
+        weight = self.fwtdict[self.fwtcb.GetStringSelection()]
+        self.properties['font']['wxfont'].SetWeight(weight)
+
+    def OnSave(self, event):
+        """!Button 'Save' pressed"""
+        self.UpdateSettings()
+        fileSettings = {}
+        UserSettings.ReadSettingsFile(settings = fileSettings)
+        fileSettings['profile']  =  UserSettings.Get(group = 'profile')
+        file = UserSettings.SaveToFile(fileSettings)
+        self.parent.parent.GetLayerManager().goutput.WriteLog(_('Profile settings saved to file \'%s\'.') % file)
+        self.EndModal(wx.ID_OK)
+
+    def OnApply(self, event):
+        """!Button 'Apply' pressed"""
+        self.UpdateSettings()
+        self.parent.OnPText(self)
+        
+    def OnOk(self, event):
+        """!Button 'OK' pressed"""
+        self.UpdateSettings()
+        self.EndModal(wx.ID_OK)
+
+    def OnCancel(self, event):
+        """!Button 'Cancel' pressed"""
+        self.EndModal(wx.ID_CANCEL)
+        
+class OptDialog(wx.Dialog):
+    def __init__(self, parent, id, title, 
+                 style = wx.DEFAULT_DIALOG_STYLE, **kwargs):
+        """!Dialog to set various profile options, including: line
+        width, color, style; marker size, color, fill, and style; grid
+        and legend options.
+        """
+        wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
+        # init variables
+        self.pstyledict = parent.pstyledict
+        self.ptfilldict = parent.ptfilldict
+
+        self.pttypelist = ['circle',
+                           'dot',
+                           'square',
+                           'triangle',
+                           'triangle_down',
+                           'cross',
+                           'plus']
+        
+        self.axislist = ['min',
+                         'auto',
+                         'custom']
+
+        # widgets ids
+        self.wxId = {}
+        
+        self.parent = parent
+
+        # read-only
+        self.raster = self.parent.raster
+        self.properties = self.parent.properties
+        
+        self._do_layout()
+
+    def _do_layout(self):
+        """!Do layout"""
+        # dialog layout
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        #
+        # profile line settings
+        #
+        box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                           label = " %s " % _("Profile line settings"))
+        boxMainSizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+
+        idx = 1
+        self.wxId['pcolor'] = []
+        self.wxId['pwidth'] = []
+        self.wxId['pstyle'] = []
+        self.wxId['plegend'] = []
+        for r in self.raster.itervalues():
+            box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                               label = " %s %d " % (_("Profile"), idx))
+            boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+            
+            gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+            row = 0            
+            label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Line color"))
+            gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
+            pcolor = csel.ColourSelect(parent = self, id = wx.ID_ANY, colour = r['prop']['pcolor'])
+            self.wxId['pcolor'].append(pcolor.GetId())
+            gridSizer.Add(item = pcolor, pos = (row, 1))
+
+            row += 1
+            label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Line width"))
+            gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
+            pwidth = wx.SpinCtrl(parent = self, id = wx.ID_ANY, value = "",
+                                 size = (50,-1), style = wx.SP_ARROW_KEYS)
+            pwidth.SetRange(1, 10)
+            pwidth.SetValue(r['prop']['pwidth'])
+            self.wxId['pwidth'].append(pwidth.GetId())
+            gridSizer.Add(item = pwidth, pos = (row, 1))
+
+            row +=1
+            label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Line style"))
+            gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
+            pstyle = wx.ComboBox(parent = self, id = wx.ID_ANY, 
+                                 size = (120, -1), choices = self.pstyledict.keys(), style = wx.CB_DROPDOWN)
+            pstyle.SetStringSelection(r['prop']['pstyle'])
+            self.wxId['pstyle'].append(pstyle.GetId())
+            gridSizer.Add(item = pstyle, pos = (row, 1))
+
+            row += 1
+            label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Legend"))
+            gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
+            plegend = wx.TextCtrl(parent = self, id = wx.ID_ANY, value = "", size = (200,-1))
+            plegend.SetValue(r['plegend'])
+            gridSizer.Add(item = plegend, pos = (row, 1))
+            self.wxId['plegend'].append(plegend.GetId())
+            boxSizer.Add(item = gridSizer)
+
+            if idx == 0:
+                flag = wx.ALL
+            else:
+                flag = wx.TOP | wx.BOTTOM | wx.RIGHT
+            boxMainSizer.Add(item = boxSizer, flag = flag, border = 3)
+
+            idx += 1
+            
+        sizer.Add(item = boxMainSizer, flag = wx.ALL | wx.EXPAND, border = 3)
+
+        middleSizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        #
+        # segment marker settings
+        #
+        box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                           label = " %s " % _("Transect segment marker settings"))
+        boxMainSizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+
+        self.wxId['marker'] = {}
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Color"))
+        gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
+        ptcolor = csel.ColourSelect(parent = self, id = wx.ID_ANY, colour = self.properties['marker']['color'])
+        self.wxId['marker']['color'] = ptcolor.GetId()
+        gridSizer.Add(item = ptcolor, pos = (0, 1))
+
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Size"))
+        gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (1, 0))
+        ptsize = wx.SpinCtrl(parent = self, id = wx.ID_ANY, value = "",
+                             size = (50, -1), style = wx.SP_ARROW_KEYS)
+        ptsize.SetRange(1, 10)
+        ptsize.SetValue(self.properties['marker']['size'])
+        self.wxId['marker']['size'] = ptsize.GetId()
+        gridSizer.Add(item = ptsize, pos = (1, 1))
+        
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Style"))
+        gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (2, 0))
+        ptfill = wx.ComboBox(parent = self, id = wx.ID_ANY,
+                             size = (120, -1), choices = self.ptfilldict.keys(), style = wx.CB_DROPDOWN)
+        ptfill.SetStringSelection(self.properties['marker']['fill'])
+        self.wxId['marker']['fill'] = ptfill.GetId()
+        gridSizer.Add(item = ptfill, pos = (2, 1))
+        
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Legend"))
+        gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (3, 0))
+        ptlegend = wx.TextCtrl(parent = self, id = wx.ID_ANY, value = "", size = (200,-1))
+        ptlegend.SetValue(self.properties['marker']['legend'])
+        self.wxId['marker']['legend'] = ptlegend.GetId()
+        gridSizer.Add(item = ptlegend, pos = (3, 1))
+                
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Type"))
+        gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (4, 0))
+        pttype = wx.ComboBox(parent = self, 
+                             size = (200, -1), choices = self.pttypelist, style = wx.CB_DROPDOWN)
+        pttype.SetStringSelection(self.properties['marker']['type'])
+        self.wxId['marker']['type'] = pttype.GetId()
+        gridSizer.Add(item = pttype, pos = (4, 1))
+
+        boxMainSizer.Add(item = gridSizer, flag = wx.ALL, border = 3)
+        middleSizer.Add(item = boxMainSizer, flag = wx.ALL | wx.EXPAND, border = 3)
+
+        #
+        # axis options
+        #
+        box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                           label = " %s " % _("Axis settings"))
+        boxMainSizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+
+        self.wxId['x-axis'] = {}
+        self.wxId['y-axis'] = {}
+        idx = 0
+        for axis, atype in [(_("X-Axis"), 'x-axis'),
+                     (_("Y-Axis"), 'y-axis')]:
+            box = wx.StaticBox(parent = self, id = wx.ID_ANY,
+                               label = " %s " % axis)
+            boxSizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+            gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+
+            prop = self.properties[atype]['prop']
+            
+            row = 0
+            label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Style"))
+            gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
+            type = wx.ComboBox(parent = self, id = wx.ID_ANY,
+                               size = (100, -1), choices = self.axislist, style = wx.CB_DROPDOWN)
+            type.SetStringSelection(prop['type']) 
+            self.wxId[atype]['type'] = type.GetId()
+            gridSizer.Add(item = type, pos = (row, 1))
+            
+            row += 1
+            label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Custom min"))
+            gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
+            min = wx.TextCtrl(parent = self, id = wx.ID_ANY, value = "", size = (70, -1))
+            min.SetValue(str(prop['min']))
+            self.wxId[atype]['min'] = min.GetId()
+            gridSizer.Add(item = min, pos = (row, 1))
+
+            row += 1
+            label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Custom max"))
+            gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
+            max = wx.TextCtrl(parent = self, id = wx.ID_ANY, value = "", size = (70, -1))
+            max.SetValue(str(prop['max']))
+            self.wxId[atype]['max'] = max.GetId()
+            gridSizer.Add(item = max, pos = (row, 1))
+            
+            row += 1
+            log = wx.CheckBox(parent = self, id = wx.ID_ANY, label = _("Log scale"))
+            log.SetValue(prop['log'])
+            self.wxId[atype]['log'] = log.GetId()
+            gridSizer.Add(item = log, pos = (row, 0), span = (1, 2))
+
+            if idx == 0:
+                flag = wx.ALL | wx.EXPAND
+            else:
+                flag = wx.TOP | wx.BOTTOM | wx.RIGHT | wx.EXPAND
+
+            boxSizer.Add(item = gridSizer, flag = wx.ALL, border = 3)
+            boxMainSizer.Add(item = boxSizer, flag = flag, border = 3)
+
+            idx += 1
+            
+        middleSizer.Add(item = boxMainSizer, flag = wx.ALL | wx.EXPAND, border = 3)
+
+        #
+        # grid & legend options
+        #
+        self.wxId['grid'] = {}
+        self.wxId['legend'] = {}
+        self.wxId['font'] = {}
+        box  =  wx.StaticBox(parent = self, id = wx.ID_ANY,
+                           label = " %s " % _("Grid and Legend settings"))
+        boxMainSizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+
+        row = 0
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Grid color"))
+        gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
+        gridcolor = csel.ColourSelect(parent = self, id = wx.ID_ANY, colour = self.properties['grid']['color'])
+        self.wxId['grid']['color'] = gridcolor.GetId()
+        gridSizer.Add(item = gridcolor, pos = (row, 1))
+
+        row +=1
+        gridshow = wx.CheckBox(parent = self, id = wx.ID_ANY, label = _("Show grid"))
+        gridshow.SetValue(self.properties['grid']['enabled'])
+        self.wxId['grid']['enabled'] = gridshow.GetId()
+        gridSizer.Add(item = gridshow, pos = (row, 0), span = (1, 2))
+
+        row +=1
+        label = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Legend font size"))
+        gridSizer.Add(item = label, flag = wx.ALIGN_CENTER_VERTICAL, pos = (row, 0))
+        legendfontsize = wx.SpinCtrl(parent = self, id = wx.ID_ANY, value = "", 
+                                     size = (50, -1), style = wx.SP_ARROW_KEYS)
+        legendfontsize.SetRange(5,100)
+        legendfontsize.SetValue(int(self.properties['font']['prop']['legendSize']))
+        self.wxId['font']['legendSize'] = legendfontsize.GetId()
+        gridSizer.Add(item = legendfontsize, pos = (row, 1))
+
+        row += 1
+        legendshow  =  wx.CheckBox(parent = self, id = wx.ID_ANY, label = _("Show legend"))
+        legendshow.SetValue(self.properties['legend']['enabled'])
+        self.wxId['legend']['enabled'] = legendshow.GetId()
+        gridSizer.Add(item = legendshow, pos = (row, 0), span = (1, 2))
+
+        boxMainSizer.Add(item = gridSizer, flag = flag, border = 3)
+
+        middleSizer.Add(item = boxMainSizer, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
+
+        sizer.Add(item = middleSizer, flag = wx.ALL, border = 0)
+        
+        #
+        # line & buttons
+        #
+        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 = 3)
+
+        #
+        # buttons
+        #
+        btnSave = wx.Button(self, wx.ID_SAVE)
+        btnApply = wx.Button(self, wx.ID_APPLY)
+        btnCancel = wx.Button(self, wx.ID_CANCEL)
+        btnSave.SetDefault()
+
+        # bindigs
+        btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
+        btnApply.SetToolTipString(_("Apply changes for the current session"))
+        btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
+        btnSave.SetToolTipString(_("Apply and save changes to user settings file (default for next sessions)"))
+        btnSave.SetDefault()
+        btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+        btnCancel.SetToolTipString(_("Close dialog and ignore changes"))
+
+        # sizers
+        btnStdSizer = wx.StdDialogButtonSizer()
+        btnStdSizer.AddButton(btnCancel)
+        btnStdSizer.AddButton(btnSave)
+        btnStdSizer.AddButton(btnApply)
+        btnStdSizer.Realize()
+        
+        sizer.Add(item = btnStdSizer, proportion = 0, flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
+
+        self.SetSizer(sizer)
+        sizer.Fit(self)
+
+    def UpdateSettings(self):
+        idx = 0
+        for r in self.raster.itervalues():
+            r['prop']['pcolor'] = self.FindWindowById(self.wxId['pcolor'][idx]).GetColour()
+            r['prop']['pwidth'] = int(self.FindWindowById(self.wxId['pwidth'][idx]).GetValue())
+            r['prop']['pstyle'] = self.FindWindowById(self.wxId['pstyle'][idx]).GetStringSelection()
+            r['plegend'] = self.FindWindowById(self.wxId['plegend'][idx]).GetValue()
+            idx +=1
+
+        self.properties['marker']['color'] = self.FindWindowById(self.wxId['marker']['color']).GetColour()
+        self.properties['marker']['fill'] = self.FindWindowById(self.wxId['marker']['fill']).GetStringSelection()
+        self.properties['marker']['size'] = self.FindWindowById(self.wxId['marker']['size']).GetValue()
+        self.properties['marker']['type'] = self.FindWindowById(self.wxId['marker']['type']).GetValue()
+        self.properties['marker']['legend'] = self.FindWindowById(self.wxId['marker']['legend']).GetValue()
+
+        for axis in ('x-axis', 'y-axis'):
+            self.properties[axis]['prop']['type'] = self.FindWindowById(self.wxId[axis]['type']).GetValue()
+            self.properties[axis]['prop']['min'] = float(self.FindWindowById(self.wxId[axis]['min']).GetValue())
+            self.properties[axis]['prop']['max'] = float(self.FindWindowById(self.wxId[axis]['max']).GetValue())
+            self.properties[axis]['prop']['log'] = self.FindWindowById(self.wxId[axis]['log']).IsChecked()
+
+        self.properties['grid']['color'] = self.FindWindowById(self.wxId['grid']['color']).GetColour()
+        self.properties['grid']['enabled'] = self.FindWindowById(self.wxId['grid']['enabled']).IsChecked()
+
+        self.properties['font']['prop']['legendSize'] = self.FindWindowById(self.wxId['font']['legendSize']).GetValue()
+        self.properties['legend']['enabled'] = self.FindWindowById(self.wxId['legend']['enabled']).IsChecked()
+
+    def OnSave(self, event):
+        """!Button 'Save' pressed"""
+        self.UpdateSettings()
+        fileSettings = {}
+        UserSettings.ReadSettingsFile(settings = fileSettings)
+        fileSettings['profile'] = UserSettings.Get(group = 'profile')
+        file = UserSettings.SaveToFile(fileSettings)
+        self.parent.parent.GetLayerManager().goutput.WriteLog(_('Profile settings saved to file \'%s\'.') % file)
+        self.parent.SetGraphStyle()
+        if self.parent.profile:
+            self.parent.DrawPlot()
+        self.Close()
+
+    def OnApply(self, event):
+        """!Button 'Apply' pressed. Does not close dialog"""
+        self.UpdateSettings()
+        self.parent.SetGraphStyle()
+        if self.parent.profile:
+            self.parent.DrawPlot()
+        
+    def OnCancel(self, event):
+        """!Button 'Cancel' pressed"""
+        self.Close()


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

Added: grass/branches/releasebranch_6_4/gui/wxpython/wxplot/profile.py
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/wxplot/profile.py	                        (rev 0)
+++ grass/branches/releasebranch_6_4/gui/wxpython/wxplot/profile.py	2012-02-19 20:31:20 UTC (rev 50882)
@@ -0,0 +1,428 @@
+"""!
+ at package wxplot.profile
+
+ at brief Profiling using PyPlot
+
+Classes:
+ - profile::ProfileFrame
+ - profile::ProfileToolbar
+
+(C) 2011 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Michael Barton, Arizona State University
+"""
+
+import os
+import sys
+import math
+
+import wx
+import wx.lib.plot as plot
+
+import grass.script as grass
+
+try:
+    import numpy
+except ImportError:
+    msg = _("This module requires the NumPy module, which could not be "
+            "imported. It probably is not installed (it's not part of the "
+            "standard Python distribution). See the Numeric Python site "
+            "(http://numpy.scipy.org) for information on downloading source or "
+            "binaries.")
+    print >> sys.stderr, "wxplot.py: " + msg
+
+from wxplot.base       import BasePlotFrame, PlotIcons
+from gui_core.toolbars import BaseToolbar, BaseIcons
+from wxplot.dialogs    import ProfileRasterDialog, PlotStatsFrame
+from core.gcmd         import RunCommand
+
+class ProfileFrame(BasePlotFrame):
+    """!Mainframe for displaying profile of one or more raster maps. Uses wx.lib.plot.
+    """
+    def __init__(self, parent, id = wx.ID_ANY, style = wx.DEFAULT_FRAME_STYLE,
+                 size = wx.Size(700, 400),
+                 rasterList = [], **kwargs):
+        BasePlotFrame.__init__(self, parent, size = size, **kwargs)
+
+        self.toolbar = ProfileToolbar(parent = self)
+        self.SetToolBar(self.toolbar)
+        self.SetTitle(_("GRASS Profile Analysis Tool"))
+        
+        #
+        # Init variables
+        #
+        self.rasterList = rasterList
+        self.plottype = 'profile'
+        self.coordstr = ''              # string of coordinates for r.profile
+        self.seglist = []               # segment endpoint list
+        self.ppoints = ''               # segment endpoints data         
+        self.transect_length = 0.0      # total transect length        
+        self.ptitle = _('Profile of')   # title of window
+        self.colorList =  ["blue", "red", "green", "yellow", "magenta", "cyan",
+                           "aqua", "black", "grey", "orange", "brown", "purple", "violet",
+                           "indigo"]
+        
+        if len(self.rasterList) > 0: # set raster name(s) from layer manager if a map is selected
+            self.raster = self.InitRasterOpts(self.rasterList, self.plottype)
+        else:
+            self.raster = {}
+        
+        self._initOpts()
+        
+        # determine units (axis labels)
+        if self.parent.Map.projinfo['units'] != '':
+            self.xlabel = _('Distance (%s)') % self.parent.Map.projinfo['units']
+        else:
+            self.xlabel = _("Distance along transect")
+        self.ylabel = _("Cell values")
+        
+    def _initOpts(self):
+        """!Initialize plot options
+        """
+        self.InitPlotOpts('profile')
+
+    def OnDrawTransect(self, event):
+        """!Draws transect to profile in map display
+        """
+        self.mapwin.polycoords = []
+        self.seglist = []
+        self.mapwin.ClearLines(self.mapwin.pdc)
+        self.ppoints = ''
+
+        self.parent.SetFocus()
+        self.parent.Raise()
+        
+        self.mapwin.mouse['use'] = 'profile'
+        self.mapwin.mouse['box'] = 'line'
+        self.mapwin.pen = wx.Pen(colour = 'Red', width = 2, style = wx.SHORT_DASH)
+        self.mapwin.polypen = wx.Pen(colour = 'dark green', width = 2, style = wx.SHORT_DASH)
+        self.mapwin.SetCursor(self.Parent.cursors["cross"])
+
+    def OnSelectRaster(self, event):
+        """!Select raster map(s) to profile
+        """
+        dlg = ProfileRasterDialog(parent = self)
+
+        if dlg.ShowModal() == wx.ID_OK:
+            self.rasterList = dlg.rasterList
+            self.raster = self.InitRasterOpts(self.rasterList, self.plottype)
+            
+            # plot profile
+            if len(self.mapwin.polycoords) > 0 and len(self.rasterList) > 0:
+                self.OnCreateProfile(event = None)
+
+        dlg.Destroy()
+
+    def SetupProfile(self):
+        """!Create coordinate string for profiling. Create segment list for
+           transect segment markers.
+        """
+
+        #
+        # create list of coordinate points for r.profile
+        #                
+        dist = 0
+        cumdist = 0
+        self.coordstr = ''
+        lasteast = lastnorth = None
+        
+        if len(self.mapwin.polycoords) > 0:
+            for point in self.mapwin.polycoords:
+                # build string of coordinate points for r.profile
+                if self.coordstr == '':
+                    self.coordstr = '%d,%d' % (point[0], point[1])
+                else:
+                    self.coordstr = '%s,%d,%d' % (self.coordstr, point[0], point[1])
+
+        if len(self.rasterList) == 0:
+            return
+
+        # title of window
+        self.ptitle = _('Profile of')
+
+        #
+        # create list of coordinates for transect segment markers
+        #
+        if len(self.mapwin.polycoords) > 0:
+            self.seglist = []
+            for point in self.mapwin.polycoords:
+                # get value of raster cell at coordinate point
+                ret = RunCommand('r.what',
+                                 parent = self,
+                                 read = True,
+                                 input = self.rasterList[0],
+                                 east_north = '%d,%d' % (point[0],point[1]))
+                
+                val = ret.splitlines()[0].split('|')[3]
+                if val == None or val == '*': continue
+                val = float(val)
+                
+                # calculate distance between coordinate points
+                if lasteast and lastnorth:
+                    dist = math.sqrt(math.pow((lasteast-point[0]),2) + math.pow((lastnorth-point[1]),2))
+                cumdist += dist
+                
+                #store total transect length
+                self.transect_length = cumdist
+
+                # build a list of distance,value pairs for each segment of transect
+                self.seglist.append((cumdist,val))
+                lasteast = point[0]
+                lastnorth = point[1]
+
+            # delete extra first segment point
+            try:
+                self.seglist.pop(0)
+            except:
+                pass
+
+        #
+        # create datalist of dist/value pairs and y labels for each raster map
+        #    
+        self.ylabel = ''
+        i = 0
+        
+        for r in self.raster.iterkeys():
+            self.raster[r]['datalist'] = []
+            datalist = self.CreateDatalist(r, self.coordstr)
+            if len(datalist) > 0:   
+                self.raster[r]['datalist'] = datalist
+
+                # update ylabel to match units if they exist           
+                if self.raster[r]['units'] != '':
+                    self.ylabel += '%s (%d),' % (r['units'], i)
+                i += 1
+
+                # update title
+                self.ptitle += ' %s ,' % r.split('@')[0]
+
+        self.ptitle = self.ptitle.rstrip(',')
+            
+        if self.ylabel == '':
+            self.ylabel = _('Raster values')
+        else:
+            self.ylabel = self.ylabel.rstrip(',')
+
+    def CreateDatalist(self, raster, coords):
+        """!Build a list of distance, value pairs for points along transect using r.profile
+        """
+        datalist = []
+        
+        # keep total number of transect points to 500 or less to avoid 
+        # freezing with large, high resolution maps
+        region = grass.region()
+        curr_res = min(float(region['nsres']),float(region['ewres']))
+        transect_rec = 0
+        if self.transect_length / curr_res > 500:
+            transect_res = self.transect_length / 500
+        else: transect_res = curr_res
+        
+        ret = RunCommand("r.profile",
+                         parent = self,
+                         input = raster,
+                         profile = coords,
+                         res = transect_res,
+                         null = "nan",
+                         quiet = True,
+                         read = True)
+        
+        if not ret:
+            return []
+            
+        for line in ret.splitlines():
+            dist, elev = line.strip().split(' ')
+            if dist == None or dist == '' or dist == 'nan' or \
+                elev == None or elev == '' or elev == 'nan':
+                continue
+            dist = float(dist)
+            elev = float(elev)
+            datalist.append((dist,elev))
+
+        return datalist
+
+    def OnCreateProfile(self, event):
+        """!Main routine for creating a profile. Uses r.profile to
+        create a list of distance,cell value pairs. This is passed to
+        plot to create a line graph of the profile. If the profile
+        transect is in multiple segments, these are drawn as
+        points. Profile transect is drawn, using methods in mapdisp.py
+        """
+            
+        if len(self.mapwin.polycoords) == 0 or len(self.rasterList) == 0:
+            dlg = wx.MessageDialog(parent = self,
+                                   message = _('You must draw a transect to profile in the map display window.'),
+                                   caption = _('Nothing to profile'),
+                                   style = wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+            dlg.ShowModal()
+            dlg.Destroy()
+            return
+
+        self.mapwin.SetCursor(self.parent.cursors["default"])
+        self.SetCursor(self.parent.cursors["default"])
+        self.SetGraphStyle()
+        self.SetupProfile()
+        p = self.CreatePlotList()
+        self.DrawPlot(p)
+
+        # reset transect
+        self.mapwin.mouse['begin'] = self.mapwin.mouse['end'] = (0.0,0.0)
+        self.mapwin.mouse['use'] = 'pointer'
+        self.mapwin.mouse['box'] = 'point'
+
+    def CreatePlotList(self):
+        """!Create a plot data list from transect datalist and
+            transect segment endpoint coordinates.
+        """
+        # graph the distance, value pairs for the transect
+        self.plotlist = []
+
+        # Add segment marker points to plot data list
+        if len(self.seglist) > 0 :
+            self.ppoints = plot.PolyMarker(self.seglist,
+                                           legend = ' ' + self.properties['marker']['legend'],
+                                           colour = wx.Color(self.properties['marker']['color'][0],
+                                                           self.properties['marker']['color'][1],
+                                                           self.properties['marker']['color'][2],
+                                                           255),
+                                           size = self.properties['marker']['size'],
+                                           fillstyle = self.ptfilldict[self.properties['marker']['fill']],
+                                           marker = self.properties['marker']['type'])
+            self.plotlist.append(self.ppoints)
+
+        # Add profile distance/elevation pairs to plot data list for each raster profiled
+        for r in self.rasterList:
+            col = wx.Color(self.raster[r]['pcolor'][0],
+                           self.raster[r]['pcolor'][1],
+                           self.raster[r]['pcolor'][2],
+                           255)
+            self.raster[r]['pline'] = plot.PolyLine(self.raster[r]['datalist'],
+                                                    colour = col,
+                                                    width = self.raster[r]['pwidth'],
+                                                    style = self.linestyledict[self.raster[r]['pstyle']],
+                                                    legend = self.raster[r]['plegend'])
+
+            self.plotlist.append(self.raster[r]['pline'])
+
+        if len(self.plotlist) > 0:        
+            return self.plotlist
+        else:
+            return None
+
+    def Update(self):
+        """!Update profile after changing options
+        """
+        self.SetGraphStyle()
+        p = self.CreatePlotList()
+        self.DrawPlot(p)
+
+    def SaveProfileToFile(self, event):
+        """!Save r.profile data to a csv file
+        """    
+        wildcard = _("Comma separated value (*.csv)|*.csv")
+        
+        dlg = wx.FileDialog(parent = self,
+                            message = _("Path and prefix (for raster name) to save profile values..."),
+                            defaultDir = os.getcwd(), 
+                            defaultFile = "",  wildcard = wildcard, style = wx.SAVE)
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            
+            for r in self.rasterList:
+                pfile = path+'_'+str(r['name'])+'.csv'
+                try:
+                    file = open(pfile, "w")
+                except IOError:
+                    wx.MessageBox(parent = self,
+                                  message = _("Unable to open file <%s> for writing.") % pfile,
+                                  caption = _("Error"), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                    return False
+                for datapair in self.raster[r]['datalist']:
+                    file.write('%d,%d\n' % (float(datapair[0]),float(datapair[1])))
+                                        
+                file.close()
+
+        dlg.Destroy()
+        
+    def OnStats(self, event):
+        """!Displays regression information in messagebox
+        """
+        message = []
+        title = _('Statistics for Profile(s)')
+
+        for r in self.raster.iterkeys():
+            try:
+                rast = r.split('@')[0] 
+                statstr = 'Profile of %s\n\n' % rast
+
+                iterable = (i[1] for i in self.raster[r]['datalist'])            
+                a = numpy.fromiter(iterable, numpy.float)
+                
+                statstr += 'n: %f\n' % a.size
+                statstr += 'minimum: %f\n' % numpy.amin(a)
+                statstr += 'maximum: %f\n' % numpy.amax(a)
+                statstr += 'range: %f\n' % numpy.ptp(a)
+                statstr += 'mean: %f\n' % numpy.mean(a)
+                statstr += 'standard deviation: %f\n' % numpy.std(a)
+                statstr += 'variance: %f\n' % numpy.var(a)
+                cv = numpy.std(a)/numpy.mean(a)
+                statstr += 'coefficient of variation: %f\n' % cv
+                statstr += 'sum: %f\n' % numpy.sum(a)
+                statstr += 'median: %f\n' % numpy.median(a)
+                statstr += 'distance along transect: %f\n\n' % self.transect_length       
+                message.append(statstr)
+            except:
+                pass
+            
+        stats = PlotStatsFrame(self, id = wx.ID_ANY, message = message, 
+                               title = title)
+
+        if stats.Show() == wx.ID_CLOSE:
+            stats.Destroy()       
+
+    
+class ProfileToolbar(BaseToolbar):
+    """!Toolbar for profiling raster map
+    """ 
+    def __init__(self, parent):
+        BaseToolbar.__init__(self, parent)
+        
+        self.InitToolbar(self._toolbarData())
+        
+        # realize the toolbar
+        self.Realize()
+        
+    def _toolbarData(self):
+        """!Toolbar data"""
+        return self._getToolbarData((('addraster', BaseIcons["addRast"],
+                                      self.parent.OnSelectRaster),
+                                     ('transect', PlotIcons["transect"],
+                                      self.parent.OnDrawTransect),
+                                     (None, ),
+                                     ('draw', PlotIcons["draw"],
+                                      self.parent.OnCreateProfile),
+                                     ('erase', BaseIcons["erase"],
+                                      self.parent.OnErase),
+                                     ('drag', BaseIcons['pan'],
+                                      self.parent.OnDrag),
+                                     ('zoom', BaseIcons['zoomIn'],
+                                      self.parent.OnZoom),
+                                     ('unzoom', BaseIcons['zoomBack'],
+                                      self.parent.OnRedraw),
+                                     (None, ),
+                                     ('statistics', PlotIcons['statistics'],
+                                      self.parent.OnStats),
+                                     ('datasave', PlotIcons["save"],
+                                      self.parent.SaveProfileToFile),
+                                     ('image', BaseIcons["saveFile"],
+                                      self.parent.SaveToFile),
+                                     ('print', BaseIcons["print"],
+                                      self.parent.PrintMenu),
+                                     (None, ),
+                                     ('settings', PlotIcons["options"],
+                                      self.parent.PlotOptionsMenu),
+                                     ('quit', PlotIcons["quit"],
+                                      self.parent.OnQuit),
+                                     ))


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

Modified: grass/branches/releasebranch_6_4/gui/wxpython/wxpythonlib.dox
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/wxpythonlib.dox	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/wxpythonlib.dox	2012-02-19 20:31:20 UTC (rev 50882)
@@ -1,4 +1,4 @@
-/*! \page wxpythonlib GRASS WxPython-based Graphical User Interface
+/*! \page wxpythonlib GRASS wxPython-based GUI
 
 The GUI (Graphical User Interface) is written in the Python
 programming language using <a
@@ -24,17 +24,19 @@
 - \ref background
 - \ref classes
  - \ref core
+ - \ref gui_core
  - \ref lmgr
  - \ref mapdisp
  - \ref wscreen
- - \ref atm
- - \ref georect
+ - \ref dbmgr
+ - \ref gpc
  - \ref gmodeler
  - \ref vdigit
  - \ref wxnviz
+ - \ref psmap
  - \ref locWizard
- - \ref mcalc
- - \ref misc
+ - \ref plot
+ - \ref other
 - \ref devel
 - \ref seeAlso
 - \ref refs
@@ -52,322 +54,425 @@
 
 \subsection core Core modules
 
-- gui_modules/debug.py
- - gui_modules::debug::DebugMsg
+- core::debug
+ - debug::DebugMsg
+- core::globalvar
+- core::gcmd
+ - gcmd::GError
+ - gcmd::GWarning
+ - gcmd::GMessage
+ - gcmd::GException
+ - gcmd::Popen
+ - gcmd::Command
+ - gcmd::CommandThread
+- core::menudata
+ - menudata::MenuData
+- core::render
+ - render::Layer
+ - render::Layer
+ - render::MapLayer
+ - render::Overlay
+ - render::Map
+- core::settings
+ - settings::Settings
+- core::units
+ - units::BaseUnits
+- core::utils
+- core::workspace
+ - workspace::ProcessWorkspaceFile
+ - workspace::WriteWorkspaceFile
+ - workspace::ProcessGrcFile
 
-- gui_modules/gcmd.py
- - gui_modules::gcmd::GError
- - gui_modules::gcmd::GWarning
- - gui_modules::gcmd::GMessage
- - gui_modules::gcmd::GException
- - gui_modules::gcmd::Popen
- - gui_modules::gcmd::Command
- - gui_modules::gcmd::CommandThread
+\subsection gui_core GUI core modules
 
-- gui_modules/globalvar.py
+- gui_core::dialogs
+ - dialogs::ElementDialog
+ - dialogs::LocationDialog
+ - dialogs::MapsetDialog
+ - dialogs::NewVectorDialog
+ - dialogs::SavedRegion
+ - dialogs::DecorationDialog
+ - dialogs::TextLayerDialog 
+ - dialogs::GroupDialog
+ - dialogs::MapLayersDialog
+ - dialogs::ImportDialog
+ - dialogs::GdalImportDialog
+ - dialogs::GdalOutputDialog
+ - dialogs::DxfImportDialog
+ - dialogs::LayersList (used by MultiImport) 
+ - dialogs::SetOpacityDialog
+ - dialogs::StaticWrapText
+ - dialogs::ImageSizeDialog
+ - dialogs::SqlQueryFrame
+- gui_core::forms
+ - forms::TaskFrame
+ - forms::CmdPanel
+ - forms::GrassGUIApp
+- gui_core::ghelp
+ - ghelp::SearchModuleWindow
+ - ghelp::MenuTreeWindow
+ - ghelp::MenuTree
+ - ghelp::AboutWindow
+ - ghelp::HelpFrame
+ - ghelp::HelpWindow
+ - ghelp::HelpPanel
+- gui_core::goutput
+ - goutput::CmdThread
+ - goutput::GMConsole
+ - goutput::GMStc
+ - goutput::GMStdout
+ - goutput::GMStderr
+- gui_core::gselect
+ - gselect::Select
+ - gselect::VectorSelect
+ - gselect::TreeCrtlComboPopup
+ - gselect::VectorDBInfo
+ - gselect::LayerSelect
+ - gselect::DriverSelect
+ - gselect::DatabaseSelect
+ - gselect::ColumnSelect
+ - gselect::DbaseSelect
+ - gselect::LocationSelect
+ - gselect::MapsetSelect
+ - gselect::SubGroupSelect
+ - gselect::FormatSelect
+ - gselect::GdalSelect
+ - gselect::ProjSelect
+ - gselect::ElementSelect
+ - gselect::OgrTypeSelect
+- gui_core::mapdisp
+ - mapdisp::MapFrameBase
+- gui_core::mapwindow
+ - mapwindow::MapWindow
+- gui_core::menu
+ - menu::Menu
+- gui_core::preferences
+ - preferences::PreferencesBaseDialog
+ - preferences::PreferencesDialog
+ - preferences::DefaultFontDialog
+ - preferences::MapsetAccess
+- gui_core::prompt
+ - prompt::PromptListCtrl
+ - prompt::TextCtrlAutoComplete
+ - prompt::GPrompt
+ - prompt::GPromptPopUp
+ - prompt::GPromptSTC
+- gui_core::toolbars
+ - toolbars::BaseToolbar
+- gui_core::widgets
+ - widgets::ScrolledPanel
+ - widgets::NTCValidator
+ - widgets::NumTextCtrl
+ - widgets::FloatSlider
+ - widgets::SymbolButton
+ - widgets::StaticWrapText
+ - widgets::BaseValidator
+ - widgets::IntegerValidator
+ - widgets::FloatValidator
+ - widgets::NTCValidator
+ - widgets::ItemTree
 
-- gui_modules/gselect.py
- - gui_modules::gselect::Select
- - gui_modules::gselect::VectorSelect
- - gui_modules::gselect::TreeCtrlComboPopup
- - gui_modules::gselect::VectorDBInfo
- - gui_modules::gselect::LayerSelect
- - gui_modules::gselect::LayerNameSelect
- - gui_modules::gselect::DriverSelect
- - gui_modules::gselect::DatabaseSelect
- - gui_modules::gselect::TableSelect
- - gui_modules::gselect::ColumnSelect
- - gui_modules::gselect::LocationSelect
- - gui_modules::gselect::MapsetSelect
- - gui_modules::gselect::SubGroupSelect
- - gui_modules::gselect::FormatSelect
- - gui_modules::gselect::GdalSelect
-
-- gui_modules/menuform.py
- - gui_modules::menuform::UpdateThread
- - gui_modules::menuform::UpdateQThread
- - gui_modules::menuform::grassTask
- - gui_modules::menuform::processTask
- - gui_modules::menuform::mainFrame
- - gui_modules::menuform::cmdPanel
- - gui_modules::menuform::GrassGUIApp
- - gui_modules::menuform::GUI
- - gui_modules::menuform::FloatValidator
-
-- gui_modules/units.py
- - gui_modules::units::BaseUnits
-
-- gui_modules/utils.py
-
 \subsection lmgr Layer Manager
 
-- wxgui.py
+- wxgui
  - wxgui::GMFrame
  - wxgui::GMApp
+ - wxgui::Usage
+- lmgr::layertree
+ - lmgr::LayerTree
+- lmgr::menudata
+ - menudata::MenuData
+- lmgr::pyshell
+ - pyshell::PyShellWindow
+- lmgr::toolbars
+ - toolbars::LMWorkspaceToolbar
+ - toolbars::LMDataToolbar
+ - toolbars::LMToolsToolbar
+ - toolbars::LMMiscToolbar
+ - toolbars::LMVectorToolbar
+ - toolbars::LMNvizToolbar
 
-- gui_modules/layertree.py
- - gui_modules::layertree::LayerTree
-
-- gui_modules/goutput.py
- - gui_modules::goutput::CmdThread
- - gui_modules::goutput::GMConsole
- - gui_modules::goutput::GMStdout
- - gui_modules::goutput::GMStderr
- - gui_modules::goutput::GMStc
-
-- gui_modules/ghelp.py
- - gui_modules::ghelp::HelpFrame
- - gui_modules::ghelp::SearchModuleWindow
- - gui_modules::ghelp::MenuTreeWindow
- - gui_modules::ghelp::ItemTree
- - gui_modules::ghelp::MenuTree
- - gui_modules::ghelp::AboutWindow
- - gui_modules::ghelp::InstallExtensionWindow
- - gui_modules::ghelp::ExtensionTree
- - gui_modules::ghelp::HelpWindow
- - gui_modules::ghelp::HelpPanel
-
-- gui_modules/menudata.py
- - gui_modules::menudata::MenuData
- - gui_modules::menudata::ManagerData
- - gui_modules::menudata::ModelerData
-
-- gui_modules/menu.py
- - gui_modules::menu::Menu
-
-- gui_modules/preferences.py
- - gui_modules::preferences::Settings
- - gui_modules::preferences::PreferencesBaseDialog
- - gui_modules::preferences::PreferencesDialog
- - gui_modules::preferences::DefaultFontDialog
- - gui_modules::preferences::MapsetAccess
- - gui_modules::preferences::CheckListMapset
-
-- gui_modules/prompt.py
- - gui_modules::prompt::PromptListCtrl
- - gui_modules::prompt::TextCtrlAutoComplete
- - gui_modules::prompt::GPrompt
- - gui_modules::prompt::GPromptPopUp
- - gui_modules::prompt::GPromptSTC
-
 \subsection mapdisp Map Display Window
 
-- gui_modules/disp_print.py
- - gui_modules::disp_print::MapPrint
- - gui_modules::disp_print::PrintOptions
+- mapdisp::frame
+ - mapdisp::MapFrame
+ - mapdisp::MapApp
+- mapdisp::gprint
+ - gprint::MapPrint
+ - gprint::PrintOptions
+- mapdisp::mapwindow
+ - mapwindow::BufferedWindow
+- mapdisp::statusbar
+ - statusbar::SbException
+ - statusbar::SbManager
+ - statusbar::SbItem
+ - statusbar::SbRender
+ - statusbar::SbShowRegion
+ - statusbar::SbAlignExtent
+ - statusbar::SbResolution
+ - statusbar::SbMapScale
+ - statusbar::SbGoTo
+ - statusbar::SbProjection
+ - statusbar::SbMask
+ - statusbar::SbTextItem
+ - statusbar::SbDisplayGeometry
+ - statusbar::SbCoordinates
+ - statusbar::SbRegionExtent
+ - statusbar::SbCompRegionExtent
+ - statusbar::SbProgress
+- mapdisp::toolbars
+ - toolbars::MapToolbar
 
-- gui_modules/mapdisp_command.py
- - gui_modules::mapdisp_command::Command
-
-- gui_modules/mapdisp.py
- - gui_modules::mapdisp::MapFrame
- - gui_modules::mapdisp::MapApp
-
-- gui_modules/mapdisp_window.py
- - gui_modules::mapdisp_window::MapWindow
- - gui_modules::mapdisp_window::BufferedWindow
-
-- gui_modules/render.py
- - gui_modules::render::Layer
- - gui_modules::render::MapLayer
- - gui_modules::render::Overlay
- - gui_modules::render::Map
-
-- gui_modules/toolbars.py
- - gui_modules::toolbars::AbstractToolbar
- - gui_modules::toolbars::MapToolbar
- - gui_modules::toolbars::GCPManToolbar
- - gui_modules::toolbars::GCPDisplayToolbar
- - gui_modules::toolbars::GRToolbar
- - gui_modules::toolbars::GCPToolbar
- - gui_modules::toolbars::VDigitToolbar
- - gui_modules::toolbars::ProfileToolbar
- - gui_modules::toolbars::NvizToolbar
- - gui_modules::toolbars::ModelToolbar
- - gui_modules::toolbars::HistogramToolbar
- - gui_modules::toolbars::LayerManagerToolbar
-
 \subsection wscreen Welcome screen
 
-- gis_set_error.py
-- gis_set.py
+- gis_set_error
+- gis_set
  - gis_set::GRASSStartup
  - gis_set::StartUp
  - gis_set::GListBox
 
-\subsection atm Attribute Table Manager
+\subsection dbmgr Database Manager
 
-- gui_modules/dbm_base.py
- - gui_modules::dbm_base::VectorDBInfo
+- dbmgr::dialogs
+ - dialogs::DisplayAttributesDialog
+ - dialogs::ModifyTableRecord
+- dbmgr::manager
+ - manager::Log
+ - manager::VirtualAttributeList
+ - manager::AttributeManager
+ - manager::TableListCtrl
+ - manager::LayerListCtrl
+ - manager::LayerBook
+- dbmgr::sqlbuilder
+ - sqlbuilder::SQLFrame
+- dbmgr::vinfo
+ - vinfo::VectorDBInfo
 
-- gui_modules/dbm_dialogs.py
- - gui_modules::dbm_dialogs::DisplayAttributesDialog
- - gui_modules::dbm_dialogs::ModifyTableRecord
+\subsection gpc Georectifier
 
-- gui_modules/dbm.py
- - gui_modules::dbm::Log
- - gui_modules::dbm::VirtualAttributeList
- - gui_modules::dbm::AttributeManager
- - gui_modules::dbm::TableListCtrl
- - gui_modules::dbm::LayerListCtrl
- - gui_modules::dbm::LayerBook
+- gcp::manager
+ - manager::GCPWizard
+ - manager::LocationPage
+ - manager::GroupPage
+ - manager::DispMapPage
+ - manager::GCP
+ - manager::GCPList
+ - manager::VectGroup
+ - manager::EditGCP
+ - manager::GrSettingsDialog
+- gcp::mapdisplay
+- mapdisplay::MapFrame
+- gcp::toolbars
+ - toolbars::GCPMapToolbar
+ - toolbars::GCPDisplayToolbar
 
-- gui_modules/sqlbuilder.py
- - gui_modules::sqlbuilder::SQLFrame
-
-\subsection georect Georectifier
-
-- gui_modules/gcpmanager.py
- - gui_modules::gcpmanager::GCPWizard
- - gui_modules::gcpmanager::LocationPage
- - gui_modules::gcpmanager::GroupPage
- - gui_modules::gcpmanager::DispMapPage
- - gui_modules::gcpmanager::GCP
- - gui_modules::gcpmanager::GCPList
- - gui_modules::gcpmanager::VectGroup
- - gui_modules::gcpmanager::EditGCP
- - gui_modules::gcpmanager::GrSettingsDialog
-
-- gui_modules/gcpmapdisp.py
- - gui_modules::gcpmapdisp::MapFrame
-
 \subsection gmodeler Graphical Modeler
 
-- gui_modules/gmodeler.py
- - gui_modules::gmodeler::Model
- - gui_modules::gmodeler::ModelFrame
- - gui_modules::gmodeler::ModelCanvas
- - gui_modules::gmodeler::ModelObject
- - gui_modules::gmodeler::ModelAction
- - gui_modules::gmodeler::ModelData
- - gui_modules::gmodeler::ModelDataDialog
- - gui_modules::gmodeler::ModelEvtHandler
- - gui_modules::gmodeler::ModelSearchDialog
- - gui_modules::gmodeler::ModelRelation
- - gui_modules::gmodeler::ProcessModelFile
- - gui_modules::gmodeler::WriteModelFile
- - gui_modules::gmodeler::PreferencesDialog
- - gui_modules::gmodeler::PropertiesDialog
- - gui_modules::gmodeler::ModelParamDialog
- - gui_modules::gmodeler::ModelListCtrl
- - gui_modules::gmodeler::VariablePanel
- - gui_modules::gmodeler::VariableListCtrl
- - gui_modules::gmodeler::ModelItem
- - gui_modules::gmodeler::ModelItemDialog
- - gui_modules::gmodeler::ModelLoop
- - gui_modules::gmodeler::ModelLoopDialog
- - gui_modules::gmodeler::ItemPanel
- - gui_modules::gmodeler::ItemListCtrl
- - gui_modules::gmodeler::ItemCheckListCtrl
- - gui_modules::gmodeler::ModelCondition
- - gui_modules::gmodeler::ModelConditionDialog
- - gui_modules::gmodeler::WritePythonFile
+- gmodeler::dialogs
+ - dialogs::ModelDataDialog
+ - dialogs::ModelSearchDialog
+ - dialogs::ModelRelationDialog
+ - dialogs::ModelItemDialog
+ - dialogs::ModelLoopDialog
+ - dialogs::ModelConditionDialog
+ - dialogs::ModelListCtrl
+ - dialogs::ValiableListCtrl
+ - dialogs::ItemListCtrl
+ - dialogs::ItemCheckListCtrl
+- gmodeler::frame
+ - frame::ModelToolbar
+ - frame::ModelFrame
+ - frame::ModelCanvas
+ - frame::ModelEvtHandler
+ - frame::VariablePanel
+ - frame::ItemPanel
+- gmodeler::menudata
+ - menudata::ModelerData
+- gmodeler::model
+ - model::Model
+ - model::ModelObject
+ - model::ModelAction
+ - model::ModelData
+ - model::ModelRelation
+ - model::ModelItem
+ - model::ModelLoop
+ - model::ModelCondition
+ - model::ProcessModelFile
+ - model::WriteModelFile
+ - model::WritePythonFile
+ - model::ModelParamDialog
+- gmodeler::preferences
+ - preferences::PreferencesDialog
+ - preferences::PropertiesDialog
 
 \subsection vdigit Vector digitizer
 
-- gui_modules/vdigit.py
- - gui_modules::vdigit::VDigit
- - gui_modules::vdigit::VDigitSettingsDialog
- - gui_modules::vdigit::VDigitCategoryDialog
- - gui_modules::vdigit::CategoryListCtrl
- - gui_modules::vdigit::VDigitZBulkDialog
- - gui_modules::vdigit::VDigitDuplicatesDialog
- - gui_modules::vdigit::CheckListFeature
+- vdigit::dialogs
+ - dialogs::VDigitCategoryDialog
+ - dialogs::CategoryListCtrl
+ - dialogs::VDigitZBulkDialog
+ - dialogs::VDigitDuplicatesDialog
+ - dialogs::CheckListFeature
+- vdigit::main
+ - main::VDigit
+- vdigit::mapwindow
+ - mapwindow::VDigitWindow
+- vdigit::preferences
+ - preferences::VDigitSettingsDialog
+- vdigit::toolbars
+ - toolbars::VDigitToolbar
+- vdigit::wxvdigit
+ - wxdigit::VDigitError
+ - wxdigit::IVDigit
+- vdigit::wxdisplay
+ - wxdisplay::DisplayDriver
 
-- gui_modules/wxvdriver.py
- - gui_modules::wxvdriver::DisplayDriver
- 
-- gui_modules/wxvdigit.py
- - gui_modules::wxvdigit::VDigitError
- - gui_modules::wxvdigit::IVDigit
-
 \subsection wxnviz 3D view mode (wxNviz)
 
-- gui_modules/nviz_mapdisp.py
- - gui_modules::nviz_mapdisp::NvizThread
- - gui_modules::nviz_mapdisp::GLWindow
+- nviz::animation
+ - animation::Animation
+- nviz::main
+- nviz::mapwindow
+ - mapwindow::NvizThread
+ - mapwindow::GLWindow
+- nviz::preferences
+ - preferences::NvizPreferencesDialog
+- nviz::tools
+ - tools::NvizToolWindow
+ - tools::PositionWindow
+ - tools::ViewPositionWindow
+ - tools::LightPositionWindow
+- nviz::workspace
+ - workspace::NvizSettings
+- nviz::wxnviz
+ - wxnviz::Nviz
+ - wxnviz::Texture
+ - wxnviz::ImageTexture
+ - wxnviz::TextTexture
 
-- gui_modules/nviz_preferences.py
- - gui_modules::nviz_preferences::NvizPreferencesDialog
+\subsection psmap Cartograpic Composer
 
-- gui_modules/nviz.py
+- psmap::dialogs
+ - dialogs::TCValidator
+ - dialogs::PenStyleComboBox
+ - dialogs::CheckListCtrl
+ - dialogs::PsmapDialog
+ - dialogs::PageSetupDialog
+ - dialogs::MapDialog
+ - dialogs::MapFramePanel
+ - dialogs::RasterPanel
+ - dialogs::VectorPanel
+ - dialogs::RasterDialog
+ - dialogs::MainVectorDialog
+ - dialogs::VPropertiesDialog
+ - dialogs::LegendDialog
+ - dialogs::MapinfoDialog
+ - dialogs::ScalebarDialog
+ - dialogs::TextDialog
+ - dialogs::ImageDialog
+ - dialogs::NorthArrowDialog
+- psmap::instructions
+ - dialogs::Instruction
+ - dialogs::InstructionObject
+ - dialogs::InitMap
+ - dialogs::MapFrame
+ - dialogs::PageSetup
+ - dialogs::Mapinfo
+ - dialogs::Text
+ - dialogs::Image
+ - dialogs::NorthArrow
+ - dialogs::Point
+ - dialogs::Line
+ - dialogs::Rectangle
+ - dialogs::Scalebar
+ - dialogs::RasterLegend
+ - dialogs::VectorLegend
+ - dialogs::Raster
+ - dialogs::Vector
+ - dialogs::VProperties
+- psmap::utils
+ - utils::Rect2D
+ - utils::Rect2DPP
+ - utils::Rect2DPS
+ - utils::UnitConversion
+- psmap::frame
+ - frame::PsMapFrame
+ - frame::PsMapBufferedWindow
+- psmap::menudata
+ - menudata::PsMapData
+- psmap::toolbars
+ - toolbars::PsMapToolbar
 
-- gui_modules/nviz_tools.py
- - gui_modules::nviz_tools::NvizToolWindow
- - gui_modules::nviz_tools::PositionWindow
- - gui_modules::nviz_tools::ViewPositionWindow
- - gui_modules::nviz_tools::LightPositionWindow
-
-- gui_modules/wxnviz.py
- - gui_modules::wxnviz::Nviz
-
 \subsection locWizard Location Wizard
 
-- gui_modules/location_wizard.py
- - gui_modules::location_wizard::BaseClass
- - gui_modules::location_wizard::TitledPage
- - gui_modules::location_wizard::DatabasePage
- - gui_modules::location_wizard::CoordinateSystemPage
- - gui_modules::location_wizard::ProjectionsPage
- - gui_modules::location_wizard::ItemList
- - gui_modules::location_wizard::ProjParamsPage
- - gui_modules::location_wizard::DatumPage
- - gui_modules::location_wizard::EllipsePage
- - gui_modules::location_wizard::GeoreferencedFilePage
- - gui_modules::location_wizard::WKTPage
- - gui_modules::location_wizard::EPSGPage
- - gui_modules::location_wizard::CustomPage
- - gui_modules::location_wizard::SummaryPage
- - gui_modules::location_wizard::LocationWizard
- - gui_modules::location_wizard::RegionDef
- - gui_modules::location_wizard::TransList
- - gui_modules::location_wizard::SelectTransformDialog
+- location_wizard::base
+ - location_wizard::BaseClass
+- location_wizard::dialogs
+ - dialogs::RegionDef
+ - dialogs::TransList
+ - dialogs::SelectTransformDialog
+- location_wizard::wizard
+ - wizard::TitledPage
+ - wizard::DatabasePage
+ - wizard::CoordinateSystemPage
+ - wizard::ProjectionsPage
+ - wizard::ItemList
+ - wizard::ProjParamsPage
+ - wizard::DatumPage
+ - wizard::EllipsePage
+ - wizard::GeoreferencedFilePage
+ - wizard::EPSGPage
+ - wizard::CustomPage
+ - wizard::SummaryPage
+ - wizard::LocationWizard
 
-\subsection mcalc Map Calculator
+\subsection plot Plotting modules
 
-- gui_modules/mcalc_builder.py
- - gui_modules::mcalc_builder::MapCalcFrame
+- wxplot::base
+ - base::BasePlotFrame
+- wxplot::dialogs
+ - dialogs::ProfileRasterDialog
+ - dialogs::ScatterRasterDialog
+ - dialogs::PlotStatsFrame
+ - dialogs::HistRasterDialog
+ - dialogs::TextDialog
+ - dialogs::OptDialog
+- wxplot::histogram
+ - histogram::Histogram2Frame
+ - histogram::Histogram2Toolbar
+- wxplot::profile
+ - profile::ProfileFrame
+ - profile::ProfileToolbar
+- wxplot::scatter
+ - scatter::ScatterFrame
+ - scatter::ScatterToolbar
 
-\subsection misc Miscellaneous
+\subsection other Other GUI modules
 
-- gui_modules/colorrules.py
- - gui_modules::colorrules::ColorTable
- - gui_modules::colorrules::BufferedWindow
+- modules::colorrules
+ - colorrules::RulesPanel
+ - colorrules::ColorTable
+ - colorrules::RasterColorTable
+ - colorrules::VectorColorTable
+ - colorrules::ThematicVectorTable
+ - colorrules::BufferedWindow
+- modules::extensions
+ - extensions::InstallExtensionWindow
+ - extensions::ExtensionTree
+ - extensions::UninstallExtensionWindow
+ - extensions::CheckListExtension
+- modules::histogram
+ - histogram::BufferedWindow
+ - histogram::HistogramFrame
+ - histogram::HistogramToolbar
+- modules::mcalc_builder
+ - mcalc_builder::MapCalcFrame
+- modules::ogc_services
+ - ogc_services::WMSDialog
+ - ogc_services::LayersList
+- modules::vclean
+ - vclean::VectorCleaningFrame
 
-- gui_modules/gdialogs.py
- - gui_modules::gdialogs::ElementDialog
- - gui_modules::gdialogs::LocationDialog
- - gui_modules::gdialogs::MapsetDialog
- - gui_modules::gdialogs::NewVectorDialog
- - gui_modules::gdialogs::SavedRegion
- - gui_modules::gdialogs::DecorationDialog
- - gui_modules::gdialogs::TextLayerDialog
- - gui_modules::gdialogs::LoadMapLayersDialog
- - gui_modules::gdialogs::ImportDialog
- - gui_modules::gdialogs::GdalImportDialog
- - gui_modules::gdialogs::DxfImportDialog
- - gui_modules::gdialogs::LayersList
- - gui_modules::gdialogs::SetOpacityDialog
- - gui_modules::gdialogs::StaticWrapText
- - gui_modules::gdialogs::ImageSizeDialog
-
-- gui_modules/histogram.py
- - gui_modules::histogram::BufferedWindow
- - gui_modules::histogram::HistFrame
-
-- gui_modules/ogc_services.py
- - gui_modules::ogc_services::WMSDialog
- - gui_modules::ogc_services::LayersList
-
-- gui_modules/profile.py
- - gui_modules::profile::ProfileFrame
- - gui_modules::profile::SetRasterDialog
- - gui_modules::profile::TextDialog
- - gui_modules::profile::OptDialog
-
-- gui_modules/vclean.py
- - gui_modules::vclean::VectorCleaningFrame
-
 \section devel Further Development
 
 Ongoing development focuses on stability, portability and on the

Modified: grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata.xml
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata.xml	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata.xml	2012-02-19 20:31:20 UTC (rev 50882)
@@ -11,28 +11,33 @@
 	      <help>Create new workspace</help>
 	      <handler>OnWorkspaceNew</handler>
 	      <shortcut>Ctrl+N</shortcut>
+	      <id>ID_NEW</id>
 	    </menuitem>
 	    <menuitem>
 	      <label>Open</label>
 	      <help>Load workspace from file</help>
 	      <handler>OnWorkspaceOpen</handler>
 	      <shortcut>Ctrl+O</shortcut>
+	      <id>ID_OPEN</id>
 	    </menuitem>
 	    <menuitem>
 	      <label>Save</label>
 	      <help>Save workspace</help>
 	      <handler>OnWorkspaceSave</handler>
 	      <shortcut>Ctrl+S</shortcut>
+	      <id>ID_SAVE</id>
 	    </menuitem>
 	    <menuitem>
 	      <label>Save as</label>
 	      <help>Save workspace to file</help>
 	      <handler>OnWorkspaceSaveAs</handler>
+	      <id>ID_SAVEAS</id>
 	    </menuitem>
 	    <menuitem>
 	      <label>Close</label>
 	      <help>Close workspace file</help>
 	      <handler>OnWorkspaceClose</handler>
+	      <id>ID_CLOSE</id>
 	    </menuitem>
 	    <separator />
 	    <menuitem>
@@ -87,7 +92,7 @@
 	  <label>Import raster data</label>
 	  <items>
 	    <menuitem>
-	      <label>Common import formats</label>
+	      <label>Common formats import</label>
 	      <help>Import raster data into a GRASS map layer using GDAL.</help>
 	      <keywords>raster,import</keywords>
 	      <handler>OnImportGdalLayers</handler>
@@ -364,7 +369,7 @@
 	    <menuitem>
 	      <label>MPEG-1 export</label>
 	      <help>Raster File Series to MPEG Conversion.</help>
-	      <keywords>raster,export</keywords>
+	      <keywords>raster,export,animation</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>r.out.mpeg</command>
 	    </menuitem>
@@ -424,7 +429,7 @@
 	  <items>
 	    <menuitem>
 	      <label>Common export formats</label>
-	      <help>Converts GRASS vector map to one of the supported OGR vector formats.</help>
+	      <help>Converts to one of the supported OGR vector formats.</help>
 	      <keywords>vector,export</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>v.out.ogr</command>
@@ -588,7 +593,7 @@
 	  <items>
 	    <menuitem>
 	      <label>Raster to vector</label>
-	      <help>Converts a raster map into a vector map layer.</help>
+	      <help>Converts a raster map into a vector map.</help>
 	      <keywords>raster,conversion,vectorization</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>r.to.vect</command>
@@ -706,6 +711,7 @@
 	  <help>Quit wxGUI session</help>
 	  <handler>OnCloseWindow</handler>
 	  <shortcut>Ctrl+Q</shortcut>
+	  <id>ID_EXIT</id>
 	</menuitem>
       </items>
     </menu>
@@ -759,11 +765,13 @@
 	    <menuitem>
 	      <label>Change location and mapset</label>
 	      <help>Change current location and mapset.</help>
+	      <keywords>general,location,current</keywords>
               <handler>OnChangeLocation</handler>
 	    </menuitem>
 	    <menuitem>
 	      <label>Change mapset</label>
 	      <help>Change current mapset.</help>
+	      <keywords>general,mapset,current</keywords>
               <handler>OnChangeMapset</handler>
 	    </menuitem>
 	    <separator />
@@ -790,6 +798,19 @@
 	    </menuitem>
 	    <separator />
 	    <menuitem>
+	      <label>Create new location</label>
+	      <help>Launches location wizard to create new GRASS location.</help>
+	      <keywords>general,location,wizard</keywords>
+              <handler>OnLocationWizard</handler>
+	    </menuitem>
+	    <menuitem>
+	      <label>Create new mapset</label>
+	      <help>Creates new mapset in the current location, changes current mapset.</help>
+	      <keywords>general,mapset,create</keywords>
+              <handler>OnCreateMapset</handler>
+	    </menuitem>
+	    <separator />
+	    <menuitem>
 	      <label>Version</label>
 	      <help>Displays version and copyright information.</help>
 	      <keywords>general,version</keywords>
@@ -859,6 +880,7 @@
 	  <label>Preferences</label>
 	  <help>User GUI preferences (display font, commands, digitizer, etc.)</help>
 	  <handler>OnPreferences</handler>
+	  <id>ID_PREFERENCES</id>
 	</menuitem>
       </items>
     </menu>
@@ -1052,7 +1074,7 @@
 	  <items>
 	    <menuitem>
 	      <label>Raster to vector</label>
-	      <help>Converts a raster map into a vector map layer.</help>
+	      <help>Converts a raster map into a vector map.</help>
 	      <keywords>raster,conversion,vectorization</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>r.to.vect</command>
@@ -1252,7 +1274,7 @@
 	    </menuitem>
 	    <menuitem>
 	      <label>Distance to features</label>
-	      <help>Generates a raster map layer of distance to features in input layer.</help>
+	      <help>Generates a raster map of distance to features in input raster map.</help>
 	      <keywords>raster,geometry</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>r.grow.distance</command>
@@ -1299,7 +1321,7 @@
 	  <items>
 	    <menuitem>
 	      <label>Carve stream channels</label>
-	      <help>Takes vector stream data, transforms it to raster and subtracts depth from the output DEM.</help>
+	      <help>Generates stream channels.</help>
 	      <keywords>raster,hydrology</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>r.carve</command>
@@ -1328,14 +1350,14 @@
 	    </menuitem>
 	    <menuitem>
 	      <label>Flow lines</label>
-	      <help>Construction of slope curves (flowlines), flowpath lengths, and flowline densities (upslope areas) from a raster digital elevation model (DEM).</help>
+	      <help>Constructs flow lines.</help>
 	      <keywords>raster,hydrology</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>r.flow</command>
 	    </menuitem>
 	    <menuitem>
 	      <label>Watershed analysis</label>
-	      <help>Watershed basin analysis program.</help>
+	      <help>Watershed basin analysis program</help>
 	      <keywords>raster,hydrology</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>r.watershed</command>
@@ -1349,7 +1371,7 @@
 	    </menuitem>
 	    <menuitem>
 	      <label>Watershed basin creation</label>
-	      <help>Watershed basin creation program.</help>
+	      <help>Creates watershed basins.</help>
 	      <keywords>raster,hydrology</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>r.water.outlet</command>
@@ -1358,7 +1380,7 @@
 	    <menuitem>
 	      <label>Groundwater modeling</label>
 	      <help>Numerical calculation program for transient, confined and unconfined groundwater flow in two dimensions.</help>
-	      <keywords>raster,hydrology</keywords>
+	      <keywords>raster</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>r.gwflow</command>
 	    </menuitem>
@@ -1582,8 +1604,8 @@
 	    </menuitem>
 	    <menuitem>
 	      <label>Reclassify</label>
-	      <help>Creates a new map layer whose category values are based upon a reclassification of the categories in an existing raster map layer.</help>
-	      <keywords>raster,statistics,reclass</keywords>
+	      <help>Reclassify raster map based on category values.</help>
+	      <keywords>raster,reclassification</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>r.reclass</command>
 	    </menuitem>
@@ -1747,7 +1769,7 @@
 	    <separator />
 	    <menuitem>
 	      <label>Fill NULL cells</label>
-	      <help>Fills no-data areas in raster maps using v.surf.rst splines interpolation</help>
+	      <help>Fills no-data areas in raster maps using spline interpolation.</help>
 	      <keywords>raster,elevation,interpolation</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>r.fillnulls</command>
@@ -1803,7 +1825,7 @@
 	    </menuitem>
 	    <menuitem>
 	      <label>Sum area by raster map and category</label>
-	      <help>Reports statistics for raster map layers.</help>
+	      <help>Reports statistics for raster maps.</help>
 	      <keywords>raster,statistics</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>r.report</command>
@@ -1906,7 +1928,7 @@
 	    <separator />
 	    <menuitem>
 	      <label>Parallel lines</label>
-	      <help>Creates parallel line to input vector lines.</help>
+	      <help>Create parallel line to input lines</help>
 	      <keywords>vector,geometry</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>v.parallel</command>
@@ -2074,19 +2096,19 @@
 	      <command>v.db.select</command>
 	    </menuitem>
 	  </items>
-	 </menu>
+	</menu>
 	<menu>
 	  <label>Feature selection</label>
 	  <items>
 	    <menuitem>
-	      <label>Query with attributes</label>
+	      <label>Attribute query</label>
 	      <help>Selects vector objects from an existing vector map and creates a new map containing only the selected objects.</help>
 	      <keywords>vector,extract</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>v.extract</command>
 	    </menuitem>
 	    <menuitem>
-	      <label>Query with another vector map</label>
+	      <label>Spatial query</label>
 	      <help>Selects features from vector map (A) by features from other vector map (B).</help>
 	      <keywords>vector,spatial query</keywords>
 	      <handler>OnMenuCmd</handler>
@@ -2559,9 +2581,9 @@
 	  <items>
 	    <menuitem>
 	      <label>Create/edit group</label>
-	      <help>Creates, edits, and lists groups and subgroups of imagery files.</help>
+	      <help>Creates, edits, and lists groups of imagery files.</help>
 	      <keywords>imagery,map management</keywords>
-	      <handler>OnMenuCmd</handler>
+	      <handler>OnEditImageryGroups</handler>
 	      <command>i.group</command>
 	    </menuitem>
 	    <menuitem>
@@ -2574,7 +2596,7 @@
 	    <separator />
 	    <menuitem>
 	      <label>Mosaic images</label>
-	      <help>Mosaics up to 4 images and extends colormap; creates map *.mosaic</help>
+	      <help>Mosaics up to four images, extending the color table.</help>
 	      <keywords>raster,imagery,mosaicking</keywords>
 	      <handler>OnMenuCmd</handler>
 	      <command>i.image.mosaic</command>
@@ -2609,14 +2631,6 @@
 	</menu>
 	<separator />
 	<menuitem>
-	  <label>Ortho photo rectification (requires Xterm)</label>
-	  <help>Menu driver for the photo imagery programs.</help>
-	  <keywords>imagery,orthorectify</keywords>
-	  <handler>OnXTerm</handler>
-	  <command>i.ortho.photo</command>
-	</menuitem>
-	<separator />
-	<menuitem>
 	  <label>Rectify image or raster</label>
 	  <help>Rectifies an image by computing a coordinate transformation for each pixel in the image based on the control points.</help>
 	  <keywords>imagery,rectify</keywords>
@@ -2626,7 +2640,7 @@
 	<menuitem>
 	  <label>Histogram</label>
 	  <help>Generate histogram of image</help>
-	  <handler>DispHistogram</handler>
+	  <handler>OnHistogram</handler>
 	</menuitem>
 	<menuitem>
 	  <label>Spectral response</label>
@@ -2750,6 +2764,29 @@
 	  <label>Satellite images tools</label>
 	  <items>
 	    <menuitem>
+	      <label>Landsat DN to radiance/reflectance</label>
+	      <help>Calculates top-of-atmosphere radiance or reflectance and temperature for Landsat MSS/TM/ETM+.</help>
+	      <keywords>imagery,landsat,top-of-atmosphere reflectance,dos-type simple atmospheric correction</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>i.landsat.toar</command>
+	    </menuitem>
+	    <menuitem>
+	      <label>Landsat cloud cover assessment</label>
+	      <help>Performs Landsat TM/ETM+ Automatic Cloud Cover Assessment (ACCA).</help>
+	      <keywords>imagery,landsat,acca</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>i.landsat.acca</command>
+	    </menuitem>
+	    <separator />
+	    <menuitem>
+	      <label>Modis quality control</label>
+	      <help>Extract quality control parameters from Modis QC layers</help>
+	      <keywords>QC,Quality Control,surface reflectance,Modis</keywords>
+	      <handler>OnMenuCmd</handler>
+	      <command>i.modis.qc</command>
+	    </menuitem>
+	    <separator />
+	    <menuitem>
 	      <label>Atmospheric correction</label>
 	      <help>Performs atmospheric correction using the 6S algorithm.</help>
 	      <keywords>imagery,atmospheric correction</keywords>
@@ -3101,6 +3138,7 @@
 	  <keywords>general,manual,help</keywords>
 	  <handler>RunMenuCmd</handler>
 	  <command>g.manual -i</command>
+	  <id>ID_HELP</id>
 	</menuitem>
 	<menuitem>
 	  <label>GUI help</label>
@@ -3108,12 +3146,14 @@
 	  <keywords>general,manual,help</keywords>
 	  <handler>RunMenuCmd</handler>
 	  <command>g.manual entry=wxGUI</command>
+	  <id>ID_HELP</id>
 	</menuitem>
 	<separator />
 	<menuitem>
 	  <label>About GRASS GIS</label>
 	  <help>About GRASS GIS</help>
 	  <handler>OnAboutGRASS</handler>
+	  <id>ID_ABOUT</id>
 	</menuitem>
       </items>
     </menu>

Modified: grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata_modeler.xml
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata_modeler.xml	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata_modeler.xml	2012-02-19 20:31:20 UTC (rev 50882)
@@ -66,8 +66,8 @@
       <label>&amp;Model</label>
       <items>
 	<menuitem>
-	  <label>Add action</label>
-	  <help>Add action (GRASS module) to model</help>
+	  <label>Add command</label>
+	  <help>Add action (GRASS command) to model</help>
 	  <handler>OnAddAction</handler>
 	  <shortcut>Ctrl+A</shortcut>
 	</menuitem>
@@ -83,8 +83,8 @@
 	  <handler>OnDefineRelation</handler>
 	</menuitem>
 	<menuitem>
-	  <label>Add loop</label>
-	  <help>Adds loop (for) to model</help>
+	  <label>Add loop / series</label>
+	  <help>Adds loop (series) to model</help>
 	  <handler>OnDefineLoop</handler>
 	  <shortcut>Ctrl+L</shortcut>
 	</menuitem>
@@ -122,7 +122,6 @@
 	  <label>Validate model</label>
 	  <help>Validate entire model</help>
 	  <handler>OnValidateModel</handler>
-	  <shortcut>Ctrl+V</shortcut>
 	</menuitem>
       </items>
     </menu>

Modified: grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata_psmap.xml
===================================================================
--- grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata_psmap.xml	2012-02-19 18:44:49 UTC (rev 50881)
+++ grass/branches/releasebranch_6_4/gui/wxpython/xml/menudata_psmap.xml	2012-02-19 20:31:20 UTC (rev 50882)
@@ -94,6 +94,18 @@
 	  <handler>OnAddText</handler>
 	  <shortcut>Ctrl+T</shortcut>
 	</menuitem>
+	<menuitem>
+	  <label>Image</label>
+	  <help>Add image</help>
+	  <handler>OnAddImage</handler>
+	  <shortcut></shortcut>
+	</menuitem>
+	<menuitem>
+	  <label>North Arrow</label>
+	  <help>Add north arrow</help>
+	  <handler>OnAddNorthArrow</handler>
+	  <shortcut></shortcut>
+	</menuitem>
 	<separator/>
 	<menuitem>
 	  <label>Delete</label>



More information about the grass-commit mailing list