[GRASS-SVN] r40794 - grass-addons/gui/wxpython/data_catalog

svn_grass at osgeo.org svn_grass at osgeo.org
Wed Feb 3 11:29:52 EST 2010


Author: rashadkm
Date: 2010-02-03 11:29:51 -0500 (Wed, 03 Feb 2010)
New Revision: 40794

Added:
   grass-addons/gui/wxpython/data_catalog/LayerTree.py
   grass-addons/gui/wxpython/data_catalog/gmconsole.py
   grass-addons/gui/wxpython/data_catalog/mapdisplay.py
   grass-addons/gui/wxpython/data_catalog/newprompt.py
   grass-addons/gui/wxpython/data_catalog/wx_utils.py
Modified:
   grass-addons/gui/wxpython/data_catalog/catalog.py
   grass-addons/gui/wxpython/data_catalog/mapwindow.py
Log:
data catalog; new features added

Added: grass-addons/gui/wxpython/data_catalog/LayerTree.py
===================================================================
--- grass-addons/gui/wxpython/data_catalog/LayerTree.py	                        (rev 0)
+++ grass-addons/gui/wxpython/data_catalog/LayerTree.py	2010-02-03 16:29:51 UTC (rev 40794)
@@ -0,0 +1,317 @@
+"""
+ at package layertree.py
+
+ at brief Tree widget for listing maps raster, vector and DBF.
+
+Classes:
+ - LayerTree
+
+
+(C) 2006-2009 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS
+for details.
+
+ at author Michael Barton (Arizona State University)
+ at author Jachym Cepicky (Mendel University of Agriculture)
+ at author Martin Landa <landa.martin gmail.com>
+ at author Mohammed Rashad K.M <rashadkm at gmail dot com> (modified for DataCatalog)
+"""
+
+import os
+import sys
+import wx
+import glob
+import render
+
+
+
+#To run DataCatalog from any directory set this pathname for access to gui_modules 
+gbase = os.getenv("GISBASE") 
+pypath = os.path.join(gbase,'etc','wxpython','gui_modules')
+sys.path.append(pypath)
+
+
+import globalvar
+if not os.getenv("GRASS_WXBUNDLED"):
+    globalvar.CheckForWx()
+import gcmd
+
+
+
+
+class LayerTree(wx.TreeCtrl):
+
+    def __init__(self, parent, id,
+                 pos = wx.DefaultPosition,
+                 size = wx.DefaultSize,
+                 style= wx.TR_HIDE_ROOT|wx.TR_HAS_BUTTONS,gisdbase=None):
+
+        wx.TreeCtrl.__init__(self, parent, id, pos, size, style)
+        self.itemFont = wx.Font(pointSize=9,weight=0, family=wx.FONTFAMILY_DEFAULT ,style=wx.FONTSTYLE_ITALIC)
+
+        self.gisdbase = gisdbase
+        self.Map = None
+        if self.Map is not None:
+            print self.Map.width
+
+        self.ID_REN= wx.NewId()
+        self.ID_COPY = wx.NewId()
+        self.ID_DEL = wx.NewId()
+
+        acel = wx.AcceleratorTable([ 
+		        (wx.ACCEL_CTRL,  ord('R'), self.ID_REN ) ,
+		        (wx.ACCEL_CTRL,  ord('C'), self.ID_COPY) ,
+		        (wx.ACCEL_NORMAL, wx.WXK_DELETE, self.ID_DEL) ])
+
+
+        self.SetAcceleratorTable(acel)
+
+
+
+
+    def AddTreeNodes(self,location,mapset):
+	    """
+	    Adds tree nodes. raster,vector and dbf files are identified using 
+	    their directory structure.
+	    """
+
+	    root = self.AddRoot('root')
+	    node_raster = self.AppendItem(root, "Raster Map")
+	    node_vector = self.AppendItem(root, "Vector Map")
+	    node_dbf = self.AppendItem(root, "DBF")
+	    treeNodes = [node_raster,node_vector,node_dbf]
+
+	    glocs = glob.glob(os.path.join(self.gisdbase,location, mapset,"*"))
+	    for gloc in glocs:
+		    if not os.path.isfile(gloc) and os.path.isdir(gloc):
+			    if(os.path.basename(gloc)=='cats'):
+				    for rast in glob.glob(os.path.join(self.gisdbase,location, mapset,gloc, "*")):
+					    self.AppendItem(node_raster, os.path.basename(rast))
+			    elif(os.path.basename(gloc)=='vector'):
+				    for vect in glob.glob(os.path.join(self.gisdbase,location, mapset,gloc, "*")):
+					    self.AppendItem(node_vector, os.path.basename(vect))
+			    elif(os.path.basename(gloc)=='dbf'):
+				    for dfile in glob.glob(os.path.join(self.gisdbase,location, mapset,gloc, "*")):
+					    self.AppendItem(node_dbf, os.path.basename(dfile))
+
+        #Nodes with no children are given an italic type font
+	    for node in treeNodes: 
+		    if not self.ItemHasChildren(node):
+			    if self.GetItemText(node) == 'Raster Map':
+				    tmp_item = self.AppendItem(node, "No raster maps found.")
+			    elif self.GetItemText(node) == 'Vector Map':
+				    tmp_item = self.AppendItem(node, "No vector maps found.")
+			    elif self.GetItemText(node) == 'DBF':
+				    tmp_item = self.AppendItem(node, "No DBF files found.")
+			    self.SetItemFont(tmp_item,self.itemFont)
+
+
+
+
+    def OnToggleExpand(self,event):  
+	    if self.treeExpand.GetValue() == True:
+		    self.ExpandAll()
+	    else: 
+		    self.CollapseAll()
+
+
+    def OnBeginRename(self,event):
+
+        item = self.GetItemText(event.GetItem())
+        if type(item) == str and item in ("Raster Map", "Vector Map" , "DBF"):
+            event.Veto()             #disable editing of parent items
+
+    def OnEndRename(self,event):
+	    """
+	    Rename mapset using grass commands
+	    """
+	    item = event.GetItem()
+	    oldName = self.GetItemText(item) 
+	    try:
+		    newName =  self.GetEditControl().GetValue()
+	    except:
+		    return
+	    parent =self.GetItemParent(item)
+	    if self.GetItemText(parent) == "Raster Map" :
+		    cmdflag = 'rast=' +  oldName + ',' + newName
+	    elif self.GetItemText(parent) == "Vector Map" :
+		    cmdflag = 'vect=' +  oldName + ',' + newName
+
+	    if cmdflag:
+		    command = ["g.rename", cmdflag]
+		    gcmd.CommandThread(command,stdout=None,stderr=None).run()
+
+
+
+    def OnTreePopUp(self,event):
+	    """
+	    Display a popupMenu for copy,rename & delete operations
+	    """
+	    item =  event.GetItem()
+	    if not self.ItemHasChildren(item) and \
+		       self.GetItemFont(item) != self.itemFont:
+
+		    self.popupmenu = wx.Menu()
+		    mnuCopy = self.popupmenu.Append(self.ID_COPY,'&Copy\tCtrl+C')
+		    mnuRename = self.popupmenu.Append(self.ID_REN,'&Rename\tCtrl-R')
+		    mnuDel = self.popupmenu.Append(self.ID_DEL,'&Delete\tDEL')
+		    self.PopupMenu(self.popupmenu)
+
+
+    def OnCopy( self,event ):
+        #print "copy"
+	    item =  self.GetSelection()
+	    parent = self.GetItemParent(item)
+	    pText = self.GetItemText(parent)
+	    name = self.GetCopyName(item)
+	    if pText == "Raster Map" :
+		    cmdflag = 'rast=' + self.GetItemText(item) + ',' + name
+		    self.InsertItem(parent,item, name)
+	    elif pText  == "Vector Map" :
+		    cmdflag = 'vect=' + self.GetItemText(item) + ',' + name
+		    self.InsertItem(parent,item, name)
+
+	    if cmdflag:
+		    command = ["g.copy", cmdflag]
+		    gcmd.CommandThread(command,stdout=None,stderr=None).run()
+
+
+    def GetCopyName(self, item):
+	    """
+	    Returns unique name depending on the mapname to be copied.
+	    """
+
+	    def GetPrefix(prefix):
+		    """
+		    This returns a prefix to the given map name 
+		    prefix applied here is _copy_x.
+		    """
+
+		    prefix = "_copy_" + str(self.count)
+		    self.count = self.count + 1
+		    return prefix
+        
+            #end of GetPrefix
+
+	    def CheckName(parent,prefix,name):
+		    """
+		    Checks all silbings of the parent wheather the name 
+		    already exists.
+		    """
+		    ncount = self.GetChildrenCount(parent, False)
+		    ck = 1
+		    current , ck = self.GetFirstChild(parent)
+		    for i in range(ncount):
+			    if str(self.GetItemText(current)) == str(name + prefix):
+				    return False
+			    else:
+				    current,ck = self.GetNextChild(parent,ck)
+		    return True
+            
+            #End of CheckName
+
+        #GetCopyName function starts here
+	    ext = None	
+	    self.count  = 1
+	    ext = GetPrefix(ext)
+	    name = str(self.GetItemText(item))
+	    parent = self.GetItemParent(item)
+	    while  CheckName(parent,ext,name) == False:
+		    ext = GetPrefix(ext)
+		    CheckName(parent,ext,name)
+
+	    name = str(name + ext)
+	    return name
+
+
+    def OnRename( self,event ):
+
+        item = self.GetSelection()
+        self.EditLabel( self.GetSelection())
+
+
+    def OnDelete( self,event ):
+        """
+        Performs grass command for deleting a map
+        """
+        item =  self.GetSelection()
+        dlg = wx.MessageDialog(self, message=_(    "Do you want to delete selected map ?"),
+                            caption=_("Delete Map"),
+                            style=wx.YES_NO | wx.YES_DEFAULT | \
+                                wx.CANCEL | wx.ICON_QUESTION)
+        ret = dlg.ShowModal()
+        if ret == wx.ID_YES:
+            dlg.Destroy()
+            parent  =self.GetItemParent(item) 
+            if self.GetItemText(parent) == "Raster Map" :
+                cmdflag = 'rast=' + str(self.GetItemText(item))
+            elif self.tree.GetItemText(parent) == "Vector Map" :
+                cmdflag = 'vect=' + str(self.GetItemText(item))
+
+            if cmdflag:
+                command = ["g.remove", cmdflag]
+                gcmd.CommandThread(command,stdout=None,stderr=None).run()
+                select = self.GetPrevSibling(item)
+                self.Delete(item)
+                self.SelectItem(select)
+
+        elif ret == wx.ID_CANCEL:
+         dlg.Destroy()
+         return
+        
+
+
+    def OnCloseWindow(self,event):
+
+	    if self.gisrc['LOCATION_NAME'] != self.iLocation or \
+		    self.gisrc['MAPSET'] != self.iMapset:
+		    self.gisrc['LOCATION_NAME'] = self.iLocation
+		    self.gisrc['MAPSET'] = self.iMapset
+		    self.update_grassrc(self.gisrc)	
+
+	    self.Map.Clean()
+	    event.Skip()
+        #self.Destroy()
+
+
+    def OnDisplay(self, event):
+        item =  event.GetItem()
+        pText = self.GetItemText(self.GetItemParent(item)) 
+
+        leftpanel=self.GetParent()
+        splitter = leftpanel.GetParent()
+        frame = splitter.GetParent()
+        window2 = splitter.GetWindow2()
+
+        winlist = window2.GetChildren()
+        for win in winlist:
+            if type(win) == wx.lib.flatnotebook.FlatNotebook:
+                child=win.GetChildren()
+                for panel in child:
+                    if panel.GetName() == "pg_panel":
+                        ss = panel.GetName()
+
+
+
+        if not self.ItemHasChildren(item):
+            self.mapname = "map=" + self.GetItemText(item) + "@" + frame.cmbMapset.GetValue()
+            #self.mapname = "map=" + self.GetItemText(item) + "@PERMANENT" 
+            if pText == "Raster Map" :
+	            self.cmd= ['d.rast', str(self.mapname)]
+	            self.infocmd = ["r.info", str(self.mapname)]
+            elif pText == "Vector Map" :
+	            self.cmd= ['d.vect', str(self.mapname)]
+	            self.infocmd = ["v.info", str(self.mapname)]
+
+        if self.cmd:
+            panel.Map.Clean()
+            panel.Map.__init__()			#to update projection and region
+            panel.Map.AddLayer(type='raster', name='layer1', command=self.cmd)	
+            panel.Map.region = panel.Map.GetRegion()
+            panel.MapWindow2D.flag = True
+            panel.MapWindow2D.UpdateMap(render=True)
+
+		
+
+

Modified: grass-addons/gui/wxpython/data_catalog/catalog.py
===================================================================
--- grass-addons/gui/wxpython/data_catalog/catalog.py	2010-02-03 10:46:47 UTC (rev 40793)
+++ grass-addons/gui/wxpython/data_catalog/catalog.py	2010-02-03 16:29:51 UTC (rev 40794)
@@ -19,8 +19,40 @@
 
 """
 
+import sys
 import os
-import sys
+import time
+import traceback
+import re
+import string
+import getopt
+import platform
+
+try:
+    import xml.etree.ElementTree as etree
+except ImportError:
+    import elementtree.ElementTree as etree 
+
+
+gbase = os.getenv("GISBASE") 
+pypath = os.path.join(gbase,'etc','wxpython')
+sys.path.append(pypath)
+
+
+
+import gui_modules
+gmpath = gui_modules.__path__[0]
+sys.path.append(gmpath)
+
+import images
+imagepath = images.__path__[0]
+sys.path.append(imagepath)
+
+import icons
+gmpath = icons.__path__[0]
+sys.path.append(gmpath)
+
+
 #To run DataCatalog from any directory set this pathname for access to gui_modules 
 gbase = os.getenv("GISBASE") 
 pypath = os.path.join(gbase,'etc','wxpython','gui_modules')
@@ -34,469 +66,1603 @@
 import gcmd
 import glob
 import render
-from mapwindow import BufferedWindow
+import gui_modules.gdialogs as gdialogs
+import gui_modules.goutput as goutput
+import gui_modules.histogram as histogram
+from   gui_modules.debug import Debug
+import gui_modules.menuform as menuform
+import gui_modules.menudata as menudata
 
+import gui_modules.utils as utils
+import gui_modules.preferences as preferences
+import gui_modules.mapdisp as mapdisp
+import gui_modules.histogram as histogram
+import gui_modules.profile as profile
+import gui_modules.rules as rules
+import gui_modules.mcalc_builder as mapcalculator
+import gui_modules.gcmd as gcmd
+import gui_modules.georect as georect
+import gui_modules.dbm as dbm
+import gui_modules.workspace as workspace
+import gui_modules.colorrules as colorrules
+import gui_modules.ogc_services as ogc_services
+import newprompt as prompt
+from   gui_modules.help import MenuTreeWindow
+from   gui_modules.help import AboutWindow
+from   icons.icon import Icons
 
+from gmconsole import GLog
+from mapdisplay import MapFrame
+from LayerTree import LayerTree
+import wx.lib.flatnotebook as FN
+from   icons.icon import Icons
+import wx_utils as wx_utils
+from preferences import globalSettings as UserSettings
+
+
 class DataCatalog(wx.Frame):
 
-	def __init__(self,size=wx.DefaultSize):
-		wx.Frame.__init__(self, None, wx.ID_ANY, "Data Catalog", wx.DefaultPosition, wx.DefaultSize)
-		self.Maximize()
 
-		self.gisbase  = os.getenv("GISBASE")
-		self.gisrc  = self.read_gisrc()
-		self.viewInfo = True        #to display v/r.info on mapdisplay
-		self.gisdbase = self.gisrc['GISDBASE']
+    def __init__(self, parent=None, id=wx.ID_ANY, title=_("Data Catalog"),
+                 workspace=None,size=wx.DefaultSize,pos=wx.DefaultPosition):
 
+        self.iconsize  = (16, 16)
+        self.baseTitle = title
+
+        wx.Frame.__init__(self, parent, id, title, pos=pos, size=size)
+ 
+
+        #self.Maximize()
+
+
+        self.gisbase  = os.getenv("GISBASE")
+        self.gisrc  = self.read_gisrc()
+        self.viewInfo = True        #to display v/r.info on mapdisplay
+        self.gisdbase = self.gisrc['GISDBASE']
+
         #backup location and mapset from gisrc which may be modified  by datacatalog
-		self.iLocation = self.gisrc['LOCATION_NAME']
-		self.iMapset = self.gisrc['MAPSET']
+        self.iLocation = self.gisrc['LOCATION_NAME']
+        self.iMapset = self.gisrc['MAPSET']
+        
 
-		self.ID_REN= wx.NewId()
-		self.ID_COPY = wx.NewId()
-		self.ID_DEL = wx.NewId()
+        #self.Map = render.Map()
 
-		acel = wx.AcceleratorTable([ 
-				(wx.ACCEL_CTRL,  ord('R'), self.ID_REN ) ,
-				(wx.ACCEL_CTRL,  ord('C'), self.ID_COPY) ,
-				(wx.ACCEL_NORMAL, wx.WXK_DELETE, self.ID_DEL) ])
+        self.curr_pagenum  = -1           # currently selected page number for layer tree notebook
+        self.encoding      = 'ISO-8859-1' # default encoding for display fonts
+        self.workspaceFile = workspace    # workspace file
+        self.menucmd       = dict()       # menuId / cmd
+        self.georectifying = None         # reference to GCP class or None
 
+        self.dialogs        = dict()
+        self.dialogs['preferences'] = None
+        self.dialogs['atm'] = list()
 
-		self.SetAcceleratorTable(acel)
 
 
-		#creating sizers    
-		self.cmbSizer = wx.GridBagSizer(hgap=5, vgap=0) 
-		self.mSizer = wx.BoxSizer(wx.VERTICAL)
+        self.g_catalog=None
 
-		#these two sizers are applied to splitter window
-		self.leftSizer = wx.BoxSizer(wx.HORIZONTAL)
-		self.rightSizer = wx.BoxSizer(wx.VERTICAL)
 
-		#populate location combobox
-		self.loclist = self.GetLocations()
+        self.ID_REN= wx.NewId()
+        self.ID_COPY = wx.NewId()
+        self.ID_DEL = wx.NewId()
 
-		#setting splitter window
-		self.win = wx.SplitterWindow(self)
-		self.pLeft = wx.Panel(self.win, style=wx.SUNKEN_BORDER)
-		self.pRight = wx.Panel(self.win, style=wx.SUNKEN_BORDER)
-		self.cmbPanel = wx.Panel(self)
-		self.pRight.SetBackgroundColour("white")
-		self.pLeft.Hide()
-		self.pRight.Hide()
-		self.win.Initialize(self.pLeft)
-		self.win.SplitVertically(self.pLeft, self.pRight, 310)
+        acel = wx.AcceleratorTable([ 
+                (wx.ACCEL_CTRL,  ord('R'), self.ID_REN ) ,
+                (wx.ACCEL_CTRL,  ord('C'), self.ID_COPY) ,
+                (wx.ACCEL_NORMAL, wx.WXK_DELETE, self.ID_DEL) ])
 
 
-		#creating controls
-		self.mInfo = wx.TextCtrl(self.pRight, wx.ID_ANY, style = wx.TE_MULTILINE|wx.HSCROLL|wx.TE_READONLY)
-		self.chkInfo = wx.CheckBox(self.cmbPanel, wx.ID_ANY,"display Info", wx.DefaultPosition, wx.DefaultSize)
-		self.treeExpand = wx.CheckBox(self.cmbPanel, wx.ID_ANY,"Expand All", wx.DefaultPosition, wx.DefaultSize)
-		self.lbLocation = wx.StaticText(self.cmbPanel, wx.ID_ANY, "Location")
-		self.lbMapset = wx.StaticText(self.cmbPanel, wx.ID_ANY, "Mapset")
-		self.cmbLocation = wx.ComboBox(self.cmbPanel, value = "Select Location",size=wx.DefaultSize, choices=self.loclist)
-		self.cmbMapset = wx.ComboBox(self.cmbPanel, value = "Select Mapset", size=wx.DefaultSize)	
-		self.tree = wx.TreeCtrl(self.pLeft, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TR_HIDE_ROOT|wx.TR_HAS_BUTTONS|wx.TR_EDIT_LABELS)
-		self.itemFont = wx.Font(pointSize=9,weight=0, family=wx.FONTFAMILY_DEFAULT ,style=wx.FONTSTYLE_ITALIC)
-		#self.redir = wx.TextCtrl(self, wx.ID_ANY)
+        self.SetAcceleratorTable(acel)
+        self.menucmd       = dict() 
 
-		self.Map = render.Map()
-		self.MapWindow = BufferedWindow(self.pRight, wx.ID_ANY,wx.DefaultPosition, size=(640,480), Map=self.Map)
-		self.Map.Clean()
 
+     #creating sizers    
+        self.cmbSizer = wx.BoxSizer(wx.HORIZONTAL) 
+        self.mSizer = wx.BoxSizer(wx.VERTICAL)
 
-		#By default v/r.info will be displayed
-		self.chkInfo.SetValue(True) 
-		self.treeExpand.SetValue(False)
+        #these two sizers are applied to splitter window
+        self.leftSizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.rightSizer = wx.BoxSizer(wx.HORIZONTAL)
 
-		#apply bindings and setting layouts
-		self.doBindings()
-		self.doLayout()
-		self.Centre()
+        #populate location combobox
+        self.loclist = self.GetLocations()
 
-	def OnToggleExpand(self,event):  
-		if self.treeExpand.GetValue() == True:
-			self.tree.ExpandAll()
-		else: 
-			self.tree.CollapseAll()
+        #self.pg_panel4 = None
 
+        self.menubar, self.menudata = self.__createMenuBar()
+        #self.statusbar = self.CreateStatusBar(number=1)
+        #self.cmdprompt, self.cmdinput = self.__createCommandPrompt()
+        self.toolbar   = self.__createToolBar()
+
+        #setting splitter window
+        self.win = wx.SplitterWindow(self)
+        self.pLeft = wx.Panel(self.win, style=wx.SUNKEN_BORDER)
+        self.pRight = wx.Panel(self.win, style=wx.SUNKEN_BORDER,name="rightpanel")
+        self.cmbPanel = wx.Panel(self)
+        self.rightPanel = wx.Panel(self.pRight)
+        self.pRight.SetBackgroundColour("white")
+        self.pLeft.Hide()
+        self.pRight.Hide()
+        self.win.Initialize(self.pLeft)
+        self.win.SplitVertically(self.pLeft, self.pRight, 310)
+
+        self.maptree = None
+        self.pg_panel = None
+        
+        #creating controls
+        #self.mInfo = wx.TextCtrl(self.pRight, wx.ID_ANY, style = wx.TE_MULTILINE|wx.HSCROLL|wx.TE_READONLY)
+        #self.chkInfo = wx.CheckBox(self.cmbPanel, wx.ID_ANY,"display Info", wx.DefaultPosition, wx.DefaultSize)
+        self.treeExpand = wx.CheckBox(self.cmbPanel, wx.ID_ANY,"Expand All", wx.DefaultPosition, wx.DefaultSize)
+        self.cmbLocation = wx.ComboBox(self.cmbPanel, value = "Select Location",size=wx.DefaultSize, choices=self.loclist)
+        self.cmbMapset = wx.ComboBox(self.cmbPanel, value = "Select Mapset", size=wx.DefaultSize)	
+        #self.tree = wx.TreeCtrl(self.pLeft, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TR_HIDE_ROOT|wx.TR_HAS_BUTTONS|wx.TR_EDIT_LABELS)
+
+        self.ltree = LayerTree(self.pLeft,wx.ID_ANY,gisdbase=self.gisdbase)
+
+        self.itemFont = wx.Font(pointSize=9,weight=0, family=wx.FONTFAMILY_DEFAULT ,style=wx.FONTSTYLE_ITALIC)
+
+        
+        self.notebook  = self.__createNoteBook()
+        self.cmdprompt, self.cmdinput = self.__createCommandPrompt()
+
+       # self._mgr = self.pg_panel._layerManager
+
+#        self._mgr.AddPane(self.cmdprompt, wx.aui.AuiPaneInfo().CentrePane().Dockable(False).BestSize((-1,-1)).CloseButton(False).DestroyOnClose(True). Layer(0))
+
+        self.current = self.notebook.GetCurrentPage()      
+
     
-	def OnToggleInfo(self,event):  
-		if self.chkInfo.GetValue() == True:
-			self.viewInfo = True
-			self.mInfo.Show()
-		else: 
-			self.viewInfo = False
-			self.mInfo.Hide()
 
-	def OnBeginRename(self,event):
 
-	    item = self.tree.GetItemText(event.GetItem())
-	    if type(item) == str and item in ("Raster Map", "Vector Map" , "DBF"):
-	        event.Veto()             #disable editing of parent items
-	
-	def OnEndRename(self,event):
-		"""
-		Rename mapset using grass commands
-		"""
-		item = event.GetItem()
-		oldName = self.tree.GetItemText(item) 
-		try:
-			newName =  self.tree.GetEditControl().GetValue()
-		except:
-			return
-		parent =self.tree.GetItemParent(item)
-		if self.tree.GetItemText(parent) == "Raster Map" :
-			cmdflag = 'rast=' +  oldName + ',' + newName
-		elif self.tree.GetItemText(parent) == "Vector Map" :
-			cmdflag = 'vect=' +  oldName + ',' + newName
+        self.doBindings()
+        self.doLayout()
 
-		if cmdflag:
-			command = ["g.rename", cmdflag]
-			gcmd.CommandThread(command,stdout=None,stderr=None).run()
+        self.cmbSizer.Add(self.cmdprompt)
 
+        self.Map =    self.GetMapDisplay()
 
 
-	def OnTreePopUp(self,event):
-		"""
-		Display a popupMenu for copy,rename & delete operations
-		"""
-		item =  event.GetItem()
-		if not self.tree.ItemHasChildren(item) and \
-			   self.tree.GetItemFont(item) != self.itemFont:
 
-			self.popupmenu = wx.Menu()
-			mnuCopy = self.popupmenu.Append(self.ID_COPY,'&Copy\tCtrl+C')
-			mnuRename = self.popupmenu.Append(self.ID_REN,'&Rename\tCtrl-R')
-			mnuDel = self.popupmenu.Append(self.ID_DEL,'&Delete\tDEL')
-			self.tree.PopupMenu(self.popupmenu)
 
+    def GetMapDisplay(self):
+        self.winlist = self.GetChildren()
+        for win in self.winlist:
+            if win.GetName()== "splitter":
+                self.splitter = win.GetWindow2()
+               
+        self.books = self.splitter.GetChildren()
+        for self.book in self.books:
+                if type(self.book) == wx.lib.flatnotebook.FlatNotebook:
+                    self.panel = self.book.GetChildren()
+                    for self.subpanel in self.panel:
+                        if self.subpanel.GetName() == "pg_panel":
+                            self.mapnew = self.subpanel.Map
+        return self.mapnew
+                            
 
-	def OnCopy( self,event ):
-        #print "copy"
-		item =  self.tree.GetSelection()
-		parent = self.tree.GetItemParent(item)
-		pText = self.tree.GetItemText(parent)
-		name = self.GetCopyName(item)
-		if pText == "Raster Map" :
-			cmdflag = 'rast=' + self.tree.GetItemText(item) + ',' + name
-			self.tree.InsertItem(parent,item, name)
-		elif pText  == "Vector Map" :
-			cmdflag = 'vect=' + self.tree.GetItemText(item) + ',' + name
-			self.tree.InsertItem(parent,item, name)
+    def __createMenuBar(self):
+        """!Creates menubar"""
 
-		if cmdflag:
-			command = ["g.copy", cmdflag]
-			gcmd.CommandThread(command,stdout=None,stderr=None).run()
+        self.menubar = wx.MenuBar()
+        self.menudata = menudata.Data()
+        for eachMenuData in self.menudata.GetMenu():
+            for eachHeading in eachMenuData:
+                menuLabel = eachHeading[0]
+                menuItems = eachHeading[1]
+                self.menubar.Append(self.__createMenu(menuItems), menuLabel)
 
- 
-	def GetCopyName(self, item):
-		"""
-		Returns unique name depending on the mapname to be copied.
-		"""
+        self.SetMenuBar(self.menubar)
 
-		def GetPrefix(prefix):
-			"""
-			This returns a prefix to the given map name 
-			prefix applied here is _copy_x.
-			"""
+        return (self.menubar, self.menudata)
 
-			prefix = "_copy_" + str(self.count)
-			self.count = self.count + 1
-			return prefix
+    def __createCommandPrompt(self):
+        """!Creates command-line input area"""
+        p = prompt.GPrompt(parent=self.cmbPanel)
+
+        return p.GetPanel(), p.GetInput()
+
+    def __createMenu(self, menuData):
+        """!Creates menu"""
+
+        menu = wx.Menu()
+        for eachItem in menuData:
+            if len(eachItem) == 2:
+                label = eachItem[0]
+                subMenu = self.__createMenu(eachItem[1])
+                menu.AppendMenu(wx.ID_ANY, label, subMenu)
+            else:
+                self.__createMenuItem(menu, *eachItem)
+        self.Bind(wx.EVT_MENU_HIGHLIGHT_ALL, self.OnMenuHighlight)
+        return menu
+
+    def __createMenuItem(self, menu, label, help, handler, gcmd, keywords, shortcut = '', kind = wx.ITEM_NORMAL):
+        """!Creates menu items"""
+
+        if not label:
+            menu.AppendSeparator()
+            return
+
+        if len(gcmd) > 0:
+            helpString = gcmd + ' -- ' + help
+        else:
+            helpString = help
         
-            #end of GetPrefix
+        if shortcut:
+            label += '\t' + shortcut
+        
+        menuItem = menu.Append(wx.ID_ANY, label, helpString, kind)
+        
+        self.menucmd[menuItem.GetId()] = gcmd
 
-		def CheckName(parent,prefix,name):
-			"""
-			Checks all silbings of the parent wheather the name 
-			already exists.
-			"""
-			ncount = self.tree.GetChildrenCount(parent, False)
-			ck = 1
-			current , ck = self.tree.GetFirstChild(parent)
-			for i in range(ncount):
-				if str(self.tree.GetItemText(current)) == str(name + prefix):
-					return False
-				else:
-					current,ck = self.tree.GetNextChild(parent,ck)
-			return True
+        if len(gcmd) > 0 and \
+                gcmd.split()[0] not in globalvar.grassCmd['all']:
+            menuItem.Enable (False)
+
+        rhandler = eval(handler)
+
+        self.Bind(wx.EVT_MENU, rhandler, menuItem)
+
+
+
+    def __createToolBar(self):
+        """!Creates toolbar"""
+
+        self.toolbar = self.CreateToolBar()
+        self.toolbar.SetToolBitmapSize(globalvar.toolbarSize)
+
+        for each in self.ToolbarData():
+            self.AddToolbarButton(self.toolbar, *each)
+        self.toolbar.Realize()
+
+        return self.toolbar
+
+    def OnMenuHighlight(self, event):
+        """
+        Default menu help handler
+        """
+         # Show how to get menu item info from this event handler
+        id = event.GetMenuId()
+        item = self.GetMenuBar().FindItemById(id)
+        if item:
+            text = item.GetText()
+            help = item.GetHelp()
+
+        # but in this case just call Skip so the default is done
+        event.Skip()
+
+        
+    def __createNoteBook(self):
+        """!Creates notebook widgets"""
+
+        #create main notebook widget
+        nbStyle = FN.FNB_FANCY_TABS | \
+            FN.FNB_BOTTOM | \
+            FN.FNB_NO_NAV_BUTTONS | \
+            FN.FNB_NO_X_BUTTON
+        
+
+        self.disp_idx = -1
+  
+
+        # create displays notebook widget and add it to main notebook page
+        cbStyle = globalvar.FNPageStyle
+        self.notebook = FN.FlatNotebook(parent=self.pRight, id=wx.ID_ANY, style=cbStyle,name="mynotebook")
+        
+        self.notebook.SetTabAreaColour(globalvar.FNPageColor)
+
+
+        self.pg_panel = MapFrame(parent=self.notebook, id=wx.ID_ANY, Map=render.Map(),  size=globalvar.MAP_WINDOW_SIZE,frame=self,flag=True)
+        
+        self.disp_idx = self.disp_idx + 1
+        self.notebook.AddPage(self.pg_panel, text="Display "+ str(self.disp_idx), select = True)
+
+
+        self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
+        self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CLOSING, self.OnPageClosed)
+
+
+        return self.notebook
+
+
+    def OnGeorectify(self, event):
+        """
+        Launch georectifier module
+        """
+        georect.GeorectWizard(self)
+        
+    def OnMapsets(self, event):
+        """
+        Launch mapset access dialog
+        """
+        dlg = preferences.MapsetAccess(parent=self, id=wx.ID_ANY)
+        dlg.CenterOnScreen()
+
+        # if OK is pressed...
+        if dlg.ShowModal() == wx.ID_OK:
+            ms = dlg.GetMapsets()
+            # run g.mapsets with string of accessible mapsets
+            gcmd.RunCommand('g.mapsets',
+                            parent = self,
+                            mapset = '%s' % ','.join(ms))
             
-            #End of CheckName
+    def OnRDigit(self, event):
+        """
+        Launch raster digitizing module
+        """
+        pass
 
-        #GetCopyName function starts here
-		ext = None	
-		self.count  = 1
-		ext = GetPrefix(ext)
-		name = str(self.tree.GetItemText(item))
-		parent = self.tree.GetItemParent(item)
-		while  CheckName(parent,ext,name) == False:
-			ext = GetPrefix(ext)
-			CheckName(parent,ext,name)
+    def OnPageChanged(self,event):
+        self.current = event.GetSelection()
+        event.Skip()
 
-		name = str(name + ext)
-		return name
 
 
-	def OnRename( self,event ):
+    def OnPageClosed(self, event):
+        """
+        Page of notebook closed
+        Also close associated map display
+        """
 
-	    item = self.tree.GetSelection()
-	    self.tree.EditLabel( self.tree.GetSelection())
 
+#        if UserSettings.Get(group='manager', key='askOnQuit', subkey='enabled'):
+#            maptree = self.current.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.disp_idx),
+#                                       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()
 
-	def OnDelete( self,event ):
-		"""
-		Performs grass command for deleting a map
-		"""
-		item =  self.tree.GetSelection()
-		parent  =self.tree.GetItemParent(item) 
-		if self.tree.GetItemText(parent) == "Raster Map" :
-			cmdflag = 'rast=' + str(self.tree.GetItemText(item))
-		elif self.tree.GetItemText(parent) == "Vector Map" :
-			cmdflag = 'vect=' + str(self.tree.GetItemText(item))
 
-		if cmdflag:
-			command = ["g.remove", cmdflag]
-			gcmd.CommandThread(command,stdout=None,stderr=None).run()
-			select = self.tree.GetPrevSibling(item)
-			self.tree.Delete(item)
-			self.tree.SelectItem(select)
+        self.notebook.GetPage(event.GetSelection()).maptree.Map.Clean()
+        self.disp_idx = self.disp_idx - 1
+        self.notebook.DeletePage(self.notebook.GetCurrentPage())
+        self.current = self.notebook.GetCurrentPage()
+        self.current.Map.Clean()
+        event.Skip()
 
+        
+    def GetLogWindow(self):
+        """!Get widget for command output"""
+        return self.gmconsole.goutput
+    
+    def GetMenuCmd(self, event):
+        """!Get GRASS command from menu item
 
-	def OnCloseWindow(self,event):
+        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']:
+            return cmdlist
 
-		if self.gisrc['LOCATION_NAME'] != self.iLocation or \
-			self.gisrc['MAPSET'] != self.iMapset:
-			self.gisrc['LOCATION_NAME'] = self.iLocation
-			self.gisrc['MAPSET'] = self.iMapset
-			self.update_grassrc(self.gisrc)	
- 
-		self.Map.Clean()
-		event.Skip()
-        #self.Destroy()
-	
+        try:
+            layer = self.current.maptree.layer_selected
+            name = self.current.maptree.GetPyData(layer)[0]['maplayer'].name
+            type = self.current.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))
 
-	def OnDisplay(self, event):
-		item =  event.GetItem()
-		pText = self.tree.GetItemText(self.tree.GetItemParent(item)) 
+        return cmdlist
 
-		if not self.tree.ItemHasChildren(item):
-			self.mapname = "map=" + self.tree.GetItemText(item) + "@" +self.cmbMapset.GetValue()
-	        if pText == "Raster Map" :
-				self.cmd= ['d.rast', str(self.mapname)]
-				self.infocmd = ["r.info", str(self.mapname)]
-	        elif pText == "Vector Map" :
-				self.cmd= ['d.vect', str(self.mapname)]
-				self.infocmd = ["v.info", str(self.mapname)]
+    def RunMenuCmd(self, event):
+        """!Run command selected from menu"""
+        print "asdf"
+        #cmd = self.GetMenuCmd(event)
+        #goutput.GMConsole(self, pageid=1).RunCmd(cmd, switchPage=True)
 
-		if self.cmd:		
-			self.Map.Clean()
-			self.Map.__init__()			#to update projection and region
-			self.Map.AddLayer(type='raster', name='layer1', command=self.cmd)	
-			self.Map.region = self.Map.GetRegion()
-			self.MapWindow.flag = True
-			self.MapWindow.Map = self.Map
-			self.MapWindow.UpdateMap(render=True)
+    def OnMenuCmd(self, event, cmd = ''):
+        """!Parse command selected from menu"""
+        if event:
+            cmd = self.GetMenuCmd(event)
+        menuform.GUI().ParseCommand(cmd, parentframe=self)
 
-		if self.viewInfo is True:
-	   		gcmd.CommandThread(self.infocmd, stdout=self.mInfo).run()
-	   		lines = self.mInfo.GetValue()
-	   		lines = lines.replace('|','')
-	   		lines =lines.replace('+','')
-	   		lines =lines.replace('-','')
-	   		#lines =lines.replace('-+','-------------------------------+')
-	   		#lines =lines.replace('-\n','---------------------------------')
-	   		#lines = lines.replace('+','----+')
-			self.mInfo.SetValue(lines)
+    def OnChangeLocation(self, event):
+        """Change current location"""
+        print "asdf"
+                    
+    def OnChangeMapset(self, event):
+        """Change current mapset"""
+        print "asdf"
+        
+    def OnNewVector(self, event):
+        """!Create new vector map layer"""
+        name, add = gdialogs.CreateNewVector(self, cmd = (('v.edit',  { 'tool' : 'create' }, 'map')))
+        
+        if name and add:
+            # add layer to map layer tree
+            self.current.maptree.AddLayer(ltype='vector',
+                                            lname=name,
+                                            lchecked=True,
+                                            lopacity=1.0,
+                                            lcmd=['d.vect', 'map=%s' % name])
+           
+    def OnMenuTree(self, event):
+        """!Show dialog with menu tree"""
+        dlg = MenuTreeWindow(self)
+        dlg.CentreOnScreen()
+        dlg.Show()
+    
+    def OnAboutGRASS(self, event):
+        """!Display 'About GRASS' dialog"""
+        win = AboutWindow(self)
+        win.Centre()
+        win.Show(True)  
+        
+    def OnWorkspace(self, event):
+        """!Workspace menu (new, load)"""
+        point = wx.GetMousePosition()
+        menu = wx.Menu()
 
-	def OnMapsetChange(self,event):
-		"""
-		Create the tree nodes based on selected location and mapset.
-		Also update gisrc and grassrc files.
-		"""
-		self.tree.DeleteAllItems()
-		self.AddTreeNodes(self.cmbLocation.GetValue(),self.cmbMapset.GetValue())	
-		self.gisrc['LOCATION_NAME'] = str(self.cmbLocation.GetValue())
-		self.gisrc['MAPSET'] = str(self.cmbMapset.GetValue())
-		self.update_grassrc(self.gisrc)
-		#gcmd.RunCommand("g.gisenv", set = "MAPSET=%s" % str(self.cmbMapset.GetValue()))
+        # Add items to the menu
+        new = wx.MenuItem(menu, wx.ID_ANY, Icons["workspaceNew"].GetLabel())
+        new.SetBitmap(Icons["workspaceNew"].GetBitmap(self.iconsize))
+        menu.AppendItem(new)
+        self.Bind(wx.EVT_MENU, self.OnWorkspaceNew, new)
 
+        load = wx.MenuItem(menu, wx.ID_ANY, Icons["workspaceLoad"].GetLabel())
+        load.SetBitmap(Icons["workspaceLoad"].GetBitmap(self.iconsize))
+        menu.AppendItem(load)
+        self.Bind(wx.EVT_MENU, self.OnWorkspaceLoad, load)
 
-	def OnLocationChange(self,event):
-		"""
-		Populate mapset combobox with selected location.
-		"""
+        # create menu
+        self.PopupMenu(menu)
+        menu.Destroy()
 
-		self.cmbMapset.Clear()
-		self.cmbMapset.SetValue("Select Mapset")
-		self.tree.DeleteAllItems()
+    def OnWorkspaceNew(self, event=None):
+        """!Create new workspace file
 
-		maplists = self.GetMapsets(self.cmbLocation.GetValue())
-		for mapsets in maplists:
-			self.cmbMapset.Append(str(mapsets))
+        Erase current workspace settings first"""
 
+        Debug.msg(4, "GMFrame.OnWorkspaceNew():")
+        
+        # start new map display if no display is available
+        if not self.current:
+            self.NewDisplay()
+        
+        maptree = self.current.maptree
+        
+        # ask user to save current settings
+        if 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.current.maptree.SetPyData(maptree.root, (None,None))
+        
+        # no workspace file loaded
+        self.workspaceFile = None
+        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="*.gxw")
 
-	def AddTreeNodes(self,location,mapset):
-		"""
-		Adds tree nodes. raster,vector and dbf files are identified using 
-		their directory structure.
-		"""
+        filename = ''
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
 
-		root = self.tree.AddRoot('root')
-		node_raster = self.tree.AppendItem(root, "Raster Map")
-		node_vector = self.tree.AppendItem(root, "Vector Map")
-		node_dbf = self.tree.AppendItem(root, "DBF")
-		treeNodes = [node_raster,node_vector,node_dbf]
+        if filename == '':
+            return
 
-		glocs = glob.glob(os.path.join(self.gisdbase,location, mapset,"*"))
-		for gloc in glocs:
-			if not os.path.isfile(gloc) and os.path.isdir(gloc):
-				if(os.path.basename(gloc)=='cats'):
-					for rast in glob.glob(os.path.join(self.gisdbase,location, mapset,gloc, "*")):
-						self.tree.AppendItem(node_raster, os.path.basename(rast))
-				elif(os.path.basename(gloc)=='vector'):
-					for vect in glob.glob(os.path.join(self.gisdbase,location, mapset,gloc, "*")):
-						self.tree.AppendItem(node_vector, os.path.basename(vect))
-				elif(os.path.basename(gloc)=='dbf'):
-					for dfile in glob.glob(os.path.join(self.gisdbase,location, mapset,gloc, "*")):
-						self.tree.AppendItem(node_dbf, os.path.basename(dfile))
+        Debug.msg(4, "GMFrame.OnWorkspaceOpen(): filename=%s" % filename)
 
-        #Nodes with no children are given an italic type font
-		for node in treeNodes: 
-			if not self.tree.ItemHasChildren(node):
-				if self.tree.GetItemText(node) == 'Raster Map':
-					tmp_item = self.tree.AppendItem(node, "No raster maps found.")
-				elif self.tree.GetItemText(node) == 'Vector Map':
-					tmp_item = self.tree.AppendItem(node, "No vector maps found.")
-				elif self.tree.GetItemText(node) == 'DBF':
-					tmp_item = self.tree.AppendItem(node, "No DBF files found.")
-				self.tree.SetItemFont(tmp_item,self.itemFont)
+        # delete current layer tree content
+        self.OnWorkspaceClose()
+        
+        self.LoadWorkspaceFile(filename)
 
-	def GetMapsets(self,location):
-		"""
-		Read and returns all mapset int GRASS data directory.
-		"""
-		
-		maplist = []
-		for mapset in glob.glob(os.path.join(self.gisdbase, location, "*")):
-			if os.path.isdir(mapset) and os.path.isfile(os.path.join(self.gisdbase, location, mapset, "WIND")):
-				maplist.append(os.path.basename(mapset))
-		return maplist
+        self.workspaceFile = filename
+        self.SetTitle(self.baseTitle + " - " +  os.path.basename(self.workspaceFile))
 
-	def GetLocations(self):
-		"""
-		Read and returns all locations int GRASS data directory.
-		"""
-		loclist = []
-		for location in glob.glob(os.path.join(self.gisdbase, "*")):
-			if os.path.join(location, "PERMANENT") in glob.glob(os.path.join(location, "*")):
-				loclist.append(os.path.basename(location))
-		return loclist
+    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, err:
+            raise gcmd.GStdError(_("Reading workspace file <%(file)s> failed.\n"
+                                   "Invalid file, unable to parse XML document."
+                                   "\n\n%(err)s") % { 'file' : filename, 'err': err},
+                                 parent = self)
+        
+        busy = wx.BusyInfo(message=_("Please wait, loading workspace..."),
+                           parent=self)
+        wx.Yield()
 
-	def doBindings(self):
+        #
+        # 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'])
         
-		#Event bindings for combo boxes
-		self.Bind(wx.EVT_COMBOBOX,self.OnMapsetChange,self.cmbMapset)
-		self.Bind(wx.EVT_COMBOBOX,self.OnLocationChange,self.cmbLocation)
+        #
+        # start map displays first (list of layers can be empty)
+        #
+        displayId = 0
+        mapdisplay = []
+        for display in gxwXml.displays:
+            mapdisplay.append(self.NewDisplay())
+            maptree = self.notebook.GetPage(displayId).maptree
+            
+            # set windows properties
+            mapdisplay[-1].SetProperties(render=display['render'],
+                                         mode=display['mode'],
+                                         showCompExtent=display['showCompExtent'],
+                                         constrainRes=display['constrainRes'],
+                                         projection=display['projection']['enabled'])
 
-		#Event bindings for tree -(display,popup,label edit.)
-		self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnDisplay,self.tree)
-		self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK,self.OnTreePopUp,self.tree)
-		self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnEndRename,self.tree)
-		self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnBeginRename,self.tree)
+            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']:
+                    mapdisplay[-1].SetPosition(display['pos'])
+                if display['size']:
+                    mapdisplay[-1].SetSize(display['size'])
+                    
+            # set extent if defined
+            if display['extent']:
+                w, s, e, n = display['extent']
+                maptree.Map.region = maptree.Map.GetRegion(w=w, s=s, e=e, n=n)
+                
+            mapdisplay[-1].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.notebook.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()
+        
+        if maptree:
+            # reverse list of map layers
+            maptree.Map.ReverseListOfLayers()
 
-		#Event bindings for tree menu
-		self.Bind(wx.EVT_MENU,self.OnCopy,id=self.ID_COPY)
-		self.Bind(wx.EVT_MENU,self.OnRename,id=self.ID_REN)
-		self.Bind(wx.EVT_MENU,self.OnDelete,id=self.ID_DEL)
+        for mdisp in mapdisplay:
+            mdisp.MapWindow2D.UpdateMap()
 
+        return True
 
-		self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
+    def OnWorkspaceLoad(self, event=None):
+        """!Load given map layers into layer tree"""
+        dialog = gdialogs.LoadMapLayersDialog(parent=self, title=_("Load map layers into layer tree"))
 
-		#Event bindings for v/r.info checkbox
-		self.Bind(wx.EVT_CHECKBOX, self.OnToggleInfo,self.chkInfo)
-		self.Bind(wx.EVT_CHECKBOX, self.OnToggleExpand,self.treeExpand)
+        if dialog.ShowModal() == wx.ID_OK:
+            # start new map display if no display is available
+            if not self.current:
+                self.NewDisplay()
 
-	def doLayout(self):
+            maptree = self.current.maptree
+            busy = wx.BusyInfo(message=_("Please wait, loading workspace..."),
+                               parent=self)
+            wx.Yield()
 
-		#combo panel sizers
-		self.cmbSizer.Add(self.lbLocation,pos=(1,0),flag=wx.ALL,border=2)
-		self.cmbSizer.Add(self.lbMapset,pos=(1,1),flag=wx.ALL,border=2)
-		self.cmbSizer.Add(self.cmbLocation,pos=(2,0),flag=wx.ALL)
-		self.cmbSizer.Add(self.cmbMapset,pos=(2,1),flag=wx.ALL)
-		self.cmbSizer.Add(self.chkInfo,pos=(2,2),flag=wx.ALL)
-		self.cmbSizer.Add(self.treeExpand,pos=(2,3),flag=wx.ALL)
-		self.cmbPanel.SetSizer(self.cmbSizer)
+            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=True,
+                                           lopacity=1.0,
+                                           lcmd=cmd,
+                                           lgroup=None)
 
-		#splitter window sizers
-		self.mSizer.Add(self.cmbPanel,flag=wx.EXPAND)
-		self.mSizer.Add(self.win, 1, wx.EXPAND)
-		self.SetSizer(self.mSizer)
+            busy.Destroy()
 
-		#sizers for splitter window panels
-		self.leftSizer.Add(self.tree,1,wx.EXPAND)
-		self.rightSizer.Add(self.MapWindow)
-		self.rightSizer.Add(self.mInfo,1,wx.EXPAND)
-		self.pLeft.SetSizer(self.leftSizer)
-		self.pRight.SetSizer(self.rightSizer)
+    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="*.grc")
 
+        filename = ''
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
 
+        if filename == '':
+            return
 
-	def read_gisrc(self):
-		"""
-		Read variables from $HOME/.grassrc7 file
-		"""
+        Debug.msg(4, "GMFrame.OnWorkspaceLoadGrcFile(): filename=%s" % filename)
 
-		rc = {}
+        # start new map display if no display is available
+        if not self.current:
+            self.NewDisplay()
 
-		gisrc = os.getenv("GISRC")
+        busy = wx.BusyInfo(message=_("Please wait, loading workspace..."),
+                           parent=self)
+        wx.Yield()
 
-		if gisrc and os.path.isfile(gisrc):
-			try:
-				f = open(gisrc, "r")
-				for line in f.readlines():
-					key, val = line.split(":", 1)
-					rc[key.strip()] = val.strip()
-			finally:
-				f.close()
+        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'])
 
-		return rc
+            busy.Destroy()
+            
+        if maptree:
+            # reverse list of map layers
+            maptree.Map.ReverseListOfLayers()
 
-	def update_grassrc(self,gisrc):
-		"""
-		Update $HOME/.grassrc7 and gisrc files
-		"""
+    def OnWorkspaceSaveAs(self, event=None):
+        """!Save workspace definition to selected file"""
 
-		rc = os.getenv("GISRC")
-		grassrc = os.path.join(os.getenv('HOME'), ".grassrc7.%s" % os.uname()[1])
-		if not os.access(grassrc, os.R_OK):
-			grassrc = os.path.join(os.getenv('HOME'), ".grassrc7")
-		if rc and os.path.isfile(rc):
-			try:
-				f = open(rc, 'w')
-				for key, val in gisrc.iteritems():
-				    f.write("%s: %s\n" % (key, val))
-			finally:
-				f.close()
+        dlg = wx.FileDialog(parent=self, message=_("Choose file to save current workspace"),
+                            defaultDir=os.getcwd(), wildcard="*.gxw", style=wx.FD_SAVE)
 
-		if grassrc and os.path.isfile(grassrc):
-			try:
-				g = open(grassrc, 'w')
-				for key, val in gisrc.iteritems():
-				    g.write("%s: %s\n" % (key, val))
-			finally:
-				    g.close()
+        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)
+        else:
+            self.OnWorkspaceSaveAs()
+
+    def SaveToWorkspaceFile(self, filename):
+        """!Save layer tree layout to workspace file
+
+        Return True on success, False on error
+        """
+
+        try:
+            file = open(filename, "w")
+        except IOError:
+            wx.MessageBox(parent=self,
+                          message=_("Unable to open workspace file <%s> for writing.") % filename,
+                          caption=_("Error"), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            return False
+
+        try:
+            workspace.WriteWorkspaceFile(lmgr=self, file=file)
+        except StandardError, e:
+            file.close()
+            wx.MessageBox(parent=self,
+                          message=_("Writing current settings to workspace file failed (%s)." % e),
+                          caption=_("Error"),
+                          style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+            return False
+
+        file.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.workspaceFile = None
+        self.SetTitle(self.baseTitle)
+
+        displays = []
+        for page in range(0, self.notebook.GetPageCount()):
+            displays.append(self.notebook.GetPage(page).maptree.mapdisplay)
+        
+        for display in displays:
+            display.OnCloseWindow(event)
+        
+        self.disp_idx = 0
+        self.curr_page = None
+        
+
+    def RulesCmd(self, event, cmd = ''):
+        """
+        Launches dialog for commands that need rules
+        input and processes rules
+        """
+        if event:
+            cmd = self.GetMenuCmd(event)
+                
+        if cmd[0] == 'r.colors' or cmd[0] == 'vcolors':
+            ctable = colorrules.ColorTable(self, cmd=cmd[0])
+            ctable.Show()
+        else:
+            dlg = rules.RulesText(self, cmd=cmd)
+            dlg.CenterOnScreen()
+            if dlg.ShowModal() == wx.ID_OK:
+                gtemp = utils.GetTempfile()
+                output = open(gtemp, "w")
+                try:
+                    output.write(dlg.rules)
+                finally:
+                    output.close()
+    
+                cmdlist = [cmd[0],
+                           'input=%s' % dlg.inmap,
+                           'output=%s' % dlg.outmap,
+                           'rules=%s' % gtemp]
+    
+                if dlg.overwrite == True:
+                    cmdlist.append('--o')
+    
+                dlg.Destroy()
+    
+                self.goutput.RunCmd(cmdlist)
+
+    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()
+
+        self.dialogs['preferences'].ShowModal()
+        
+    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 DispMapCalculator(self, event):
+        """
+        Init map calculator for interactive creation of mapcalc statements
+        """
+        
+        self.mapcalculator = mapcalculator.MapCalcFrame(self, wx.ID_ANY, title='',
+                                                        dimension=2)
+
+    def Disp3DMapCalculator(self, event):
+        """
+        Init map calculator for interactive creation of mapcalc statements
+        """
+        
+        self.mapcalculator = mapcalculator.MapCalcFrame(self, wx.ID_ANY, title='',
+                                                        dimension=3)
+
+    def AddToolbarButton(self, toolbar, label, icon, help, handler):
+        """!Adds button to the given toolbar"""
+
+        if not label:
+            toolbar.AddSeparator()
+            return
+        tool = toolbar.AddLabelTool(id=wx.ID_ANY, label=label, bitmap=icon, shortHelp=help)
+        self.Bind(wx.EVT_TOOL, handler, tool)
+
+    def ToolbarData(self):
+
+        return   (
+                 ('newdisplay', Icons["newdisplay"].GetBitmap(),
+                  Icons["newdisplay"].GetLabel(), self.OnNewDisplay),
+                 ('', '', '', ''),
+                 ('workspaceLoad', Icons["workspaceLoad"].GetBitmap(),
+                  Icons["workspaceLoad"].GetLabel(), self.OnWorkspace),
+                 ('workspaceOpen', Icons["workspaceOpen"].GetBitmap(),
+                  Icons["workspaceOpen"].GetLabel(), self.OnWorkspaceOpen),
+                 ('workspaceSave', Icons["workspaceSave"].GetBitmap(),
+                  Icons["workspaceSave"].GetLabel(), self.OnWorkspaceSave),
+                 ('', '', '', ''),
+                 ('addrast', Icons["addrast"].GetBitmap(),
+                  Icons["addrast"].GetLabel(), self.OnAddRaster),
+                 ('addshaded', Icons["addshaded"].GetBitmap(),
+                  _("Add various raster-based map layers"), self.OnAddRasterMisc),
+                 ('addvect', Icons["addvect"].GetBitmap(),
+                  Icons["addvect"].GetLabel(), self.OnAddVector),
+                 ('addthematic', Icons["addthematic"].GetBitmap(),
+                  _("Add various vector-based map layer"), self.OnAddVectorMisc),
+                 ('addcmd',  Icons["addcmd"].GetBitmap(),
+                  Icons["addcmd"].GetLabel(),  self.OnAddCommand),
+                 ('addgrp',  Icons["addgrp"].GetBitmap(),
+                  Icons["addgrp"].GetLabel(), self.OnAddGroup),
+                 ('addovl',  Icons["addovl"].GetBitmap(),
+                  Icons["addovl"].GetLabel(), self.OnAddOverlay),
+                 ('delcmd',  Icons["delcmd"].GetBitmap(),
+                  Icons["delcmd"].GetLabel(), self.OnDeleteLayer),
+                 ('', '', '', ''),
+                 ('attrtable', Icons["attrtable"].GetBitmap(),
+                  Icons["attrtable"].GetLabel(), self.OnShowAttributeTable)
+                  )
+
+    def OnImportDxfFile(self, event):
+        """!Convert multiple DXF layers to GRASS vector map layers"""
+        dlg = gdialogs.MultiImportDialog(parent=self, type='dxf',
+                                         title=_("Import DXF layers"))
+        dlg.ShowModal()
+
+    def OnImportGdalLayers(self, event):
+        """!Convert multiple GDAL layers to GRASS raster map layers"""
+        dlg = gdialogs.MultiImportDialog(parent=self, type='gdal',
+                                         title=_("Import GDAL layers"))
+        dlg.ShowModal()
+
+    def OnLinkGdalLayers(self, event):
+        """!Link multiple GDAL layers to GRASS raster map layers"""
+        dlg = gdialogs.MultiImportDialog(parent=self, type='gdal',
+                                         title=_("Link GDAL layers"),
+                                         link = True)
+        dlg.ShowModal()
+        
+    def OnImportOgrLayers(self, event):
+        """!Convert multiple OGR layers to GRASS vector map layers"""
+        dlg = gdialogs.MultiImportDialog(parent=self, type='ogr',
+                                         title=_("Import OGR layers"))
+        dlg.ShowModal()
+    
+    def OnLinkOgrLayers(self, event):
+        """!Links multiple OGR layers to GRASS vector map layers"""
+        dlg = gdialogs.MultiImportDialog(parent=self, type='ogr',
+                                         title=_("Link OGR layers"),
+                                         link = True)
+        dlg.ShowModal()
+        
+    def OnImportWMS(self, event):
+        """!Import data from OGC WMS server"""
+        dlg = ogc_services.WMSDialog(parent = self, service = 'wms')
+        dlg.CenterOnScreen()
+        
+        if dlg.ShowModal() == wx.ID_OK: # -> import layers
+            layers = dlg.GetLayers()
+            
+            if len(layers.keys()) > 0:
+                for layer in layers.keys():
+                    cmd = ['r.in.wms',
+                           'mapserver=%s' % dlg.GetSettings()['server'],
+                           'layers=%s' % layer,
+                           'output=%s' % layer]
+                    styles = ','.join(layers[layer])
+                    if styles:
+                        cmd.append('styles=%s' % styles)
+                    self.goutput.RunCmd(cmd, switchPage = True)
+            else:
+                self.goutput.WriteWarning(_("Nothing to import. No WMS layer selected."))
+        
+        dlg.Destroy()
+        
+    def OnShowAttributeTable(self, event):
+        """
+        Show attribute table of the given vector map layer
+        """
+        if not self.current:
+            self.MsgNoLayerSelected()
+            return
+        
+        layer = self.current.maptree.layer_selected
+        # no map layer selected
+        if not layer:
+            self.MsgNoLayerSelected()
+            return
+        
+        # available only for vector map layers
+        try:
+            maptype = self.current.maptree.GetPyData(layer)[0]['maplayer'].type
+        except:
+            maptype = None
+        
+        if not maptype or maptype != 'vector':
+            wx.MessageBox(parent=self,
+                          message=_("Attribute management is available only "
+                                    "for vector maps."),
+                          caption=_("Message"),
+                          style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+            return
+        
+        if not self.current.maptree.GetPyData(layer)[0]:
+            return
+        dcmd = self.current.maptree.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)
+        
+        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):
+        """!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.disp_idx = self.disp_idx + 1
+#        self.curr_pagenum  = self.disp_idx
+    
+        self.page = MapFrame(parent=self.notebook, id=wx.ID_ANY, Map=render.Map(),  size=globalvar.MAP_WINDOW_SIZE,frame=self)
+        self.notebook.AddPage(self.page, text="Display "+ str(self.disp_idx), select = True)
+
+        self.current = self.notebook.GetCurrentPage()
+
+        
+
+    # toolBar button handlers
+    def OnAddRaster(self, event):
+        """!Add raster map layer"""
+        #create image list to use with layer tree
+        il = wx.ImageList(16, 16, mask=False)
+
+        trart = wx.ArtProvider.GetBitmap(wx.ART_FOLDER_OPEN, wx.ART_OTHER, (16, 16))
+        self.folder_open = il.Add(trart)
+        trart = wx.ArtProvider.GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, (16, 16))
+        self.folder = il.Add(trart)
+
+        bmpsize = (16, 16)
+        trgif = Icons["addrast"].GetBitmap(bmpsize)
+        self.rast_icon = il.Add(trgif)
+
+        trgif = Icons["addrast3d"].GetBitmap(bmpsize)
+        self.rast3d_icon = il.Add(trgif)
+
+        trgif = Icons["addrgb"].GetBitmap(bmpsize)
+        self.rgb_icon = il.Add(trgif)
+        
+        trgif = Icons["addhis"].GetBitmap(bmpsize)
+        self.his_icon = il.Add(trgif)
+
+        trgif = Icons["addshaded"].GetBitmap(bmpsize)
+        self.shaded_icon = il.Add(trgif)
+
+        trgif = Icons["addrarrow"].GetBitmap(bmpsize)
+        self.rarrow_icon = il.Add(trgif)
+
+        trgif = Icons["addrnum"].GetBitmap(bmpsize)
+        self.rnum_icon = il.Add(trgif)
+
+        trgif = Icons["addvect"].GetBitmap(bmpsize)
+        self.vect_icon = il.Add(trgif)
+
+        trgif = Icons["addthematic"].GetBitmap(bmpsize)
+        self.theme_icon = il.Add(trgif)
+
+        trgif = Icons["addchart"].GetBitmap(bmpsize)
+        self.chart_icon = il.Add(trgif)
+
+        trgif = Icons["addgrid"].GetBitmap(bmpsize)
+        self.grid_icon = il.Add(trgif)
+
+        trgif = Icons["addgeodesic"].GetBitmap(bmpsize)
+        self.geodesic_icon = il.Add(trgif)
+
+        trgif = Icons["addrhumb"].GetBitmap(bmpsize)
+        self.rhumb_icon = il.Add(trgif)
+
+        trgif = Icons["addlabels"].GetBitmap(bmpsize)
+        self.labels_icon = il.Add(trgif)
+
+        trgif = Icons["addcmd"].GetBitmap(bmpsize)
+        self.cmd_icon = il.Add(trgif)
+
+#        self.current.maptree.AssignImageList(il)
+
+        
+        self.AddRaster(event)
+        
+    def OnAddRasterMisc(self, event):
+        """!Add raster menu"""
+        # start new map display if no display is available
+        if not self.current:
+            self.NewDisplay()
+
+        point = wx.GetMousePosition()
+        rastmenu = wx.Menu()
+
+        # add items to the menu
+        if self.current.maptree.mapdisplay.toolbars['nviz']:
+            addrast3d = wx.MenuItem(rastmenu, -1, Icons ["addrast3d"].GetLabel())
+            addrast3d.SetBitmap(Icons["addrast3d"].GetBitmap (self.iconsize))
+            rastmenu.AppendItem(addrast3d)
+            self.Bind(wx.EVT_MENU, self.AddRaster3d, addrast3d)
+
+        addshaded = wx.MenuItem(rastmenu, -1, Icons ["addshaded"].GetLabel())
+        addshaded.SetBitmap(Icons["addshaded"].GetBitmap (self.iconsize))
+        rastmenu.AppendItem(addshaded)
+        self.Bind(wx.EVT_MENU, self.AddShaded, addshaded)
+
+        addrgb = wx.MenuItem(rastmenu, -1, Icons["addrgb"].GetLabel())
+        addrgb.SetBitmap(Icons["addrgb"].GetBitmap(self.iconsize))
+        rastmenu.AppendItem(addrgb)
+        self.Bind(wx.EVT_MENU, self.AddRGB, addrgb)
+
+        addhis = wx.MenuItem(rastmenu, -1, Icons ["addhis"].GetLabel())
+        addhis.SetBitmap(Icons["addhis"].GetBitmap (self.iconsize))
+        rastmenu.AppendItem(addhis)
+        self.Bind(wx.EVT_MENU, self.AddHIS, addhis)
+
+        addrastarrow = wx.MenuItem(rastmenu, -1, Icons ["addrarrow"].GetLabel())
+        addrastarrow.SetBitmap(Icons["addrarrow"].GetBitmap (self.iconsize))
+        rastmenu.AppendItem(addrastarrow)
+        self.Bind(wx.EVT_MENU, self.AddRastarrow, addrastarrow)
+
+        addrastnums = wx.MenuItem(rastmenu, -1, Icons ["addrnum"].GetLabel())
+        addrastnums.SetBitmap(Icons["addrnum"].GetBitmap (self.iconsize))
+        rastmenu.AppendItem(addrastnums)
+        self.Bind(wx.EVT_MENU, self.AddRastnum, addrastnums)
+
+        # Popup the menu.  If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.PopupMenu(rastmenu)
+        rastmenu.Destroy()
+        
+        # show map display
+        #self.curr_page.maptree.mapdisplay.Show()
+
+    def OnAddVector(self, event):
+        """!Add vector map layer"""
+        # start new map display if no display is available
+        if not self.current:
+            self.NewDisplay()
+        
+        self.AddVector(event)
+        
+    def OnAddVectorMisc(self, event):
+        """!Add vector menu"""
+        # start new map display if no display is available
+        if not self.current:
+            self.NewDisplay()
+
+        point = wx.GetMousePosition()
+        vectmenu = wx.Menu()
+        
+        addtheme = wx.MenuItem(vectmenu, -1, Icons["addthematic"].GetLabel())
+        addtheme.SetBitmap(Icons["addthematic"].GetBitmap(self.iconsize))
+        vectmenu.AppendItem(addtheme)
+        self.Bind(wx.EVT_MENU, self.AddThemeMap, addtheme)
+
+        addchart = wx.MenuItem(vectmenu, -1, Icons["addchart"].GetLabel())
+        addchart.SetBitmap(Icons["addchart"].GetBitmap(self.iconsize))
+        vectmenu.AppendItem(addchart)
+        self.Bind(wx.EVT_MENU, self.AddThemeChart, addchart)
+
+        # Popup the menu.  If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.PopupMenu(vectmenu)
+        vectmenu.Destroy()
+
+        # show map display
+        #self.curr_page.maptree.mapdisplay.Show()
+
+    def OnAddOverlay(self, event):
+        """!Add overlay menu""" 
+        # start new map display if no display is available
+        if not self.curent:
+            self.NewDisplay()
+
+        point = wx.GetMousePosition()
+        ovlmenu = wx.Menu()
+
+        addgrid = wx.MenuItem(ovlmenu, wx.ID_ANY, Icons["addgrid"].GetLabel())
+        addgrid.SetBitmap(Icons["addgrid"].GetBitmap(self.iconsize))
+        ovlmenu.AppendItem(addgrid)
+        self.Bind(wx.EVT_MENU, self.AddGrid, addgrid)
+        
+        addlabels = wx.MenuItem(ovlmenu, wx.ID_ANY, Icons["addlabels"].GetLabel())
+        addlabels.SetBitmap(Icons["addlabels"].GetBitmap(self.iconsize))
+        ovlmenu.AppendItem(addlabels)
+        self.Bind(wx.EVT_MENU, self.OnAddLabels, addlabels)
+        
+        addgeodesic = wx.MenuItem(ovlmenu, wx.ID_ANY, Icons["addgeodesic"].GetLabel())
+        addgeodesic.SetBitmap(Icons["addgeodesic"].GetBitmap(self.iconsize))
+        ovlmenu.AppendItem(addgeodesic)
+        self.Bind(wx.EVT_MENU, self.AddGeodesic, addgeodesic)
+        
+        addrhumb = wx.MenuItem(ovlmenu, wx.ID_ANY, Icons["addrhumb"].GetLabel())
+        addrhumb.SetBitmap(Icons["addrhumb"].GetBitmap(self.iconsize))
+        ovlmenu.AppendItem(addrhumb)
+        self.Bind(wx.EVT_MENU, self.AddRhumb, addrhumb)
+
+        # Popup the menu.  If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.PopupMenu(ovlmenu)
+        ovlmenu.Destroy()
+
+        # show map display
+        self.curr_page.maptree.mapdisplay.Show()
+
+    def AddRaster(self, event):
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('raster') 
+
+    def AddRaster3d(self, event):
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('3d-raster')
+
+    def AddRGB(self, event):
+        """!Add RGB layer"""
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('rgb')
+
+    def AddHIS(self, event):
+        """!Add HIS layer"""
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('his')
+
+    def AddShaded(self, event):
+        """!Add shaded relief map layer"""
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('shaded')
+
+    def AddRastarrow(self, event):
+        """!Add raster flow arrows map"""
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('rastarrow')
+
+    def AddRastnum(self, event):
+        """!Add raster map with cell numbers"""
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('rastnum')
+
+    def AddVector(self, event):
+        """!Add vector layer"""
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('vector')
+
+    def AddThemeMap(self, event):
+        """!Add thematic map layer"""
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('thememap')
+
+    def AddThemeChart(self, event):
+        """!Add thematic chart layer"""
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('themechart')
+
+    def OnAddCommand(self, event):
+        """!Add command line layer"""
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('command')
+
+    def OnAddGroup(self, event):
+        """!Add layer group"""
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('group')
+
+    def AddGrid(self, event):
+        """!Add layer grid"""
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('grid')
+
+    def AddGeodesic(self, event):
+        """!Add layer geodesic"""
+        self.curr_page.maptree.AddLayer('geodesic')
+
+    def AddRhumb(self, event):
+        """!Add layer rhumb"""
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('rhumb')
+
+    def OnAddLabels(self, event):
+        """!Add layer vector labels"""
+        if not self.current:
+            self.NewDisplay()
+
+        self.current.maptree.AddLayer('labels')
+
+    def OnDeleteLayer(self, event):
+        """
+        Delete selected map display layer in GIS Manager tree widget
+        """
+        if UserSettings.Get(group='manager', key='askOnRemoveLayer', subkey='enabled'):
+            layerName = ''
+            for item in self.current.maptree.GetSelections():
+                name = str(self.current.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.CANCEL | wx.ICON_QUESTION)
+
+            if dlg.ShowModal() in [wx.ID_NO, wx.ID_CANCEL]:
+                dlg.Destroy()
+                return
+
+            dlg.Destroy()
+
+        for layer in self.current.maptree.GetSelections():
+            if self.current.maptree.GetPyData(layer)[0]['type'] == 'group':
+                self.current.maptree.DeleteChildren(layer)
+            self.current.maptree.Delete(layer)
+        
+    def OnKey(self, event):
+        """!Check hotkey"""
+        try:
+            kc = chr(event.GetKeyCode())
+        except ValueError:
+            event.Skip()
+            return
+        
+        if event.AltDown():
+            if kc == 'R':
+                self.OnAddRaster(None)
+            elif kc == 'V':
+                self.OnAddVector(None)
+        
+        event.Skip()
+        
+    def OnCloseWindow(self, event):
+        """!Cleanup when wxGUI is quit"""
+        count = self.notebook.GetPageCount()
+        index = 0
+        while index < count:
+            self.current = self.notebook.GetPage(index)
+            self.current.Map.Clean()
+            index = index+1
+
+        self.notebook.DeleteAllPages()
+        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)
+          
+
+
+    def OnMapsetChange(self,event):
+        """
+        Create the tree nodes based on selected location and mapset.
+        Also update gisrc and grassrc files.
+        """
+        self.ltree.DeleteAllItems()
+        self.ltree.AddTreeNodes(self.cmbLocation.GetValue(),self.cmbMapset.GetValue())	
+        self.gisrc['LOCATION_NAME'] = str(self.cmbLocation.GetValue())
+        self.gisrc['MAPSET'] = str(self.cmbMapset.GetValue())
+        self.update_grassrc(self.gisrc)
+        #gcmd.RunCommand("g.gisenv", set = "MAPSET=%s" % str(self.cmbMapset.GetValue()))
+
+
+    def OnLocationChange(self,event):
+        """
+        Populate mapset combobox with selected location.
+        """
+
+        self.cmbMapset.Clear()
+        self.cmbMapset.SetValue("Select Mapset")
+        self.ltree.DeleteAllItems()
+
+        maplists = self.GetMapsets(self.cmbLocation.GetValue())
+        for mapsets in maplists:
+	        self.cmbMapset.Append(str(mapsets))
+
+    def GetMapsets(self,location):
+        """
+        Read and returns all mapset int GRASS data directory.
+        """
+
+        maplist = []
+        for mapset in glob.glob(os.path.join(self.gisdbase, location, "*")):
+	        if os.path.isdir(mapset) and os.path.isfile(os.path.join(self.gisdbase, location, mapset, "WIND")):
+		        maplist.append(os.path.basename(mapset))
+        return maplist
+
+    def GetLocations(self):
+	    """
+	    Read and returns all locations int GRASS data directory.
+	    """
+	    loclist = []
+	    for location in glob.glob(os.path.join(self.gisdbase, "*")):
+		    if os.path.join(location, "PERMANENT") in glob.glob(os.path.join(location, "*")):
+			    loclist.append(os.path.basename(location))
+	    return loclist
+
+
+    def doBindings(self):
+        
+	    #Event bindings for combo boxes
+	    self.Bind(wx.EVT_COMBOBOX,self.OnMapsetChange,self.cmbMapset)
+	    self.Bind(wx.EVT_COMBOBOX,self.OnLocationChange,self.cmbLocation)
+
+	    #Event bindings for tree -(display,popup,label edit.)
+	    self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.ltree.OnDisplay,self.ltree)
+	    self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK,self.ltree.OnTreePopUp,self.ltree)
+	    self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.ltree.OnEndRename,self.ltree)
+	    self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.ltree.OnBeginRename,self.ltree)
+
+	    #Event bindings for tree menu
+	    self.Bind(wx.EVT_MENU,self.ltree.OnCopy,id=self.ltree.ID_COPY)
+	    self.Bind(wx.EVT_MENU,self.ltree.OnRename,id=self.ltree.ID_REN)
+	    self.Bind(wx.EVT_MENU,self.ltree.OnDelete,id=self.ltree.ID_DEL)
+
+
+	    self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
+
+	    #Event bindings for v/r.info checkbox
+	    #self.Bind(wx.EVT_CHECKBOX, self.OnToggleInfo,self.chkInfo)
+	    #self.Bind(wx.EVT_CHECKBOX, self.OnToggleExpand,self.treeExpand)
+
+    def OnToggleExpand(self,event):
+        if self.treeExpand.IsChecked():
+            if not self.gmconsole:
+                self.gmconsole = GLog(parent=self)
+               # self.gmconsole.show()
+#                sys.exit(0)
+            else:
+                self.gmconsole.Raise()
+        else:
+            self.gmconsole.Destroy()
+ 
+    def doLayout(self):
+
+	    #combo panel sizers
+        self.cmbSizer.Add(self.cmbLocation)
+        self.cmbSizer.Add(self.cmbMapset)
+        self.cmbSizer.Add(self.treeExpand)
+        #splitter window sizers
+        self.mSizer.Add(self.cmbPanel,flag=wx.EXPAND)
+        self.mSizer.Add(self.win, 1, wx.EXPAND)
+        self.leftSizer.Add(self.ltree,1,wx.EXPAND)
+        self.rightSizer.Add(self.notebook,1,wx.EXPAND)
+
+        self.cmbPanel.SetSizer(self.cmbSizer)
+        self.SetSizer(self.mSizer)
+        self.pLeft.SetSizer(self.leftSizer)
+        self.pRight.SetSizer(self.rightSizer)
+
+
+
+    def read_gisrc(self):
+	    """
+	    Read variables from $HOME/.grassrc7 file
+	    """
+
+	    rc = {}
+
+	    gisrc = os.getenv("GISRC")
+
+	    if gisrc and os.path.isfile(gisrc):
+		    try:
+			    f = open(gisrc, "r")
+			    for line in f.readlines():
+				    key, val = line.split(":", 1)
+				    rc[key.strip()] = val.strip()
+		    finally:
+			    f.close()
+
+	    return rc
+
+    def update_grassrc(self,gisrc):
+	    """
+	    Update $HOME/.grassrc7 and gisrc files
+	    """
+
+	    rc = os.getenv("GISRC")
+	    grassrc = os.path.join(os.getenv('HOME'), ".grassrc7.%s" % os.uname()[1])
+	    if not os.access(grassrc, os.R_OK):
+		    grassrc = os.path.join(os.getenv('HOME'), ".grassrc7")
+	    if rc and os.path.isfile(rc):
+		    try:
+			    f = open(rc, 'w')
+			    for key, val in gisrc.iteritems():
+			        f.write("%s: %s\n" % (key, val))
+		    finally:
+			    f.close()
+
+	    if grassrc and os.path.isfile(grassrc):
+		    try:
+			    g = open(grassrc, 'w')
+			    for key, val in gisrc.iteritems():
+			        g.write("%s: %s\n" % (key, val))
+		    finally:
+			        g.close()
+
+
+
 #End of DataCatalog class
 
+
+
 class CatalogApp(wx.App):
 
     def OnInit(self):
-        wx.InitAllImageHandlers()
         self.catalog = DataCatalog()
         self.catalog.Show()
-	    #self.catalog.Maximize()
+        self.catalog.Maximize()
 	return 1
 
 

Added: grass-addons/gui/wxpython/data_catalog/gmconsole.py
===================================================================
--- grass-addons/gui/wxpython/data_catalog/gmconsole.py	                        (rev 0)
+++ grass-addons/gui/wxpython/data_catalog/gmconsole.py	2010-02-03 16:29:51 UTC (rev 40794)
@@ -0,0 +1,120 @@
+"""
+ at package gmconsole.py
+
+ at brief window to ouptut grass command ouput.
+
+Classes:
+ - GLog
+
+
+(C) 2006-2009 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS
+for details.
+
+ at author Michael Barton (Arizona State University)
+ at author Jachym Cepicky (Mendel University of Agriculture)
+ at author Martin Landa <landa.martin gmail.com>
+ at author Mohammed Rashad K.M <rashadkm at gmail dot com> (modified for DataCatalog)
+"""
+
+import sys
+import os
+import time
+import traceback
+import re
+import string
+import getopt
+import platform
+
+### 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)
+
+pypath = "/usr/local/grass-7.0.svn/etc/wxpython"
+sys.path.append(pypath)
+
+import gui_modules
+gmpath = gui_modules.__path__[0]
+sys.path.append(gmpath)
+
+import images
+imagepath = images.__path__[0]
+sys.path.append(imagepath)
+
+import icons
+gmpath = icons.__path__[0]
+sys.path.append(gmpath)
+
+import gui_modules.globalvar as globalvar
+if not os.getenv("GRASS_WXBUNDLED"):
+    globalvar.CheckForWx()
+
+import wx
+import wx.aui
+import wx.combo
+import wx.html
+import wx.stc
+import wx.lib.customtreectrl as CT
+import wx.lib.flatnotebook as FN
+
+grassPath = os.path.join(globalvar.ETCDIR, "python")
+sys.path.append(grassPath)
+
+import gui_modules.preferences as preferences
+
+
+UserSettings = preferences.globalSettings
+
+class GLog(wx.Frame):
+    """
+    GIS Manager frame with notebook widget for controlling
+    GRASS GIS. Includes command console page for typing GRASS
+    (and other) commands, tree widget page for managing GIS map layers.
+    """
+    def __init__(self, parent=None, id=wx.ID_ANY, title=_("Command Output")):
+ 
+
+        wx.Frame.__init__(self, parent=parent, id=id, size=(550, 450),
+                          style=wx.DEFAULT_FRAME_STYLE,title="Command Output")
+                          
+       
+        self.SetName("LayerManager")
+        self.notebook  = self.__createNoteBook()
+        self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
+        self.Show()
+        self.SetBackgroundColour("white")
+        self.goutput.Redirect()
+
+
+    def OnCloseWindow(self,event):
+        self.Destroy()
+ 
+    def __createNoteBook(self):
+        """!Creates notebook widgets"""
+        nbStyle = FN.FNB_FANCY_TABS | \
+            FN.FNB_BOTTOM | \
+            FN.FNB_NO_NAV_BUTTONS | \
+            FN.FNB_NO_X_BUTTON
+        
+        self.notebook = FN.FlatNotebook(parent=self, id=wx.ID_ANY, style=nbStyle)
+        self.goutput = goutput.GMConsole(self, pageid=1)
+        self.outpage = self.notebook.AddPage(self.goutput, text=_("Command output"))
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        self.notebook.SetSize(wx.Size(600,600))
+        sizer.Add(item=self.goutput, proportion=1,flag=wx.EXPAND | wx.ALL, border=10)
+        self.SetSizer(sizer)
+        self.Layout()
+        self.Centre()
+        return self.notebook  
+
+
+
+
+   

Added: grass-addons/gui/wxpython/data_catalog/mapdisplay.py
===================================================================
--- grass-addons/gui/wxpython/data_catalog/mapdisplay.py	                        (rev 0)
+++ grass-addons/gui/wxpython/data_catalog/mapdisplay.py	2010-02-03 16:29:51 UTC (rev 40794)
@@ -0,0 +1,2089 @@
+"""!
+ at package mapdisp.py
+
+ at brief GIS map display canvas, with toolbar for various display
+management functions, and additional toolbars (vector digitizer, 3d
+view).
+
+Can be used either from Layer Manager or as p.mon backend.
+
+Classes:
+- MapFrame
+- MapApp
+
+Usage:
+python mapdisp.py monitor-identifier /path/to/command/file
+
+(C) 2006-2009 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS
+for details.
+
+ at author Michael Barton
+ at author Jachym Cepicky
+ at author Martin Landa <landa.martin gmail.com>
+ at author Mohammed Rashad K.M <rashadkm at gmail dot com> (modified for DataCatalog)
+"""
+
+import os
+import sys
+import glob
+import math
+import tempfile
+import copy
+
+gbase = os.getenv("GISBASE") 
+pypath = os.path.join(gbase,'etc','wxpython','gui_modules')
+sys.path.append(pypath)
+
+import globalvar
+if not os.getenv("GRASS_WXBUNDLED"):
+    globalvar.CheckForWx()
+import wx
+import wx.aui
+import wx_utils as wx_utils
+
+try:
+    import subprocess
+except:
+    CompatPath = os.path.join(globalvar.ETCWXDIR)
+    sys.path.append(CompatPath)
+    from compat import subprocess
+
+gmpath = os.path.join(globalvar.ETCWXDIR, "icons")
+sys.path.append(gmpath)
+
+grassPath = os.path.join(globalvar.ETCDIR, "python")
+sys.path.append(grassPath)
+
+import render
+import toolbars
+import menuform
+import gselect
+import disp_print
+import gcmd
+import dbm
+import dbm_dialogs
+import histogram
+import profile
+import globalvar
+import utils
+import gdialogs
+from grass.script import core as grass
+from debug import Debug
+from preferences import globalSettings as UserSettings
+
+
+from mapdisp_window import BufferedWindow
+
+
+
+import images
+imagepath = images.__path__[0]
+sys.path.append(imagepath)
+
+###
+### global variables
+###
+# for standalone app
+cmdfilename = None
+
+class MapFrame(wx.Panel):
+    """
+    Main frame for map display window. Drawing takes place in child double buffered
+    drawing window.
+    """
+
+    def __init__(self, parent=None, id=wx.ID_ANY, title=_("GRASS GIS - Map display"),
+                 pos=wx.DefaultPosition, size=wx.DefaultSize,
+                 style=wx.DEFAULT_FRAME_STYLE, toolbars=["map"],
+                 tree=None, notebook=None, lmgr=None, page=None,
+                 Map=None, auimgr=None,frame=None,flag=False):
+        """
+        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 mgr Layer Manager
+        @param page notebook page with layer tree
+        @param Map instance of render.Map
+        """
+        self._layerManager = lmgr   # Layer Manager object
+        self.Map        = Map       # instance of render.Map
+        self.tree       = tree      # Layer Manager layer tree object
+        self.page       = page      # Notebook page holding the layer tree
+        self.layerbook  = notebook  # Layer Manager layer tree notebook
+        self.parent     = parent
+        self.frame = frame
+        self.statusFlag = flag
+        self.statusbar = None
+
+        
+        #
+        # 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)
+            }
+
+        #wx.Frame.__init__(self, parent, id, title, pos, size, style)
+
+
+        wx.Panel.__init__(self, parent, id, pos, size, style,name="pg_panel")
+        #self.SetName("MapWindow")
+
+        #
+        # set the size & system icon
+        #
+        #self.SetClientSize(size)
+        self.iconsize = (16, 16)
+
+
+
+
+        #self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
+
+        #
+        # Fancy gui
+        #
+        # self._mgr = auimgr
+        self._mgr = wx.aui.AuiManager(self)
+
+        #
+        # Add toolbars
+        #
+        self.toolbars = { 'map' : None,
+                          'vdigit' : None,
+                          'georect' : None, 
+                          'nviz' : None }
+        for toolb in toolbars:
+            self.AddToolbar(toolb)
+
+        #
+        # Add statusbar
+        #
+        if self.statusFlag:
+            self.statusbar = self.frame.CreateStatusBar(number=4, style=0)
+            self.statusbar.SetStatusWidths([-5, -2, -1, -1])
+        else:
+            self.statusbar = self.frame.GetStatusBar()
+
+            
+
+        self.statusbarWin = dict()
+        self.statusbarWin['toggle'] = wx.Choice(self.statusbar, wx.ID_ANY,
+                                                choices = globalvar.MAP_DISPLAY_STATUSBAR_MODE)
+        self.statusbarWin['toggle'].SetSelection(UserSettings.Get(group='display',
+                                                                  key='statusbarMode',
+                                                                  subkey='selection'))
+
+        self.statusbar.Bind(wx.EVT_CHOICE, self.OnToggleStatus, self.statusbarWin['toggle'])
+
+        # auto-rendering checkbox
+        self.statusbarWin['render'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
+                                                  label=_("Render"))
+
+        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleRender, self.statusbarWin['render'])
+
+        self.statusbarWin['render'].SetValue(UserSettings.Get(group='display',
+                                                              key='autoRendering',
+                                                              subkey='enabled'))
+        self.statusbarWin['render'].SetToolTip(wx.ToolTip (_("Enable/disable auto-rendering")))
+        # show region
+        self.statusbarWin['region'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
+                                                  label=_("Show computational extent"))
+
+
+        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleShowRegion, self.statusbarWin['region'])
+        
+        self.statusbarWin['region'].SetValue(False)
+        self.statusbarWin['region'].Hide()
+        self.statusbarWin['region'].SetToolTip(wx.ToolTip (_("Show/hide computational "
+                                                             "region extent (set with g.region). "
+                                                             "Display region drawn as a blue box inside the "
+                                                             "computational region, "
+                                                             "computational region inside a display region "
+                                                             "as a red box).")))
+        # set resolution
+        self.statusbarWin['resolution'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
+                                                      label=_("Constrain display resolution to computational settings"))
+        self.statusbar.Bind(wx.EVT_CHECKBOX, self.OnToggleResolution, self.statusbarWin['resolution'])
+        self.statusbarWin['resolution'].SetValue(UserSettings.Get(group='display', key='compResolution', subkey='enabled'))
+        self.statusbarWin['resolution'].Hide()
+        self.statusbarWin['resolution'].SetToolTip(wx.ToolTip (_("Constrain display resolution "
+                                                                 "to computational region settings. "
+                                                                 "Default value for new map displays can "
+                                                                 "be set up in 'User GUI settings' dialog.")))
+        # map scale
+        self.statusbarWin['mapscale'] = wx.TextCtrl(parent=self.statusbar, id=wx.ID_ANY,
+                                                    value="", style=wx.TE_PROCESS_ENTER,
+                                                    size=(150, -1))
+        self.statusbarWin['mapscale'].Hide()
+        self.statusbar.Bind(wx.EVT_TEXT_ENTER, self.OnChangeMapScale, self.statusbarWin['mapscale'])
+
+        # go to
+        self.statusbarWin['goto'] = wx.TextCtrl(parent=self.statusbar, id=wx.ID_ANY,
+                                                value="", style=wx.TE_PROCESS_ENTER,
+                                                size=(300, -1))
+        self.statusbarWin['goto'].Hide()
+        self.statusbar.Bind(wx.EVT_TEXT_ENTER, self.OnGoTo, self.statusbarWin['goto'])
+
+        # projection
+        self.statusbarWin['projection'] = wx.CheckBox(parent=self.statusbar, id=wx.ID_ANY,
+                                                      label=_("Use defined projection"))
+        self.statusbarWin['projection'].SetValue(False)
+        size = self.statusbarWin['projection'].GetSize()
+        self.statusbarWin['projection'].SetMinSize((size[0] + 150, size[1]))
+        self.statusbarWin['projection'].SetToolTip(wx.ToolTip (_("Reproject coordinates displayed "
+                                                                 "in the statusbar. Projection can be "
+                                                                 "defined in GUI preferences dialog "
+                                                                 "(tab 'Display')")))
+        self.statusbarWin['projection'].Hide()
+        
+        # mask
+        self.statusbarWin['mask'] = wx.StaticText(parent = self.statusbar, id = wx.ID_ANY,
+                                                  label = '')
+        self.statusbarWin['mask'].SetForegroundColour(wx.Colour(255, 0, 0))
+        
+        # on-render gauge
+        self.statusbarWin['progress'] = wx.Gauge(parent=self.statusbar, id=wx.ID_ANY,
+                                      range=0, style=wx.GA_HORIZONTAL)
+        self.statusbarWin['progress'].Hide()
+        
+        self.StatusbarReposition() # reposition statusbar
+
+        #
+        # 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.Bind(wx.EVT_MOTION, self.OnMotion)
+        self.MapWindow.SetCursor(self.cursors["default"])
+        # used by Nviz (3D display mode)
+        self.MapWindow3D = None 
+
+        #
+        # initialize region values
+        #
+        self.__InitDisplay() 
+
+        #
+        # Bind various events
+        #
+        self.Bind(wx.EVT_ACTIVATE, self.OnFocus)
+        self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
+        self.Bind(render.EVT_UPDATE_PRGBAR, self.OnUpdateProgress)
+        
+        #
+        # Update fancy gui style
+        #
+        self._mgr.AddPane(self.MapWindow, wx.aui.AuiPaneInfo().CentrePane().
+                          Dockable(False).BestSize((-1,-1)).
+                          CloseButton(False).DestroyOnClose(True).
+                          Layer(0))
+
+
+        self._mgr.Update()
+
+        #
+        # Init print module and classes
+        #
+        self.printopt = disp_print.PrintOptions(self, self.MapWindow)
+        
+        #
+        # Initialization of digitization tool
+        #
+        self.digit = None
+
+        #
+        # 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
+
+        pos = wx.Point(700,10)
+
+
+
+        parent1 = self.GetParent()
+        rightpanel = parent1.GetParent()
+        splitter= rightpanel.GetParent()
+        self.lmgr= splitter.GetParent()
+
+
+        self.maptree = wx_utils.AddLayerTree(self, 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,
+                                                      frame=parent,Map=self.Map,lmgr=self.lmgr)
+        #self.maptree.SetBackgroundColour("red")
+
+        self._mgr.AddPane(self.maptree, wx.aui.AuiPaneInfo().Right().
+                                        Dockable(False).BestSize((-1,-1)).
+                                        CloseButton(False).DestroyOnClose(True).
+                                        Layer(0).Caption("Layers"))
+
+        self._mgr.Update()
+
+        #r.rightSizer.Add(self.maptree)
+
+    def AddToolbar(self, name):
+        """
+        Add defined toolbar to the window
+
+        Currently known toolbars are:
+        - map basic map toolbar
+        - digit vector digitizer
+        - georect georectifier
+        """
+        # default toolbar
+        if name == "map":
+            self.toolbars['map'] = toolbars.MapToolbar(self, self.Map)
+
+            self._mgr.AddPane(self.toolbars['map'].toolbar,
+                              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'].GetToolbar().GetSize())))
+	
+        # vector digitizer
+        elif name == "vdigit":
+            from vdigit import haveVDigit
+            if not haveVDigit:
+                from vdigit import errorMsg
+                msg = _("Unable to start vector digitizer.\nThe VDigit python extension "
+                        "was not found or loaded properly.\n"
+                        "Switching back to 2D display mode.\n\nDetails: %s" % errorMsg)
+                
+                self.toolbars['map'].combo.SetValue (_("2D view"))
+                wx.MessageBox(parent=self,
+                              message=msg,
+                              caption=_("Error"),
+                              style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                return
+            
+            if self._layerManager:
+                log = self._layerManager.goutput
+            else:
+                log = None
+            self.toolbars['vdigit'] = toolbars.VDigitToolbar(parent=self, map=self.Map,
+                                                             layerTree=self.tree,
+                                                             log=log)
+
+            for toolRow in range(0, self.toolbars['vdigit'].numOfRows):
+                self._mgr.AddPane(self.toolbars['vdigit'].toolbar[toolRow],
+                                  wx.aui.AuiPaneInfo().
+                                  Name("vdigittoolbar" + str(toolRow)).Caption(_("Vector digitizer toolbar")).
+                                  ToolbarPane().Top().Row(toolRow + 1).
+                                  LeftDockable(False).RightDockable(False).
+                                  BottomDockable(False).TopDockable(True).
+                                  CloseButton(False).Layer(2).
+                                  BestSize((self.toolbars['vdigit'].GetToolbar().GetSize())))
+	
+            # change mouse to draw digitized line
+            self.MapWindow.mouse['box'] = "point"
+            self.MapWindow.zoomtype = 0
+            self.MapWindow.pen     = wx.Pen(colour='red',   width=2, style=wx.SOLID)
+            self.MapWindow.polypen = wx.Pen(colour='green', width=2, style=wx.SOLID)
+        # georectifier
+        elif name == "georect":
+            self.toolbars['georect'] = toolbars.GRToolbar(self, self.Map)
+
+            self._mgr.AddPane(self.toolbars['georect'].toolbar,
+                              wx.aui.AuiPaneInfo().
+                              Name("georecttoolbar").Caption(_("Georectification toolbar")).
+                              ToolbarPane().Top().
+                              LeftDockable(False).RightDockable(False).
+                              BottomDockable(False).TopDockable(True).
+                              CloseButton(False).Layer(2))
+        # nviz
+        elif name == "nviz":
+            import nviz
+
+            # check for GLCanvas and OpenGL
+            msg = None
+            if not nviz.haveGLCanvas:
+                msg = _("Unable to switch to 3D display mode.\nThe GLCanvas class has not been "
+                        "included with this build "
+                        "of wxPython!\nSwitching back to "
+                        "2D display mode.\n\nDetails: %s" % nviz.errorMsg)
+            if not nviz.haveNviz:
+                msg = _("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)
+
+            if msg:
+                self.toolbars['map'].combo.SetValue (_("2D view"))
+                wx.MessageBox(parent=self,
+                              message=msg,
+                              caption=_("Error"),
+                              style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
+                return
+
+            #
+            # add Nviz toolbar and disable 2D display mode tools
+            #
+            self.toolbars['nviz'] = toolbars.NvizToolbar(self, self.Map)
+            self.toolbars['map'].Enable2D(False)
+
+            #
+            # update layer tree (-> enable 3d-rasters)
+            #
+            if self.tree:
+                self.tree.EnableItemType(type='3d-raster', enable=True)
+            
+            #
+            # update status bar
+            #
+            self.statusbarWin['toggle'].Enable(False)
+
+            #
+            # erase map window
+            #
+            self.MapWindow.EraseMap()
+
+            self.statusbar.SetStatusText(_("Please wait, loading data..."), 0)
+            
+            #
+            # create GL window & NVIZ toolbar
+            #
+            if not self.MapWindow3D:
+                self.MapWindow3D = nviz.GLWindow(self, id=wx.ID_ANY,
+                                                 Map=self.Map, tree=self.tree, lmgr=self._layerManager)
+                # -> show after paint
+                self.nvizToolWin = nviz.NvizToolWindow(self, id=wx.ID_ANY,
+                                                       mapWindow=self.MapWindow3D)
+                self.MapWindow3D.OnPaint(None) # -> LoadData
+                self.MapWindow3D.Show()
+                self.MapWindow3D.UpdateView(None)
+            
+            self.nvizToolWin.Show()
+
+            #
+            # switch from MapWindow to MapWindowGL
+            # add nviz toolbar
+            #
+            self._mgr.DetachPane(self.MapWindow2D)
+            self.MapWindow2D.Hide()
+            self._mgr.AddPane(self.MapWindow3D, wx.aui.AuiPaneInfo().CentrePane().
+                              Dockable(False).BestSize((-1,-1)).
+                              CloseButton(False).DestroyOnClose(True).
+                              Layer(0))
+            self._mgr.AddPane(self.toolbars['nviz'].toolbar,
+                              wx.aui.AuiPaneInfo().
+                              Name("nviztoolbar").Caption(_("Nviz toolbar")).
+                              ToolbarPane().Top().Row(1).
+                              LeftDockable(False).RightDockable(False).
+                              BottomDockable(False).TopDockable(True).
+                              CloseButton(False).Layer(2))
+            
+            self.MapWindow = self.MapWindow3D
+            self.frame.SetStatusText("", 0)
+            
+        self._mgr.Update()
+
+    def RemoveToolbar (self, name):
+        """
+        Removes toolbar from the window
+
+        TODO: Only hide, activate by calling AddToolbar()
+        """
+
+        # cannot hide main toolbar
+        if name == "map":
+            return
+        elif name == "vdigit":
+            # TODO: not destroy only hide
+            for toolRow in range(0, self.toolbars['vdigit'].numOfRows):
+                self._mgr.DetachPane (self.toolbars['vdigit'].toolbar[toolRow])
+                self.toolbars['vdigit'].toolbar[toolRow].Destroy()
+        else:
+            self._mgr.DetachPane (self.toolbars[name].toolbar)
+            self.toolbars[name].toolbar.Destroy()
+
+        self.toolbars[name] = None
+
+        if name == 'nviz':
+            # hide nviz tools
+            self.nvizToolWin.Hide()
+            # unload data
+            #            self.MapWindow3D.Reset()
+            # switch from MapWindowGL to MapWindow
+            self._mgr.DetachPane(self.MapWindow3D)
+            self.MapWindow3D.Hide()
+            self.MapWindow2D.Show()
+            self._mgr.AddPane(self.MapWindow2D, wx.aui.AuiPaneInfo().CentrePane().
+                              Dockable(False).BestSize((-1,-1)).
+                              CloseButton(False).DestroyOnClose(True).
+                              Layer(0))
+            self.MapWindow = self.MapWindow2D
+            
+            #
+            # update layer tree (-> disable 3d-rasters)
+            #
+            if self.tree:
+                self.tree.EnableItemType(type='3d-raster', enable=False)
+            self.MapWindow.UpdateMap()
+            
+        self.toolbars['map'].combo.SetValue (_("2D view"))
+        self.toolbars['map'].Enable2D(True)
+        self.statusbarWin['toggle'].Enable(True)
+
+        self._mgr.Update()
+
+    def __InitDisplay(self):
+        """
+        Initialize map display, set dimensions and map region
+        """
+        self.width, self.height = self.GetClientSize()
+
+        Debug.msg(2, "MapFrame.__InitDisplay():")
+        self.Map.ChangeMapSize(self.GetClientSize())
+        self.Map.region = self.Map.GetRegion() # g.region -upgc
+        # self.Map.SetRegion() # adjust region to match display window
+
+    def OnUpdateProgress(self, event):
+        """
+        Update progress bar info
+        """
+        self.statusbarWin['progress'].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.georectifying:
+            # in georectifying session; display used to get get geographic
+            # coordinates for GCPs
+            self.OnPointer(event)
+        else:
+            # change bookcontrol page to page associated with display
+            if self.page:
+                pgnum = self.layerbook.GetPageIndex(self.page)
+                if pgnum > -1:
+                    self.layerbook.SetSelection(pgnum)
+        
+        event.Skip()
+
+    def OnMotion(self, event):
+        """
+        Mouse moved
+        Track mouse motion and update status bar
+        """
+        # update statusbar if required
+        if self.statusbarWin['toggle'].GetSelection() == 0: # Coordinates
+            precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                                             subkey = 'precision'))
+            format = UserSettings.Get(group = 'projection', key = 'format',
+                                      subkey = 'll')
+            e, n = self.MapWindow.Pixel2Cell(event.GetPositionTuple())
+            if self.toolbars['vdigit'] and \
+                    self.toolbars['vdigit'].GetAction() == 'addLine' and \
+                    self.toolbars['vdigit'].GetAction('type') in ('line', 'boundary') and \
+                    len(self.MapWindow.polycoords) > 0:
+                # for linear feature show segment and total length
+                distance_seg = self.MapWindow.Distance(self.MapWindow.polycoords[-1],
+                                                       (e, n), screen=False)[0]
+                distance_tot = distance_seg
+                for idx in range(1, len(self.MapWindow.polycoords)):
+                    distance_tot += self.MapWindow.Distance(self.MapWindow.polycoords[idx-1],
+                                                            self.MapWindow.polycoords[idx],
+                                                            screen=False )[0]
+                self.statusbar.SetStatusText("%.*f, %.*f (seg: %.*f; tot: %.*f)" % \
+                                                 (precision, e, precision, n,
+                                                  precision, distance_seg,
+                                                  precision, distance_tot), 0)
+            else:
+                if self.statusbarWin['projection'].IsChecked():
+                    if not UserSettings.Get(group='projection', key='statusbar', subkey='proj4'):
+                        self.statusbar.SetStatusText(_("Projection not defined (check the settings)"), 0)
+                    else:
+                        proj, coord  = utils.ReprojectCoordinates(coord = (e, n),
+                                                                  projOut = UserSettings.Get(group='projection',
+                                                                                             key='statusbar',
+                                                                                             subkey='proj4'),
+                                                                  flags = 'd')
+                    
+                        if coord:
+                            e, n = coord
+                            if proj in ('ll', 'latlong', 'longlat') and format == 'DMS':
+                                self.statusbar.SetStatusText("%s" % \
+                                                                 utils.Deg2DMS(e, n, precision = precision),
+                                                             0)
+                            else:
+                                self.statusbar.SetStatusText("%.*f; %.*f" % \
+                                                                 (precision, e, precision, n), 0)
+                        else:
+                            self.statusbar.SetStatusText(_("Error in projection (check the settings)"), 0)
+                else:
+                    if self.Map.projinfo['proj'] == 'll' and format == 'DMS':
+                        self.statusbar.SetStatusText("%s" % \
+                                                         utils.Deg2DMS(e, n, precision = precision),
+                                                     0)
+                    else:
+                        self.statusbar.SetStatusText("%.*f; %.*f" % \
+                                                         (precision, e, precision, n), 0)
+                
+        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)
+
+        # delete tmp lines
+        if self.MapWindow.mouse["use"] in ("measure",
+                                           "profile"):
+            self.MapWindow.polycoords = []
+            self.MapWindow.ClearLines()
+        
+        # deselect features in vdigit
+        if self.toolbars['vdigit'] and self.digit:
+            self.digit.driver.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.toolbars['map']:
+            if event:
+                self.toolbars['map'].OnTool(event)
+            self.toolbars['map'].action['desc'] = ''
+        
+        self.MapWindow.mouse['use'] = "pointer"
+        self.MapWindow.mouse['box'] = "point"
+
+        # change the cursor
+        if self.toolbars['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', 'displayAttrs',
+                                                         'copyCats']:
+                self.MapWindow.mouse['box'] = 'point'
+            else: # moveLine, deleteLine
+                self.MapWindow.mouse['box'] = 'box'
+        
+        elif self._layerManager and self._layerManager.georectifying:
+            self.MapWindow.SetCursor(self.cursors["cross"])
+        
+        else:
+            self.MapWindow.SetCursor(self.cursors["default"])
+
+    def OnZoomIn(self, event):
+        """
+        Zoom in the map.
+        Set mouse cursor, zoombox attributes, and zoom direction
+        """
+        if self.toolbars['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"])
+
+    def OnZoomOut(self, event):
+        """
+        Zoom out the map.
+        Set mouse cursor, zoombox attributes, and zoom direction
+        """
+        if self.toolbars['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"])
+
+    def OnZoomBack(self, event):
+        """
+        Zoom last (previously stored position)
+        """
+        self.MapWindow.ZoomBack()
+
+    def OnPan(self, event):
+        """
+        Panning, set mouse to drag
+        """
+        if self.toolbars['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"])
+
+    def OnErase(self, event):
+        """
+        Erase the canvas
+        """
+        self.MapWindow.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 OnToggleRender(self, event):
+        """
+        Enable/disable auto-rendering
+        """
+        if self.statusbarWin['render'].GetValue():
+            self.OnRender(None)
+
+    def OnToggleShowRegion(self, event):
+        """
+        Show/Hide extent in map canvas
+        """
+        if self.statusbarWin['region'].GetValue():
+            # show extent
+            self.MapWindow.regionCoords = []
+        else:
+            del self.MapWindow.regionCoords
+
+        # redraw map if auto-rendering is enabled
+        if self.statusbarWin['render'].GetValue():
+            self.OnRender(None)
+
+    def OnToggleResolution(self, event):
+        """
+        Use resolution of computation region settings
+        for redering image instead of display resolution
+        """
+        # redraw map if auto-rendering is enabled
+        if self.statusbarWin['render'].GetValue():
+            self.OnRender(None)
+        
+    def OnToggleStatus(self, event):
+        """
+        Toggle status text
+        """
+        self.StatusbarUpdate()
+
+    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.statusbarWin['mapscale'].SetValue('1:%ld' % int(self.mapScaleValue))
+            return
+
+        dEW = value * (self.Map.region['cols'] / self.ppm[0])
+        dNS = value * (self.Map.region['rows'] / self.ppm[1])
+        self.Map.region['n'] = self.Map.region['center_northing'] + dNS / 2.
+        self.Map.region['s'] = self.Map.region['center_northing'] - dNS / 2.
+        self.Map.region['w'] = self.Map.region['center_easting']  - dEW / 2.
+        self.Map.region['e'] = self.Map.region['center_easting']  + dEW / 2.
+        
+        # add to zoom history
+        self.MapWindow.ZoomHistory(self.Map.region['n'], self.Map.region['s'],
+                                   self.Map.region['e'], self.Map.region['w'])
+        
+        # redraw a map
+        self.MapWindow.UpdateMap()
+        self.statusbarWin['mapscale'].SetFocus()
+        
+    def OnGoTo(self, event):
+        """
+        Go to position
+        """
+        try:
+            if self.statusbarWin['projection'].IsChecked():
+                if not UserSettings.Get(group='projection', key='statusbar', subkey='proj4'):
+                    self.statusbar.SetStatusText(_("Projection not defined (check the settings)"), 0)
+                else:
+                    # reproject values
+                    projIn = UserSettings.Get(group='projection',
+                                              key='statusbar',
+                                              subkey='proj4')
+                    projOut = gcmd.RunCommand('g.proj',
+                                              flags = 'jf',
+                                              read = True)
+                    proj = projIn.split(' ')[0].split('=')[1]
+                    if proj in ('ll', 'latlong', 'longlat'):
+                        e, n = self.statusbarWin['goto'].GetValue().split(';')
+                        e, n = utils.DMS2Deg(e, n)
+                        proj, coord1 = utils.ReprojectCoordinates(coord = (e, n),
+                                                                  projIn = projIn,
+                                                                  projOut = projOut, flags = 'd')
+                        e, n = coord1
+                    else:
+                        e, n = map(float, self.statusbarWin['goto'].GetValue().split(';'))
+                        proj, coord1 = utils.ReprojectCoordinates(coord = (e, n),
+                                                                  projIn = projIn,
+                                                                  projOut = projOut, flags = 'd')
+                        e, n = coord1
+            else:
+                if self.Map.projinfo['proj'] == 'll':
+                    e, n = self.statusbarWin['goto'].GetValue().split(';')
+                else:
+                    e, n = map(float, self.statusbarWin['goto'].GetValue().split(';'))
+                    
+            region = self.Map.GetCurrentRegion()
+            if self.statusbarWin['projection'].IsChecked():
+                if not UserSettings.Get(group='projection', key='statusbar', subkey='proj4'):
+                    self.statusbar.SetStatusText(_("Projection not defined (check the settings)"), 0)
+                else:
+                    region['center_easting'], region['center_northing'] = e, n
+            else:
+                if self.Map.projinfo['proj'] == 'll':
+                    region['center_easting'], region['center_northing'] = utils.DMS2Deg(e, n)
+                else:
+                    region['center_easting'], region['center_northing'] = e, n
+        except ValueError:
+            region = self.Map.GetCurrentRegion()
+            precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                                             subkey = 'precision'))
+            format = UserSettings.Get(group = 'projection', key = 'format',
+                                      subkey = 'll')
+            if self.Map.projinfo['proj'] == 'll' and format == 'DMS':
+                    self.statusbarWin['goto'].SetValue("%s" % utils.Deg2DMS(region['center_easting'], 
+                                                                            region['center_northing'],
+                                                                            precision = precision))
+            else:
+                self.statusbarWin['goto'].SetValue("%.*f; %.*f" % \
+                                                       (precision, region['center_easting'],
+                                                        precision, region['center_northing']))
+            return
+        
+        
+        dn = (region['nsres'] * region['rows']) / 2.
+        region['n'] = region['center_northing'] + dn
+        region['s'] = region['center_northing'] - dn
+        de = (region['ewres'] * region['cols']) / 2.
+        region['e'] = region['center_easting'] + de
+        region['w'] = region['center_easting'] - de
+        
+        self.Map.AdjustRegion()
+
+        # add to zoom history
+        self.MapWindow.ZoomHistory(region['n'], region['s'],
+                                   region['e'], region['w'])
+        
+        # redraw a map
+        self.MapWindow.UpdateMap()
+        self.statusbarWin['goto'].SetFocus()
+        
+    def StatusbarUpdate(self):
+        """!Update statusbar content"""
+
+        self.statusbarWin['region'].Hide()
+        self.statusbarWin['resolution'].Hide()
+        self.statusbarWin['mapscale'].Hide()
+        self.statusbarWin['goto'].Hide()
+        self.statusbarWin['projection'].Hide()
+        self.mapScaleValue = self.ppm = None
+
+        if self.statusbarWin['toggle'].GetSelection() == 0: # Coordinates
+            self.statusbar.SetStatusText("", 0)
+            # enable long help
+            self.StatusbarEnableLongHelp()
+
+        elif self.statusbarWin['toggle'].GetSelection() in (1, 2): # Extent
+            sel = self.statusbarWin['toggle'].GetSelection()
+            if sel == 1:
+                region = self.Map.region
+            else:
+                region = self.Map.GetRegion() # computation region
+
+            precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                                             subkey = 'precision'))
+            format = UserSettings.Get(group = 'projection', key = 'format',
+                                      subkey = 'll')
+            
+            if self.statusbarWin['projection'].IsChecked():
+                if not UserSettings.Get(group='projection', key='statusbar', subkey='proj4'):
+                    self.statusbar.SetStatusText(_("Projection not defined (check the settings)"), 0)
+                else:
+                    projOut = UserSettings.Get(group='projection',
+                                               key='statusbar',
+                                               subkey='proj4')
+                    proj, coord1 = utils.ReprojectCoordinates(coord = (region["w"], region["s"]),
+                                                              projOut = projOut, flags = 'd')
+                    proj, coord2 = utils.ReprojectCoordinates(coord = (region["e"], region["n"]),
+                                                          projOut = projOut, flags = 'd')
+                    if sel == 2:
+                        proj, coord3 = utils.ReprojectCoordinates(coord = (0.0, 0.0),
+                                                                  projOut = projOut, flags = 'd')
+                        proj, coord4 = utils.ReprojectCoordinates(coord = (region["ewres"], region["nsres"]),
+                                                                  projOut = projOut, flags = 'd')
+                    if coord1 and coord2:
+                        if proj in ('ll', 'latlong', 'longlat') and format == 'DMS':
+                            w, s = utils.Deg2DMS(coord1[0], coord1[1], string = False,
+                                                 precision = precision)
+                            e, n = utils.Deg2DMS(coord2[0], coord2[1], string = False,
+                                                 precision = precision)
+                            if sel == 1:
+                                self.statusbar.SetStatusText("%s - %s, %s - %s" %
+                                                             (w, e, s, n), 0)
+                            else:
+                                ewres, nsres = utils.Deg2DMS(abs(coord3[0]) - abs(coord4[0]),
+                                                             abs(coord3[1]) - abs(coord4[1]),
+                                                             string = False, hemisphere = False,
+                                                             precision = precision)
+                                self.statusbar.SetStatusText("%s - %s, %s - %s (%s, %s)" %
+                                                             (w, e, s, n, ewres, nsres), 0)
+                        else:
+                            w, s = coord1
+                            e, n = coord2
+                            if sel == 1:
+                                self.statusbar.SetStatusText("%.*f - %.*f, %.*f - %.*f" %
+                                                         (precision, w, precision, e,
+                                                          precision, s, precision, n), 0)
+                            else:
+                                ewres, nsres = coord3
+                                self.statusbar.SetStatusText("%.*f - %.*f, %.*f - %.*f (%.*f, %.*f)" %
+                                                             (precision, w, precision, e,
+                                                              precision, s, precision, n,
+                                                              precision, ewres, precision, nsres), 0)
+                    else:
+                        self.statusbar.SetStatusText(_("Error in projection (check the settings)"), 0)
+            else:
+                if self.Map.projinfo['proj'] == 'll' and format == 'DMS':
+                    w, s = utils.Deg2DMS(region["w"], region["s"],
+                                         string = False, precision = precision)
+                    e, n = utils.Deg2DMS(region["e"], region["n"],
+                                         string = False, precision = precision)
+                    if sel == 1:
+                        self.statusbar.SetStatusText("%s - %s, %s - %s" %
+                                                     (w, e, s, n), 0)
+                    else:
+                        ewres, nsres = utils.Deg2DMS(region['ewres'], region['nsres'],
+                                                     string = False, precision = precision)
+                        self.statusbar.SetStatusText("%s - %s, %s - %s (%s, %s)" %
+                                                     (w, e, s, n, ewres, nsres), 0)
+                else:
+                    w, s = region["w"], region["s"]
+                    e, n = region["e"], region["n"]
+                    if sel == 1:
+                        self.statusbar.SetStatusText("%.*f - %.*f, %.*f - %.*f" %
+                                                     (precision, w, precision, e,
+                                                      precision, s, precision, n), 0)
+                    else:
+                        ewres, nsres = region['ewres'], region['nsres']
+                        self.statusbar.SetStatusText("%.*f - %.*f, %.*f - %.*f (%.*f, %.*f)" %
+                                                     (precision, w, precision, e,
+                                                      precision, s, precision, n,
+                                                      precision, ewres, precision, nsres), 0)
+            # enable long help
+            self.StatusbarEnableLongHelp()
+
+        elif self.statusbarWin['toggle'].GetSelection() == 3: # Show comp. extent
+            self.statusbar.SetStatusText("", 0)
+            self.statusbarWin['region'].Show()
+            # disable long help
+            self.StatusbarEnableLongHelp(False)
+
+        elif self.statusbarWin['toggle'].GetSelection() == 4: # Display mode
+            self.statusbar.SetStatusText("", 0)
+            self.statusbarWin['resolution'].Show()
+            # disable long help
+            self.StatusbarEnableLongHelp(False)
+
+        elif self.statusbarWin['toggle'].GetSelection() == 5: # Display geometry
+            self.statusbar.SetStatusText("rows=%d; cols=%d; nsres=%.2f; ewres=%.2f" %
+                                         (self.Map.region["rows"], self.Map.region["cols"],
+                                          self.Map.region["nsres"], self.Map.region["ewres"]), 0)
+            # enable long help
+            self.StatusbarEnableLongHelp()
+
+        elif self.statusbarWin['toggle'].GetSelection() == 6: # Map scale
+            # 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
+            self.ppm = ((ppi[0] / 2.54) * 100, # pixel per meter
+                        (ppi[1] / 2.54) * 100)
+
+            Debug.msg(4, "MapFrame.StatusbarUpdate(mapscale): 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],
+                           self.ppm[0], self.ppm[1]))
+
+            region = self.Map.region
+
+            heightCm = region['rows'] / self.ppm[1] * 100
+            widthCm  = region['cols'] / self.ppm[0] * 100
+
+            Debug.msg(4, "MapFrame.StatusbarUpdate(mapscale): width_cm=%f, height_cm=%f" %
+                      (widthCm, heightCm))
+
+            xscale = (region['e'] - region['w']) / (region['cols'] / self.ppm[0])
+            yscale = (region['n'] - region['s']) / (region['rows'] / self.ppm[1])
+            scale = (xscale + yscale) / 2.
+            
+            Debug.msg(3, "MapFrame.StatusbarUpdate(mapscale): xscale=%f, yscale=%f -> scale=%f" % \
+                          (xscale, yscale, scale))
+
+            self.statusbar.SetStatusText("")
+            try:
+                self.statusbarWin['mapscale'].SetValue("1:%ld" % (scale + 0.5))
+            except TypeError:
+                pass
+            self.mapScaleValue = scale
+            self.statusbarWin['mapscale'].Show()
+
+            # disable long help
+            self.StatusbarEnableLongHelp(False)
+
+        elif self.statusbarWin['toggle'].GetSelection() == 7: # go to
+            self.statusbar.SetStatusText("")
+            region = self.Map.GetCurrentRegion()
+            precision = int(UserSettings.Get(group = 'projection', key = 'format',
+                                             subkey = 'precision'))
+            format = UserSettings.Get(group = 'projection', key = 'format',
+                                      subkey = 'll')
+            
+            if self.statusbarWin['projection'].IsChecked():
+                if not UserSettings.Get(group='projection', key='statusbar', subkey='proj4'):
+                    self.statusbar.SetStatusText(_("Projection not defined (check the settings)"), 0)
+                else:
+                    proj, coord  = utils.ReprojectCoordinates(coord = (region['center_easting'],
+                                                                       region['center_northing']),
+                                                              projOut = UserSettings.Get(group='projection',
+                                                                                         key='statusbar',
+                                                                                         subkey='proj4'),
+                                                              flags = 'd')
+                    if coord:
+                        if proj in ('ll', 'latlong', 'longlat') and format == 'DMS':
+                            self.statusbarWin['goto'].SetValue("%s" % utils.Deg2DMS(coord[0],
+                                                                                    coord[1],
+                                                                                    precision = precision))
+                        else:
+                            self.statusbarWin['goto'].SetValue("%.*f; %.*f" % (precision, coord[0],
+                                                                               precision, coord[1]))
+                    else:
+                        self.statusbar.SetStatusText(_("Error in projection (check the settings)"), 0)
+            else:
+                if self.Map.projinfo['proj'] == 'll' and format == 'DMS':
+                    self.statusbarWin['goto'].SetValue("%s" % utils.Deg2DMS(region['center_easting'], 
+                                                                            region['center_northing'],
+                                                                            precision = precision))
+                else:
+                    self.statusbarWin['goto'].SetValue("%.*f; %.*f" % (precision, region['center_easting'],
+                                                                       precision, region['center_northing']))
+            self.statusbarWin['goto'].Show()
+
+            # disable long help
+            self.StatusbarEnableLongHelp(False)
+        
+        elif self.statusbarWin['toggle'].GetSelection() == 8: # projection
+            self.statusbar.SetStatusText("")
+            epsg = UserSettings.Get(group='projection', key='statusbar', subkey='epsg')
+            if epsg:
+                label = '%s (EPSG: %s)' % (_("Use defined projection"), epsg)
+                self.statusbarWin['projection'].SetLabel(label)
+            else:
+                self.statusbarWin['projection'].SetLabel(_("Use defined projection"))
+            self.statusbarWin['projection'].Show()
+            
+            # disable long help
+            self.StatusbarEnableLongHelp(False)
+            
+        else:
+            self.statusbar.SetStatusText("", 1)
+
+    def StatusbarEnableLongHelp(self, enable=True):
+        """!Enable/disable toolbars long help"""
+        for toolbar in self.toolbars.itervalues():
+            if toolbar:
+                toolbar.EnableLongHelp(enable)
+                
+    def StatusbarReposition(self):
+        """!Reposition checkbox in statusbar"""
+        # reposition checkbox
+        widgets = [(0, self.statusbarWin['region']),
+                   (0, self.statusbarWin['resolution']),
+                   (0, self.statusbarWin['mapscale']),
+                   (0, self.statusbarWin['progress']),
+                   (0, self.statusbarWin['projection']),
+                   (1, self.statusbarWin['toggle']),
+                   (2, self.statusbarWin['mask']),
+                   (3, self.statusbarWin['render'])]
+        for idx, win in widgets:
+            rect = self.statusbar.GetFieldRect(idx)
+            if idx == 0: # show region / mapscale / process bar
+                # -> size
+                wWin, hWin = win.GetBestSize()
+                if win == self.statusbarWin['progress']:
+                    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 SaveToFile(self, event):
+        """
+        Save image to file
+        """
+        lext = []
+        for h in self.MapWindow.img.GetHandlers():
+            lext.append(h.GetExtension())
+        
+        filetype =  "BMP file (*.bmp)|*.bmp|"
+        if 'gif' in lext:
+            filetype += "GIF file (*.gif)|*.gif|"
+        if 'jpg' in lext:
+            filetype += "JPG file (*.jpg)|*.jpg|"
+        if 'pcx' in lext:
+            filetype += "PCX file (*.pcx)|*.pcx|"
+        if 'png' in lext:
+            filetype += "PNG file (*.png)|*.png|"
+        if 'pnm' in lext:
+            filetype += "PNM file (*.pnm)|*.pnm|"
+        if 'tif' in lext:
+            filetype += "TIF file (*.tif)|*.tif|"
+        if 'xpm' in lext:
+            filetype += "XPM file (*.xpm)|*.xpm"
+
+        dlg = wx.FileDialog(self, _("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 path == None: return
+            base = os.path.splitext(dlg.GetPath())[0]
+            ext = os.path.splitext(dlg.GetPath())[1]
+            if dlg.GetFilterIndex() == 0:
+                type = wx.BITMAP_TYPE_BMP
+                if ext != '.bmp': path = base+'.bmp'
+            if dlg.GetFilterIndex() == 1:
+                type = wx.BITMAP_TYPE_GIF
+                if ext != '.gif': path = base+'.gif'
+            elif dlg.GetFilterIndex() == 2:
+                type = wx.BITMAP_TYPE_JPEG
+                if ext != '.jpg': path = base+'.jpg'
+            elif dlg.GetFilterIndex() == 3:
+                type = wx.BITMAP_TYPE_GIF
+                if ext != '.pcx': path = base+'.pcx'
+            elif dlg.GetFilterIndex() == 4:
+                type = wx.BITMAP_TYPE_PNG
+                if ext != '.png': path = base+'.png'
+            elif dlg.GetFilterIndex() == 5:
+                type = wx.BITMAP_TYPE_PNM
+                if ext != '.pnm': path = base+'.pnm'
+            elif dlg.GetFilterIndex() == 6:
+                type = wx.BITMAP_TYPE_TIF
+                if ext != '.tif': path = base+'.tif'
+            elif dlg.GetFilterIndex() == 7:
+                type = wx.BITMAP_TYPE_XPM
+                if ext != '.xpm': path = base+'.xpm'
+            self.MapWindow.SaveToFile(path, type)
+            
+        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.toolbars['vdigit']:
+            maplayer = self.toolbars['vdigit'].GetLayer()
+            if maplayer:
+                self.toolbars['vdigit'].OnExit()
+                self.imgVectorMap = None
+
+        if self.toolbars['nviz']:
+            self.toolbars['nviz'].OnExit()
+
+        if not self._layerManager:
+            self.Destroy()
+        elif self.page:
+            pgnum = self.layerbook.GetPageIndex(self.page)
+            if pgnum > -1:
+                self.layerbook.DeletePage(pgnum)
+        
+    def GetRender(self):
+        """
+        Returns the current instance of render.Map()
+        """
+        return self.Map
+
+    def OnQueryDisplay(self, event):
+        """
+        Query currrent raster/vector map layers (display mode)
+        """
+        if self.toolbars['map'].GetAction() == 'displayAttrb': # select previous action
+            self.toolbars['map'].SelectDefault(event)
+            return
+
+        self.toolbars['map'].action['desc'] = 'displayAttrb'
+        
+        # switch GIS Manager to output console to show query results
+        self._layerManager.notebook.SetSelection(1)
+
+        self.MapWindow.mouse['use'] = "query"
+        self.MapWindow.mouse['box'] = "point"
+        self.MapWindow.zoomtype = 0
+
+        # change the cursor
+        self.MapWindow.SetCursor(self.cursors["cross"])
+
+    def OnQueryModify(self, event):
+        """
+        Query vector map layer (edit mode)
+        """
+        if self.toolbars['map'].GetAction() == 'modifyAttrb': # select previous action
+            self.toolbars['map'].SelectDefault(event)
+            return
+        
+        self.toolbars['map'].action['desc'] = 'modifyAttrb'
+        
+        self.MapWindow.mouse['use'] = "queryVector"
+        self.MapWindow.mouse['box'] = "point"
+        self.MapWindow.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
+        self.MapWindow.zoomtype = 0
+
+        # change the cursor
+        self.MapWindow.SetCursor(self.cursors["cross"])
+        
+    def QueryMap(self, x, y):
+        """!Query map layer features
+
+        Currently only raster and vector map layers are supported.
+        
+        @param x,y coordinates
+        """
+        #set query snap distance for v.what at mapunit equivalent of 10 pixels
+        qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) / self.Map.width)
+        east, north = self.MapWindow.Pixel2Cell((x, y))
+
+        num = 0
+        for layer in self.tree.GetSelections():
+            type = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
+            if type in ('raster', 'rgb', 'his',
+                        'vector', 'thememap', 'themechart'):
+                num += 1
+        
+        if num < 1:
+            dlg = wx.MessageDialog(parent=self,
+                                   message=_('No raster or vector map layer selected for querying.'),
+                                   caption=_('No map layer selected'),
+                                   style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+            dlg.ShowModal()
+            dlg.Destroy()
+            return
+        
+        mapname = None
+        raststr = ''
+        vectstr = ''
+        rcmd = ['r.what', '--q']
+        vcmd = ['v.what', '--q']
+        for layer in self.tree.GetSelections():
+            type = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
+            dcmd = self.tree.GetPyData(layer)[0]['cmd']
+            name = utils.GetLayerNameFromCmd(dcmd)
+            if name == '':
+                continue
+            if type in ('raster', 'rgb', 'his'):
+                raststr += "%s," % name
+            elif type in ('vector', 'thememap', 'themechart'):
+                vectstr += "%s," % name
+
+        # use display region settings instead of computation region settings
+        self.tmpreg = os.getenv("GRASS_REGION")
+        os.environ["GRASS_REGION"] = self.Map.SetRegion(windres=False)
+        
+        # build query commands for any selected rasters and vectors
+        if raststr != '':
+            rcmd.append('-f')
+            rcmd.append('input=%s' % raststr.rstrip(','))
+            rcmd.append('east_north=%f,%f' % (float(east), float(north)))
+        
+        if vectstr != '':
+            # check for vector maps open to be edited
+            digitToolbar = self.toolbars['vdigit']
+            if digitToolbar:
+                map = digitToolbar.GetLayer().GetName()
+                vect = []
+                for vector in vectstr.split(','):
+                    if map == vector:
+                        self._layerManager.goutput.WriteWarning("Vector map <%s> "
+                                                                "opened for editing - skipped." % map)
+                        continue
+                    vect.append(vector)
+                vectstr = ','.join(vect)
+                
+            if len(vectstr) <= 1:
+                self._layerManager.goutput.WriteCmdLog("Nothing to query.")
+                return
+            
+            vcmd.append('-a')
+            vcmd.append('map=%s' % vectstr.rstrip(','))
+            vcmd.append('east_north=%f,%f' % (float(east), float(north)))
+            vcmd.append('distance=%f' % float(qdist))
+        
+        # parse query command(s)
+        if self._layerManager:
+            if raststr:
+                self._layerManager.goutput.RunCmd(rcmd,
+                                                  compReg=False,
+                                                  onDone = self._QueryMapDone)
+            if vectstr:
+                self._layerManager.goutput.RunCmd(vcmd,
+                                                  onDone = self._QueryMapDone)
+        else:
+            if raststr:
+                gcmd.RunCommand(rcmd)
+            if vectstr:
+                gcmd.RunCommand(vcmd)
+        
+    def _QueryMapDone(self, returncode):
+        """!Restore settings after querying (restore GRASS_REGION)
+
+        @param returncode command return code
+        """
+        if hasattr(self, "tmpreg"):
+            if self.tmpreg:
+                os.environ["GRASS_REGION"] = self.tmpreg
+            elif os.environ.has_key('GRASS_REGION'):
+                del os.environ["GRASS_REGION"]
+        elif os.environ.has_key('GRASS_REGION'):
+            del os.environ["GRASS_REGION"]
+        
+        if hasattr(self, "tmpreg"):
+            del self.tmpreg
+        
+    def QueryVector(self, x, y):
+        """
+        Query vector map layer features
+
+        Attribute data of selected vector object are displayed in GUI dialog.
+        Data can be modified (On Submit)
+        """
+        if not self.tree.layer_selected or \
+                self.tree.GetPyData(self.tree.layer_selected)[0]['type'] != 'vector':
+            wx.MessageBox(parent=self,
+                          message=_("No vector map selected for querying."),
+                          caption=_("Vector querying"),
+                          style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+            return
+        
+        if self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].GetMapset() != \
+                grass.gisenv()['MAPSET']:
+            wx.MessageBox(parent=self,
+                          message=_("Only vector map from the current mapset can be modified."),
+                          caption=_("Vector querying"),
+                          style=wx.OK | wx.ICON_INFORMATION | wx.CENTRE)
+            return
+        
+        posWindow = self.ClientToScreen((x + self.MapWindow.dialogOffset,
+                                         y + self.MapWindow.dialogOffset))
+
+        qdist = 10.0 * ((self.Map.region['e'] - self.Map.region['w']) /
+                        self.Map.width)
+        
+        east, north = self.MapWindow.Pixel2Cell((x, y))
+        
+        mapName = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name
+        
+        if self.dialogs['attributes'] is None:
+            self.dialogs['attributes'] = \
+                dbm_dialogs.DisplayAttributesDialog(parent=self.MapWindow,
+                                                    map=mapName,
+                                                    query=((east, north), qdist),
+                                                    pos=posWindow,
+                                                    action="update")
+        else:
+            # selection changed?
+            if not self.dialogs['attributes'].mapDBInfo or \
+                    self.dialogs['attributes'].mapDBInfo.map != mapName:
+                self.dialogs['attributes'].UpdateDialog(map=mapName, query=((east, north), qdist))
+            else:
+                self.dialogs['attributes'].UpdateDialog(query=((east, north), qdist))
+                
+        cats = self.dialogs['attributes'].GetCats()
+        
+        try:
+            qlayer = self.Map.GetListOfLayers(l_name=globalvar.QUERYLAYER)[0]
+        except IndexError:
+            qlayer = None
+        
+        if self.dialogs['attributes'].mapDBInfo and cats:
+            # highlight feature & re-draw map
+            if qlayer:
+                qlayer.SetCmd(self.AddTmpVectorMapLayer(mapName, cats,
+                                                        useId=False,
+                                                        addLayer=False))
+            else:
+                qlayer = self.AddTmpVectorMapLayer(mapName, cats, useId=False)
+            
+            # set opacity based on queried layer
+            opacity = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].GetOpacity(float=True)
+            qlayer.SetOpacity(opacity)
+            
+            self.MapWindow.UpdateMap(render=False, renderVector=False)
+            if not self.dialogs['attributes'].IsShown():
+                self.dialogs['attributes'].Show()
+        else:
+            if qlayer:
+                self.Map.DeleteLayer(qlayer)
+                self.MapWindow.UpdateMap(render=False, renderVector=False)
+            if self.dialogs['attributes'].IsShown():
+                self.dialogs['attributes'].Hide()
+        
+    def OnQuery(self, event):
+        """!Query tools menu"""
+        if self.toolbars['map']:
+            self.toolbars['map'].OnTool(event)
+            action = self.toolbars['map'].GetAction()
+        
+        point = wx.GetMousePosition()
+        toolsmenu = wx.Menu()
+        # Add items to the menu
+        display = wx.MenuItem(parentMenu=toolsmenu, id=wx.ID_ANY,
+                              text=_("Query raster/vector map(s) (display mode)"),
+                              kind=wx.ITEM_CHECK)
+        toolsmenu.AppendItem(display)
+        self.Bind(wx.EVT_MENU, self.OnQueryDisplay, display)
+        numLayers = 0
+        for layer in self.tree.GetSelections():
+            type = self.tree.GetPyData(layer)[0]['maplayer'].GetType()
+            if type in ('raster', 'rgb', 'his',
+                        'vector', 'thememap', 'themechart'):
+                numLayers += 1
+        if numLayers < 1:
+            display.Enable(False)
+        
+        if action == "displayAttrb":
+            display.Check(True)
+        
+        modify = wx.MenuItem(parentMenu=toolsmenu, id=wx.ID_ANY,
+                             text=_("Query vector map (edit mode)"),
+                             kind=wx.ITEM_CHECK)
+        toolsmenu.AppendItem(modify)
+        self.Bind(wx.EVT_MENU, self.OnQueryModify, modify)
+        digitToolbar = self.toolbars['vdigit']
+        if self.tree.layer_selected:
+            layer_selected = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer']
+            if layer_selected.GetType() != 'vector' or \
+                    (digitToolbar and \
+                         digitToolbar.GetLayer() == layer_selected):
+                modify.Enable(False)
+            else:
+                if action == "modifyAttrb":
+                    modify.Check(True)
+        
+        self.PopupMenu(toolsmenu)
+        toolsmenu.Destroy()
+
+    def AddTmpVectorMapLayer(self, name, cats, useId=False, addLayer=True):
+        """
+        Add temporal vector map layer to map composition
+
+        @param name name of map layer
+        @param useId use feature id instead of category 
+        """
+        # color settings from ATM
+        color = UserSettings.Get(group='atm', key='highlight', subkey='color')
+        colorStr = str(color[0]) + ":" + \
+            str(color[1]) + ":" + \
+            str(color[2])
+
+        pattern = ["d.vect",
+                   "map=%s" % name,
+                   "color=%s" % colorStr,
+                   "fcolor=%s" % colorStr,
+                   "width=%d"  % UserSettings.Get(group='atm', key='highlight', subkey='width')]
+        
+        if useId:
+            cmd = pattern
+            cmd.append('-i')
+            cmd.append('cats=%s' % str(cats))
+        else:
+            cmd = []
+            for layer in cats.keys():
+                cmd.append(copy.copy(pattern))
+                lcats = cats[layer]
+                cmd[-1].append("layer=%d" % layer)
+                cmd[-1].append("cats=%s" % utils.ListOfCatsToRange(lcats))
+        
+        #     if self.icon:
+        #         cmd.append("icon=%s" % (self.icon))
+        #     if self.pointsize:
+        #         cmd.append("size=%s" % (self.pointsize))
+
+        if addLayer:
+            if useId:
+                return self.Map.AddLayer(type='vector', name=globalvar.QUERYLAYER, command=cmd,
+                                         l_active=True, l_hidden=True, l_opacity=1.0)
+            else:
+                return self.Map.AddLayer(type='command', name=globalvar.QUERYLAYER, command=cmd,
+                                         l_active=True, l_hidden=True, l_opacity=1.0)
+        else:
+            return cmd
+
+    def OnAnalyze(self, event):
+        """
+        Analysis tools menu
+        """
+        point = wx.GetMousePosition()
+        toolsmenu = wx.Menu()
+        # Add items to the menu
+        measure = wx.MenuItem(toolsmenu, wx.ID_ANY, Icons["measure"].GetLabel())
+        measure.SetBitmap(Icons["measure"].GetBitmap(self.iconsize))
+        toolsmenu.AppendItem(measure)
+        self.Bind(wx.EVT_MENU, self.OnMeasure, measure)
+
+        profile = wx.MenuItem(toolsmenu, wx.ID_ANY, Icons["profile"].GetLabel())
+        profile.SetBitmap(Icons["profile"].GetBitmap(self.iconsize))
+        toolsmenu.AppendItem(profile)
+        self.Bind(wx.EVT_MENU, self.Profile, profile)
+
+        histogram = wx.MenuItem(toolsmenu, wx.ID_ANY, Icons["histogram"].GetLabel())
+        histogram.SetBitmap(Icons["histogram"].GetBitmap(self.iconsize))
+        toolsmenu.AppendItem(histogram)
+        self.Bind(wx.EVT_MENU, self.Histogram, histogram)
+
+        # Popup the menu.  If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.PopupMenu(toolsmenu)
+        toolsmenu.Destroy()
+
+    def OnMeasure(self, event):
+        """
+        Init measurement routine that calculates
+        map distance along transect drawn on
+        map display
+        """
+
+        self.totaldist = 0.0 # total measured distance
+
+        # switch GIS Manager to output console to show measure results
+        self._layerManager.notebook.SetSelection(1)
+
+        # change mouse to draw line for measurement
+        self.MapWindow.mouse['use'] = "measure"
+        self.MapWindow.mouse['box'] = "line"
+        self.MapWindow.zoomtype = 0
+        self.MapWindow.pen     = wx.Pen(colour='red', width=2, style=wx.SHORT_DASH)
+        self.MapWindow.polypen = wx.Pen(colour='green', width=2, style=wx.SHORT_DASH)
+
+        # change the cursor
+        self.MapWindow.SetCursor(self.cursors["pencil"])
+
+        # initiating output
+        style = self._layerManager.goutput.cmd_output.StyleWarning
+        self._layerManager.goutput.WriteLog(_('Click and drag with left mouse button '
+                                              'to measure.%s'
+                                              'Double click with left button to clear.') % \
+                                                (os.linesep), style)
+        if self.Map.projinfo['proj'] != 'xy':
+            units = self.Map.projinfo['units']
+            style = self._layerManager.goutput.cmd_output.StyleCommand
+            self._layerManager.goutput.WriteLog(_('Measuring distance') + ' ('
+                                                + units + '):',
+                                                style)
+        else:
+            self._layerManager.goutput.WriteLog(_('Measuring distance:'),
+                                                style)
+
+    def MeasureDist(self, beginpt, endpt):
+        """
+        Calculate map distance from screen distance
+        and print to output window
+        """
+
+        if self._layerManager.notebook.GetSelection() != 1:
+            self._layerManager.notebook.SetSelection(1)
+
+        dist, (north, east) = self.MapWindow.Distance(beginpt, endpt)
+
+        dist = round(dist, 3)
+        d, dunits = self.FormatDist(dist)
+
+        self.totaldist += dist
+        td, tdunits = self.FormatDist(self.totaldist)
+
+        strdist = str(d)
+        strtotdist = str(td)
+
+        if self.Map.projinfo['proj'] == 'xy' or 'degree' not in self.Map.projinfo['unit']:
+            angle = int(math.degrees(math.atan2(north,east)) + 0.5)
+            angle = 180 - angle
+            if angle < 0:
+                angle = 360+angle
+
+            mstring = 'segment = %s %s\ttotal distance = %s %s\tbearing = %d deg' \
+                % (strdist,dunits,strtotdist,tdunits,angle)
+        else:
+            mstring = 'segment = %s %s\ttotal distance = %s %s' \
+                % (strdist,dunits,strtotdist,tdunits)
+
+        self._layerManager.goutput.WriteLog(mstring)
+
+        return dist
+
+    def Profile(self, event):
+        """
+        Init profile canvas and tools
+        """
+        raster = []
+        if self.tree.layer_selected and \
+                self.tree.GetPyData(self.tree.layer_selected)[0]['type'] == 'raster':
+            raster.append(self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name)
+
+        self.profile = profile.ProfileFrame(self,
+                                            id=wx.ID_ANY, pos=wx.DefaultPosition, size=(700,300),
+                                            style=wx.DEFAULT_FRAME_STYLE, rasterList=raster)
+        self.profile.Show()
+        # Open raster select dialog to make sure that a raster (and the desired raster)
+        # is selected to be profiled
+        self.profile.OnSelectRaster(None)
+
+    def FormatDist(self, dist):
+        """!Format length numbers and units in a nice way,
+        as a function of length. From code by Hamish Bowman
+        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 Histogram(self, event):
+        """
+        Init histogram display canvas and tools
+        """
+        self.histogram = histogram.HistFrame(self,
+                                             id=wx.ID_ANY, size=globalvar.HIST_WINDOW_SIZE,
+                                             style=wx.DEFAULT_FRAME_STYLE)
+
+        #show new display
+        self.histogram.Show()
+        self.histogram.Refresh()
+        self.histogram.Update()
+
+
+    def OnDecoration(self, event):
+        """
+        Decorations overlay menu
+        """
+        point = wx.GetMousePosition()
+        decmenu = wx.Menu()
+        # Add items to the menu
+        AddScale = wx.MenuItem(decmenu, wx.ID_ANY, Icons["addbarscale"].GetLabel())
+        AddScale.SetBitmap(Icons["addbarscale"].GetBitmap(self.iconsize))
+        decmenu.AppendItem(AddScale)
+        self.Bind(wx.EVT_MENU, self.OnAddBarscale, AddScale)
+
+        AddLegend = wx.MenuItem(decmenu, wx.ID_ANY, Icons["addlegend"].GetLabel())
+        AddLegend.SetBitmap(Icons["addlegend"].GetBitmap(self.iconsize))
+        decmenu.AppendItem(AddLegend)
+        self.Bind(wx.EVT_MENU, self.OnAddLegend, AddLegend)
+
+        AddText = wx.MenuItem(decmenu, wx.ID_ANY, Icons["addtext"].GetLabel())
+        AddText.SetBitmap(Icons["addtext"].GetBitmap(self.iconsize))
+        decmenu.AppendItem(AddText)
+        self.Bind(wx.EVT_MENU, self.OnAddText, AddText)
+
+        # Popup the menu.  If an item is selected then its handler
+        # will be called before PopupMenu returns.
+        self.PopupMenu(decmenu)
+        decmenu.Destroy()
+
+    def OnAddBarscale(self, event):
+        """
+        Handler for scale/arrow map decoration menu selection.
+        """
+        if self.dialogs['barscale']:
+            return
+
+        id = 0 # unique index for overlay layer
+
+        # If location is latlon, only display north arrow (scale won't work)
+        #        proj = self.Map.projinfo['proj']
+        #        if proj == 'll':
+        #            barcmd = 'd.barscale -n'
+        #        else:
+        #            barcmd = 'd.barscale'
+
+        # decoration overlay control dialog
+        self.dialogs['barscale'] = \
+            gdialogs.DecorationDialog(parent=self, title=_('Scale and North arrow'),
+                                      size=(350, 200),
+                                      style=wx.DEFAULT_DIALOG_STYLE | wx.CENTRE,
+                                      cmd=['d.barscale', 'at=0,5'],
+                                      ovlId=id,
+                                      name='barscale',
+                                      checktxt = _("Show/hide scale and North arrow"),
+                                      ctrltxt = _("scale object"))
+
+        self.dialogs['barscale'].CentreOnParent()
+        ### dialog cannot be show as modal - in the result d.barscale is not selectable
+        ### self.dialogs['barscale'].ShowModal()
+        self.dialogs['barscale'].Show()
+        self.MapWindow.mouse['use'] = 'pointer'        
+
+    def OnAddLegend(self, event):
+        """
+        Handler for legend map decoration menu selection.
+        """
+        if self.dialogs['legend']:
+            return
+        
+        id = 1 # index for overlay layer in render
+
+        cmd = ['d.legend', 'at=5,50,2,5']
+        if self.tree.layer_selected and \
+                self.tree.GetPyData(self.tree.layer_selected)[0]['type'] == 'raster':
+            cmd.append('map=%s' % self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].name)
+
+        # Decoration overlay control dialog
+        self.dialogs['legend'] = \
+            gdialogs.DecorationDialog(parent=self, title=('Legend'),
+                                      size=(350, 200),
+                                      style=wx.DEFAULT_DIALOG_STYLE | wx.CENTRE,
+                                      cmd=cmd,
+                                      ovlId=id,
+                                      name='legend',
+                                      checktxt = _("Show/hide legend"),
+                                      ctrltxt = _("legend object")) 
+
+        self.dialogs['legend'].CentreOnParent() 
+        ### dialog cannot be show as modal - in the result d.legend is not selectable
+        ### self.dialogs['legend'].ShowModal()
+        self.dialogs['legend'].Show()
+        self.MapWindow.mouse['use'] = 'pointer'
+
+    def OnAddText(self, event):
+        """
+        Handler for text decoration menu selection.
+        """
+        if self.MapWindow.dragid > -1:
+            id = self.MapWindow.dragid
+        else:
+            # index for overlay layer in render
+            if len(self.MapWindow.textdict.keys()) > 0:
+                id = self.MapWindow.textdict.keys()[-1] + 1
+            else:
+                id = 101
+
+        self.dialogs['text'] = gdialogs.TextLayerDialog(parent=self, ovlId=id, 
+                                                        title=_('Add text layer'),
+                                                        size=(400, 200))
+        self.dialogs['text'].CenterOnParent()
+
+        # If OK button pressed in decoration control dialog
+        if self.dialogs['text'].ShowModal() == wx.ID_OK:
+            text = self.dialogs['text'].GetValues()['text']
+            active = self.dialogs['text'].GetValues()['active']
+            coords, w, h = self.MapWindow.TextBounds(self.dialogs['text'].GetValues())
+        
+            # delete object if it has no text or is not active
+            if text == '' or active == False:
+                try:
+                    self.MapWindow.pdc.ClearId(id)
+                    self.MapWindow.pdc.RemoveId(id)
+                    del self.MapWindow.textdict[id]
+                except:
+                    pass
+                return
+
+            self.MapWindow.pdc.ClearId(id)
+            self.MapWindow.pdc.SetId(id)
+            self.MapWindow.textdict[id] = self.dialogs['text'].GetValues()
+            
+            self.MapWindow.Draw(self.MapWindow.pdcDec, img=self.MapWindow.textdict[id],
+                                drawid=id, pdctype='text', coords=coords)
+            
+            self.MapWindow.UpdateMap(render=False, renderVector=False)
+            
+        self.MapWindow.mouse['use'] = 'pointer'
+
+    def GetOptData(self, dcmd, type, params, propwin):
+        """
+        Callback method for decoration overlay command generated by
+        dialog created in menuform.py
+        """
+        # Reset comand and rendering options in render.Map. Always render decoration.
+        # Showing/hiding handled by PseudoDC
+        self.Map.ChangeOverlay(ovltype=type, type='overlay', name='', command=dcmd,
+                               l_active=True, l_render=False)
+        self.params[type] = params
+        self.propwin[type] = propwin
+
+    def OnZoomMenu(self, event):
+        """
+        Zoom menu
+        """
+        point = wx.GetMousePosition()
+        zoommenu = wx.Menu()
+        # Add items to the menu
+        zoommap = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to selected map(s)'))
+        zoommenu.AppendItem(zoommap)
+        self.Bind(wx.EVT_MENU, self.MapWindow.OnZoomToMap, zoommap)
+
+        zoomwind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to computational region (set with g.region)'))
+        zoommenu.AppendItem(zoomwind)
+        self.Bind(wx.EVT_MENU, self.MapWindow.ZoomToWind, zoomwind)
+
+        zoomdefault = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to default region'))
+        zoommenu.AppendItem(zoomdefault)
+        self.Bind(wx.EVT_MENU, self.MapWindow.ZoomToDefault, zoomdefault)
+
+        zoomsaved = wx.MenuItem(zoommenu, wx.ID_ANY, _('Zoom to saved region'))
+        zoommenu.AppendItem(zoomsaved)
+        self.Bind(wx.EVT_MENU, self.MapWindow.ZoomToSaved, zoomsaved)
+
+        savewind = wx.MenuItem(zoommenu, wx.ID_ANY, _('Set computational region from display'))
+        zoommenu.AppendItem(savewind)
+        self.Bind(wx.EVT_MENU, self.MapWindow.DisplayToWind, savewind)
+
+        savezoom = wx.MenuItem(zoommenu, wx.ID_ANY, _('Save display geometry to named region'))
+        zoommenu.AppendItem(savezoom)
+        self.Bind(wx.EVT_MENU, self.MapWindow.SaveDisplayRegion, savezoom)
+
+        # 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):
+        """!Set properies of map display window"""
+        self.statusbarWin['render'].SetValue(render)
+        self.statusbarWin['toggle'].SetSelection(mode)
+        self.StatusbarUpdate()
+        self.statusbarWin['region'].SetValue(showCompExtent)
+        self.statusbarWin['resolution'].SetValue(constrainRes)
+        self.statusbarWin['projection'].SetValue(projection)
+        if showCompExtent:
+            self.MapWindow.regionCoords = []
+        
+    def IsStandalone(self):
+        """!Check if Map display is standalone"""
+        if self._layerManager:
+            return False
+        
+        return True
+    
+    def GetLayerManager(self):
+        """!Get reference to Layer Manager
+
+        @return window reference
+        @return None (if standalone)
+        """
+        return self._layerManager
+    
+# end of class MapFrame
+
+class MapApp(wx.App):
+    """
+    MapApp class
+    """
+
+    def OnInit(self):
+
+        wx.InitAllImageHandlers()
+        if __name__ == "__main__":
+            Map = render.Map() # instance of Map class to render GRASS display output to PPM file
+        else:
+            Map = None
+
+        self.mapFrm = MapFrame(parent=None, id=wx.ID_ANY, Map=Map,
+                               size=globalvar.MAP_WINDOW_SIZE)
+        #self.SetTopWindow(Map)
+        self.mapFrm.Show()
+
+        if __name__ == "__main__":
+            # redraw map, if new command appears
+            self.redraw = False
+
+
+        return 1
+
+
+# end of class MapApp
+
+if __name__ == "__main__":
+
+    ###### SET command variable
+
+
+    title = "title"
+    cmdfilename = "cmdfilename"
+
+    
+
+    gm_map = MapApp(0)
+    # set title
+    gm_map.mapFrm.SetTitle(_("GRASS GIS Map Display: " +   title +   " - Location: " + grass.gisenv()["LOCATION_NAME"]))
+    
+    gm_map.MainLoop()
+    sys.exit(0)

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

Added: grass-addons/gui/wxpython/data_catalog/newprompt.py
===================================================================
--- grass-addons/gui/wxpython/data_catalog/newprompt.py	                        (rev 0)
+++ grass-addons/gui/wxpython/data_catalog/newprompt.py	2010-02-03 16:29:51 UTC (rev 40794)
@@ -0,0 +1,604 @@
+"""!
+ at package prompt.py
+
+ at brief GRASS prompt
+
+Classes:
+ - GPrompt
+ - PromptListCtrl
+ - TextCtrlAutoComplete
+
+ at todo: fix TextCtrlAutoComplete to work also on Macs (missing
+wx.PopupWindow())
+
+(C) 2009 by the GRASS Development Team
+This program is free software under the GNU General Public
+License (>=v2). Read the file COPYING that comes with GRASS
+for details.
+
+ at author Martin Landa <landa.martin gmail.com>
+ at author Mohammed Rashad K.M <rashadkm at gmail dot com> (modified for DataCatalog)
+"""
+
+import os
+import sys
+import shlex
+
+import wx
+import wx.lib.mixins.listctrl as listmix
+
+from grass.script import core as grass
+
+import globalvar
+import utils
+import menuform
+import menudata
+import goutput
+
+class GPrompt:
+    """!Interactive GRASS prompt"""
+    def __init__(self, parent):
+       # self.parent = parent # GMFrame
+        
+        # dictionary of modules (description, keywords, ...)
+
+
+
+        self.parent=parent.GetParent()
+        winlist = self.parent.GetChildren()
+        for win in winlist:
+            if type(win) == wx._windows.StatusBar:
+                self.statusbar=win
+        self.modules = self.parent.menudata.GetModules()
+        
+        self.panel, self.input = self.__create()
+        #self.goutput = goutput
+        self.goutput = goutput.GMConsole(self.parent, pageid=1)
+        self.goutput.Hide()
+        self.goutput.Redirect()
+        
+    def __create(self):
+        """!Create widget"""
+        cmdprompt = wx.Panel(self.parent)
+        
+        #
+        # search
+        #
+        #searchTxt = wx.StaticText(parent = cmdprompt, id = wx.ID_ANY,                                  label = _("Search:"))
+        
+        #self.searchBy = wx.Choice(parent = cmdprompt, id = wx.ID_ANY,     choices = [_("description"),  _("keywords")])
+        winHeight =30
+
+        #self.search = wx.TextCtrl(parent = cmdprompt, id = wx.ID_ANY,                             value = "", size = (-1, 25))
+        
+        label = wx.Button(parent = cmdprompt, id = wx.ID_ANY,
+                          label = _("Cmd >"), size = (-1, winHeight))
+        label.SetToolTipString(_("Click for erasing command prompt"))
+
+        ### todo: fix TextCtrlAutoComplete to work also on Macs
+        ### reason: missing wx.PopupWindow()
+
+        try:
+            cmdinput = TextCtrlAutoComplete(parent = cmdprompt, id = wx.ID_ANY,
+                                            value = "",
+                                            style = wx.TE_LINEWRAP | wx.TE_PROCESS_ENTER,
+                                            size = (-1, winHeight),
+                                            statusbar = self.statusbar)
+        except NotImplementedError:
+            # wx.PopupWindow may be not available in wxMac
+            # see http://trac.wxwidgets.org/ticket/9377
+            cmdinput = wx.TextCtrl(parent = cmdprompt, id = wx.ID_ANY,
+                                   value = "",
+                                   style=wx.TE_LINEWRAP | wx.TE_PROCESS_ENTER,
+                                   size = (-1, 25))
+#            self.searchBy.Enable(False)
+            #self.search.Enable(False)
+        
+        cmdinput.SetFont(wx.Font(10, wx.FONTFAMILY_MODERN, wx.NORMAL, wx.NORMAL, 0, ''))
+        
+        wx.CallAfter(cmdinput.SetInsertionPoint, 0)
+        
+        # bidnings
+        label.Bind(wx.EVT_BUTTON,        self.OnCmdErase)
+        cmdinput.Bind(wx.EVT_TEXT_ENTER, self.OnRunCmd)
+        cmdinput.Bind(wx.EVT_TEXT,       self.OnUpdateStatusBar)
+        #self.search.Bind(wx.EVT_TEXT,    self.OnSearchModule)
+        
+        # layout
+        sizer = wx.GridBagSizer(hgap=5, vgap=5)
+        sizer.AddGrowableCol(2)
+
+
+        
+        sizer.Add(item = label, 
+                  flag = wx.LEFT | wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER,
+                  border = 5,
+                  pos = (0, 1))
+        
+        sizer.Add(item = cmdinput,
+                  flag = wx.EXPAND | wx.RIGHT,
+                  border = 5,
+                  pos = (0, 2))
+        
+        cmdprompt.SetSizer(sizer)
+        sizer.Fit(cmdprompt)
+        cmdprompt.Layout()
+        
+        return cmdprompt, cmdinput
+
+    def __checkKey(self, text, keywords):
+        """!Check if text is in keywords"""
+        found = 0
+        keys = text.split(',')
+        if len(keys) > 1: # -> multiple keys
+            for k in keys[:-1]:
+                k = k.strip()
+                for key in keywords: 
+                    if k == key: # full match
+                        found += 1
+                        break
+            k = keys[-1].strip()
+            for key in keywords:
+                if k in key: # partial match
+                    found +=1
+                    break
+        else:
+            for key in keywords:
+                if text in key: # partial match
+                    found +=1
+                    break
+        
+        if found == len(keys):
+            return True
+        
+        return False
+    
+    def GetPanel(self):
+        """!Get main widget panel"""
+        return self.panel
+
+    def GetInput(self):
+        """!Get main prompt widget"""
+        return self.input
+    
+    def OnCmdErase(self, event):
+        """!Erase command prompt"""
+        self.input.SetValue('')
+        
+    def OnRunCmd(self, event):
+        """!Run command"""
+        cmdString = event.GetString()
+        
+        #if self.parent.GetName() != "LayerManager":
+        #    return
+        
+        if cmdString[:2] == 'd.' and not self.parent.current:
+            self.parent.NewDisplay()
+        
+        cmd = shlex.split(str(cmdString))
+        if len(cmd) > 1:
+            self.goutput.RunCmd(cmd, switchPage = True)
+        else:
+            self.goutput.RunCmd(cmd, switchPage = False)
+        
+        self.OnUpdateStatusBar(None)
+        
+    def OnUpdateStatusBar(self, event):
+        """!Update Layer Manager status bar"""
+        if self.parent.GetName() != "LayerManager":
+            return
+        
+        if event is None:
+            self.parent.statusbar.SetStatusText("")
+        else:
+            self.parent.statusbar.SetStatusText(_("Type GRASS command and run by pressing ENTER"))
+            event.Skip()
+        
+#    def OnSearchModule(self, event):
+#        """!Search module by metadata"""
+#        print "here"
+#        text = event.GetString()
+#        if not text:
+#            self.input.SetChoices(globalvar.grassCmd['all'])
+#            return
+#        
+#        modules = []
+#        for module, data in self.modules.iteritems():
+#         
+#            if self.searchBy.GetSelection() == 0: # -> description
+#                if text in data['desc']:
+#                    modules.append(module)
+#            else: # -> keywords
+#                if self.__checkKey(text, data['keywords']):
+#                    modules.append(module)
+#        
+#        self.statusbar.SetStatusText(_("%d modules found") % len(modules))
+#        self.input.SetChoices(modules)
+                    
+class PromptListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
+    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):
+    def __init__ (self, parent, statusbar,
+                  id = wx.ID_ANY, choices = [], **kwargs):
+        """!Constructor works just like wx.TextCtrl except you can pass in a
+        list of choices.  You can also change the choice list at any time
+        by calling setChoices.
+        
+        Inspired by http://wiki.wxpython.org/TextCtrlAutoComplete
+        """
+        self.statusbar = statusbar
+        
+        if kwargs.has_key('style'):
+            kwargs['style'] = wx.TE_PROCESS_ENTER | kwargs['style']
+        else:
+            kwargs['style'] = wx.TE_PROCESS_ENTER
+        
+        wx.ComboBox.__init__(self, parent, id, **kwargs)
+        
+        # some variables
+        self._choices = choices
+        self._hideOnNoMatch = True
+        self._module = None # currently selected module
+        self._choiceType = None # type of choice (module, params, flags, raster, vector ...)
+        self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
+        
+        # sort variable needed by listmix
+        self.itemDataMap = dict()
+        
+        # widgets
+        try:
+            self.dropdown = wx.PopupWindow(self)
+        except NotImplementedError:
+            self.Destroy()
+            raise NotImplementedError
+        
+        # create the list and bind the events
+        self.dropdownlistbox = PromptListCtrl(parent = self.dropdown,
+                                              style = wx.LC_REPORT | wx.LC_SINGLE_SEL | \
+                                                  wx.LC_SORT_ASCENDING | wx.LC_NO_HEADER,
+                                              pos = wx.Point(0, 0))
+        
+        listmix.ColumnSorterMixin.__init__(self, 1)
+        
+        # set choices (list of GRASS modules)
+        self._choicesCmd = globalvar.grassCmd['all']
+        self._choicesMap = dict()
+        for type in ('raster', 'vector'):
+            self._choicesMap[type] = grass.list_strings(type = type[:4])
+        # first search for GRASS module
+        self.SetChoices(self._choicesCmd)
+        
+        self.SetMinSize(self.GetSize())
+        # read history
+        self.SetHistoryItems()
+        
+        # bindings...
+        self.Bind(wx.EVT_KILL_FOCUS, self.OnControlChanged)
+        self.Bind(wx.EVT_TEXT, self.OnEnteredText)
+        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 _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 > -1:
+            if self._colFetch != -1:
+                col = self._colFetch
+            else:
+                col = self._colSearch
+            itemtext = self.dropdownlistbox.GetItem(sel, col).GetText()
+            
+            cmd = shlex.split(str(self.GetValue()))
+            if len(cmd) > 1:
+                # -> append text (skip last item)
+                if self._choiceType == 'param':
+                    self.SetValue(' '.join(cmd[:-1]) + ' ' + 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':
+                    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 + ' ')
+            self.SetInsertionPointEnd()
+            
+            self._showDropDown(False)
+         
+    def GetListCtrl(self):
+        """!Method required by listmix.ColumnSorterMixin"""
+        return self.dropdownlistbox
+    
+    def SetHistoryItems(self):
+        """!Read history file and update combobox items"""
+        env = grass.gisenv()
+        try:
+            fileHistory = open(os.path.join(env['GISDBASE'],
+                                            env['LOCATION_NAME'],
+                                            env['MAPSET'],
+                                            '.bash_history'), 'r')
+        except IOError:
+            self.SetItems([])
+            return
+        
+        try:
+            hist = []
+            for line in fileHistory.readlines():
+                hist.append(line.replace('\n', ''))
+            
+            self.SetItems(hist)
+        finally:
+            fileHistory.close()
+            return
+        
+        self.SetItems([])
+        
+    def SetChoices(self, choices, type = 'module'):
+        """!Sets the choices available in the popup wx.ListBox.
+        The items will be sorted case insensitively.
+        """
+        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.SetFocus()
+        
+    def OnListClick(self, evt):
+        """!Left mouse button pressed"""
+        toSel, flag = self.dropdownlistbox.HitTest( evt.GetPosition() )
+        #no values on poition, return
+        if toSel == -1: return
+        self.dropdownlistbox.Select(toSel)
+
+    def OnListDClick(self, evt):
+        """!Mouse button double click"""
+        self._setValueFromSelected()
+
+    def OnListColClick(self, evt):
+        """!Left mouse button pressed on column"""
+        col = evt.GetColumn()
+        # reverse the sort
+        if col == self._colSearch:
+            self._ascending = not self._ascending
+        self.SortListItems( evt.GetColumn(), ascending=self._ascending )
+        self._colSearch = evt.GetColumn()
+        evt.Skip()
+
+    def OnListItemSelected(self, event):
+        """!Item selected"""
+        self._setValueFromSelected()
+        event.Skip()
+
+    def OnEnteredText(self, event):
+        """!Text entered"""
+        text = event.GetString()
+        
+        if not text:
+            # control is empty; hide dropdown if shown:
+            if self.dropdown.IsShown():
+                self._showDropDown(False)
+            event.Skip()
+            return
+        
+        try:
+            cmd = shlex.split(str(text))
+        except ValueError, e:
+            self.statusbar.SetStatusText(str(e))
+            cmd = text.split(' ')
+        
+        pattern = str(text)
+        if len(cmd) > 1:
+            # search for module's options
+            if cmd[0] in self._choicesCmd and not self._module:
+                try:
+                    self._module = menuform.GUI().ParseInterface(cmd = cmd)
+                except IOError:
+                    self._module = None
+
+            if self._module:
+                if len(cmd[-1].split('=', 1)) == 1:
+                    # new option
+                    if cmd[-1][0] == '-':
+                        # -> flags
+                        self.SetChoices(self._module.get_list_flags(), type = 'flag')
+                        pattern = cmd[-1].lstrip('-')
+                    else:
+                        # -> options
+                        self.SetChoices(self._module.get_list_params(), 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
+        
+        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 <%s> not found in '%s'" % \
+                            (cmd[-1][1:], self._module.name)
+                    else: # option
+                        message = "Warning: option <%s> not found in '%s'" % \
+                            (cmd[-1], 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_DOWN:
+            if sel < (self.dropdownlistbox.GetItemCount() - 1):
+                self.dropdownlistbox.Select(sel + 1)
+                self._listItemVisible()
+            self._showDropDown()
+            skip = False
+        elif KC == wx.WXK_UP:
+            if sel > 0:
+                self.dropdownlistbox.Select(sel - 1)
+                self._listItemVisible()
+            self._showDropDown()
+            skip = False
+        
+        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()

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



More information about the grass-commit mailing list