[GRASS-SVN] r31854 - in grass-addons/visualization/nviz2: lib wxpython wxpython/nviz

svn_grass at osgeo.org svn_grass at osgeo.org
Thu Jun 26 10:05:06 EDT 2008


Author: martinl
Date: 2008-06-26 10:05:06 -0400 (Thu, 26 Jun 2008)
New Revision: 31854

Added:
   grass-addons/visualization/nviz2/wxpython/wxgui_utils.py
Modified:
   grass-addons/visualization/nviz2/lib/change_view.c
   grass-addons/visualization/nviz2/wxpython/mapdisp.py
   grass-addons/visualization/nviz2/wxpython/nviz.py
   grass-addons/visualization/nviz2/wxpython/nviz/change_view.cpp
   grass-addons/visualization/nviz2/wxpython/nviz/dig_types.i
   grass-addons/visualization/nviz2/wxpython/nviz/draw.cpp
   grass-addons/visualization/nviz2/wxpython/nviz/nviz.h
Log:
nviz2: wxGUI integration in progress (reset view, surface properties)

Modified: grass-addons/visualization/nviz2/lib/change_view.c
===================================================================
--- grass-addons/visualization/nviz2/lib/change_view.c	2008-06-26 12:39:06 UTC (rev 31853)
+++ grass-addons/visualization/nviz2/lib/change_view.c	2008-06-26 14:05:06 UTC (rev 31854)
@@ -16,6 +16,7 @@
   \date 2008
 */
 
+#include <grass/glocale.h>
 #include <grass/nviz.h>
 
 /*!
@@ -104,6 +105,12 @@
     ypos = 1.0 - y_pos;
     ypos = (ypos < 0) ? 0 : (ypos > 1.0) ? 1.0 : ypos;
 
+    if (x_pos < 0.0 || x_pos > 1.0 ||
+	y_pos < 0.0 || y_pos > 1.0) {
+	G_warning (_("Invalid view position coordinates, using %f,%f"),
+		   xpos, 1.0 - ypos);
+    }
+
     GS_get_from(from);
     
     tempx = xpos * RANGE - RANGE_OFFSET;
@@ -116,7 +123,7 @@
 
 	GS_moveto(from);
 
-	Nviz_draw_quick(data);
+	/* Nviz_draw_quick(data); */
     }
 
     return 1;
@@ -146,7 +153,7 @@
 	   GS_setlight_position(1, from[X], from[Y], from[Z], 0);
 	*/
 
-	Nviz_draw_quick(data);
+	/* Nviz_draw_quick(data); */
     }
 
     return 1;
@@ -167,7 +174,7 @@
     fov = (int) (10 * persp);
     GS_set_fov(fov);
 
-    Nviz_draw_quick(data);
+    /* Nviz_draw_quick(data); */
 
     return 1;
 }
@@ -184,7 +191,7 @@
 {
     GS_set_twist(10 * twist);
     
-    Nviz_draw_quick(data);
+    /* Nviz_draw_quick(data); */
 
     return 1;
 }
@@ -207,7 +214,7 @@
 	GS_set_global_exag(exag);
 	Nviz_update_ranges(data);
 	
-	Nviz_draw_quick(data);
+	/* Nviz_draw_quick(data); */
     }
 
     return 1;

Modified: grass-addons/visualization/nviz2/wxpython/mapdisp.py
===================================================================
--- grass-addons/visualization/nviz2/wxpython/mapdisp.py	2008-06-26 12:39:06 UTC (rev 31853)
+++ grass-addons/visualization/nviz2/wxpython/mapdisp.py	2008-06-26 14:05:06 UTC (rev 31854)
@@ -2560,13 +2560,15 @@
                 self.MapWindow3D = nviz.GLWindow(self, id=wx.ID_ANY,
                                                  Map=self.Map, tree=self.tree, gismgr=self.gismanager)
                 self.nvizToolWin = nviz.NvizToolWindow(self, id=wx.ID_ANY,
-                                                       settings=self.MapWindow3D.view)
+                                                       mapWindow=self.MapWindow3D)
             
             #
             # add Nviz toolbar and disable 2D display mode tools
             #
             self.toolbars['nviz'] = toolbars.NvizToolbar(self, self.Map)
             self.toolbars['map'].Enable2D(False)
+            self.toggleStatus.Enable(False)
+
             self.nvizToolWin.Show()
 
             #
@@ -2628,6 +2630,7 @@
         
         self.toolbars['map'].combo.SetValue ("Tools")
         self.toolbars['map'].Enable2D(True)
+        self.toggleStatus.Enable(True)
 
         self._mgr.Update()
 

Modified: grass-addons/visualization/nviz2/wxpython/nviz/change_view.cpp
===================================================================
--- grass-addons/visualization/nviz2/wxpython/nviz/change_view.cpp	2008-06-26 12:39:06 UTC (rev 31853)
+++ grass-addons/visualization/nviz2/wxpython/nviz/change_view.cpp	2008-06-26 14:05:06 UTC (rev 31854)
@@ -45,10 +45,10 @@
     Nviz_get_exag_height(&vp_height, NULL, NULL);
 
     Nviz_change_exag(data,
-		     1.0);
+		     VIEW_DEFAULT_ZEXAG);
 
-    SetView(0.85, 0.85,
-	    vp_height, 40.0, 0.0);
+    SetView(VIEW_DEFAULT_POS_X, VIEW_DEFAULT_POS_Y,
+	    vp_height, VIEW_DEFAULT_PERSP, VIEW_DEFAULT_TWIST);
 
     return vp_height;
 }
@@ -62,7 +62,6 @@
   \param twist
 
   \return 1 on success
-  \return 0 on failure
 */
 int Nviz::SetView(float x, float y,
 		  float height, float persp, float twist)
@@ -78,3 +77,15 @@
 
     return 1;
 }
+
+/*!
+  \brief Set z-exag value
+
+  \param z_exag value
+
+  \return 1
+*/
+int Nviz::SetZExag(float z_exag)
+{
+    return Nviz_change_exag(data, z_exag);
+}

Modified: grass-addons/visualization/nviz2/wxpython/nviz/dig_types.i
===================================================================
--- grass-addons/visualization/nviz2/wxpython/nviz/dig_types.i	2008-06-26 12:39:06 UTC (rev 31853)
+++ grass-addons/visualization/nviz2/wxpython/nviz/dig_types.i	2008-06-26 14:05:06 UTC (rev 31854)
@@ -13,3 +13,11 @@
 #define GV_LINES (GV_LINE | GV_BOUNDARY )
 
 #define PORT_DOUBLE_MAX 1.7976931348623157e+308
+
+/* extracted from gui/wxpython/nviz/nviz.h */
+
+#define VIEW_DEFAULT_POS_X 0.85
+#define VIEW_DEFAULT_POS_Y 0.85
+#define VIEW_DEFAULT_PERSP 40.0
+#define VIEW_DEFAULT_TWIST 0.0
+#define VIEW_DEFAULT_ZEXAG 1.0

Modified: grass-addons/visualization/nviz2/wxpython/nviz/draw.cpp
===================================================================
--- grass-addons/visualization/nviz2/wxpython/nviz/draw.cpp	2008-06-26 12:39:06 UTC (rev 31853)
+++ grass-addons/visualization/nviz2/wxpython/nviz/draw.cpp	2008-06-26 14:05:06 UTC (rev 31854)
@@ -19,13 +19,22 @@
 
 #include "nviz.h"
 
-void Nviz::Draw()
+/*!
+  \brief Draw map
+
+  \param quick true for quick rendering
+*/
+void Nviz::Draw(bool quick)
 {
     GS_clear(data->bgcolor);
     
     Nviz_draw_cplane(data, -1, -1);
-    Nviz_draw_all (data);
 
+    if (!quick)
+	Nviz_draw_all (data);
+    else
+	Nviz_draw_quick(data);
+
     return;
 }
 

Modified: grass-addons/visualization/nviz2/wxpython/nviz/nviz.h
===================================================================
--- grass-addons/visualization/nviz2/wxpython/nviz/nviz.h	2008-06-26 12:39:06 UTC (rev 31853)
+++ grass-addons/visualization/nviz2/wxpython/nviz/nviz.h	2008-06-26 14:05:06 UTC (rev 31854)
@@ -24,6 +24,12 @@
 
 #include <wx/glcanvas.h>
 
+#define VIEW_DEFAULT_POS_X 0.85
+#define VIEW_DEFAULT_POS_Y 0.85
+#define VIEW_DEFAULT_PERSP 40.0
+#define VIEW_DEFAULT_TWIST 0.0
+#define VIEW_DEFAULT_ZEXAG 1.0
+
 class Nviz
 {
 private:
@@ -54,6 +60,7 @@
     float SetViewDefault();
     int SetView(float, float,
 		float, float, float);
+    int SetZExag(float);
 
     /* init.cpp */
     void InitView();
@@ -64,7 +71,7 @@
 
     /* draw */
     /* draw.cpp */
-    void Draw();
+    void Draw(bool);
     void EraseMap();
 };
 

Modified: grass-addons/visualization/nviz2/wxpython/nviz.py
===================================================================
--- grass-addons/visualization/nviz2/wxpython/nviz.py	2008-06-26 12:39:06 UTC (rev 31853)
+++ grass-addons/visualization/nviz2/wxpython/nviz.py	2008-06-26 14:05:06 UTC (rev 31854)
@@ -7,6 +7,7 @@
  - GLWindow
  - NvizToolWindow
  - ViewPositionWindow
+ - RasterPropertiesDialog
 
 (C) 2008 by the GRASS Development Team
 
@@ -103,18 +104,31 @@
         #
         self.view = { 'persp' : { 'value' : 40,
                                   'min' : 0,
-                                  'max' : 100
+                                  'max' : 100,
+                                  'step' : 5,
+                                  'update' : False,
                                   },
                       'pos' : { 'value' : (0.85, 0.85),
+                                'update' : False,
                                 },
                       'height' : { 'value': -1,
                                    'min' : -2245, # TODO: determine min/max height
-                                   'max' : 3695 
+                                   'max' : 3695, 
+                                   'step' : 100,
+                                   'update' : False,
                                    },
                       'twist' : { 'value' : 0,
                                   'min' : -180,
-                                  'max' : 180
+                                  'max' : 180,
+                                  'step' : 5,
+                                  'update' : False,
                                   },
+                      'z-exag' : { 'value': 1.0,
+                                   'min' : 0.0,
+                                   'max' : 10,
+                                   'step' : 1,
+                                   'update' : False
+                                   },
                       }
 
         self.size = None
@@ -168,9 +182,9 @@
             current  = event.GetPositionTuple()[:]
             Debug.msg (5, "GLWindow.OnMouseMotion(): wheel=%d" % wheel)
             if wheel > 0:
-                value = -5 # TODO: settings
+                value = -1 * self.view['persp']['step']
             else:
-                value = 5
+                value = self.view['persp']['step']
             self.view['persp']['value'] += value
             if self.view['persp']['value'] < 0:
                 self.view['persp']['value'] = 0
@@ -198,12 +212,15 @@
     def OnLeftUp(self, event):
         self.ReleaseMouse()
 
-    def UpdateMap(self, render=True):
+    def UpdateMap(self, render=False):
         """
         Updates the canvas anytime there is a change to the
         underlaying images or to the geometry of the canvas.
 
-        @todo render=False
+        render:
+         - None do not render (todo)
+         - True render
+         - False quick render
 
         @param render re-render map composition
         """
@@ -228,20 +245,33 @@
         #glRotatef((self.y - self.lastY) * yScale, 1.0, 0.0, 0.0);
         #glRotatef((self.x - self.lastX) * xScale, 0.0, 1.0, 0.0);
 
-        if render is True:
+        if render is not None:
             self.parent.onRenderGauge.Show()
             if self.parent.onRenderGauge.GetRange() > 0:
                 self.parent.onRenderGauge.SetValue(1)
                 self.parent.onRenderTimer.Start(100)
             self.parent.onRenderCounter = 0
 
-        self.nvizClass.SetView(self.view['pos']['value'][0], self.view['pos']['value'][1],
-                               self.view['height']['value'],
-                               self.view['persp']['value'],
-                               self.view['twist']['value'])
+        if self.view['pos']['update'] or \
+                self.view['height']['update'] or \
+                self.view['persp']['update'] or \
+                self.view['twist']['update']:
+            self.nvizClass.SetView(self.view['pos']['value'][0], self.view['pos']['value'][1],
+                                   self.view['height']['value'],
+                                   self.view['persp']['value'],
+                                   self.view['twist']['value'])
+            for control in ('pos', 'height', 'persp', 'twist'):
+                self.view[control]['update'] = False
 
-        self.nvizClass.Draw()
+        if self.view['z-exag']['update']:
+            self.nvizClass.SetZExag(self.view['z-exag']['value'])
+            self.view['z-exag']['update'] = False
 
+        if render is True:
+            self.nvizClass.Draw(False)
+        elif render is False:
+            self.nvizClass.Draw(True) # quick
+
         self.SwapBuffers()
 
         stop = time.clock()
@@ -262,6 +292,8 @@
         Debug.msg(3, "GLWindow.UpdateMap(): render=%s, -> time=%g" % \
                       (render, (stop-start)))
 
+        print '# %.6f' % (stop-start)
+
     def EraseMap(self):
         """
         Erase the canvas
@@ -299,17 +331,31 @@
 
         self.nvizClass.SetViewportDefault()
 
+    def ResetView(self):
+        """Reset to default view"""
+        self.view['pos']['value'] = (wxnviz.VIEW_DEFAULT_POS_X,
+                                     wxnviz.VIEW_DEFAULT_POS_Y)
+        self.view['height']['value'] = self.nvizClass.SetViewDefault()
+        self.view['persp']['value'] = wxnviz.VIEW_DEFAULT_PERSP
+        self.view['twist']['value'] = wxnviz.VIEW_DEFAULT_TWIST
+        self.view['z-exag']['value'] = wxnviz.VIEW_DEFAULT_ZEXAG
+
+        for control in ('pos', 'height', 'persp',
+                        'twist', 'z-exag'):
+            self.view[control]['update'] = True
+
 class NvizToolWindow(wx.Frame):
     """Experimental window for Nviz tools
 
-    @todo integrated with Map display
+    @todo integrate with Map display
     """
     def __init__(self, parent=None, id=wx.ID_ANY, title=_("Nviz tools"),
                  pos=wx.DefaultPosition, size=wx.DefaultSize,
-                 style=wx.DEFAULT_FRAME_STYLE, settings={}):
+                 style=wx.DEFAULT_FRAME_STYLE, mapWindow=None):
         
         self.parent = parent # MapFrame
-        self.settings = settings # GLWindow.view
+        self.mapWindow = mapWindow
+        self.settings = mapWindow.view # GLWindow.view
 
         wx.Frame.__init__(self, parent, id, title, pos, size, style)
 
@@ -338,20 +384,24 @@
         pageSizer = wx.BoxSizer(wx.VERTICAL)
         gridSizer = wx.GridBagSizer(vgap=3, hgap=3)
 
+        # position
         posSizer = wx.GridBagSizer(vgap=3, hgap=3)
         posSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("W")),
                      pos=(1, 0), flag=wx.ALIGN_CENTER)
         posSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("N")),
                      pos=(0, 1), flag=wx.ALIGN_CENTER | wx.ALIGN_BOTTOM)
-        posSizer.Add(item=ViewPositionWindow(panel, id=wx.ID_ANY, size=(175, 175),
-                                             settings=self.settings, mapwindow=self.parent.MapWindow3D),
+        viewPos = ViewPositionWindow(panel, id=wx.ID_ANY, size=(175, 175),
+                                     settings=self.settings, mapwindow=self.mapWindow)
+        self.win['pos'] = viewPos.GetId()
+        posSizer.Add(item=viewPos,
                      pos=(1, 1), flag=wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL)
         posSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("S")),
                      pos=(2, 1), flag=wx.ALIGN_CENTER | wx.ALIGN_TOP)
         posSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("E")),
                      pos=(1, 2), flag=wx.ALIGN_CENTER)
         gridSizer.Add(item=posSizer, pos=(0, 0))
-                                         
+                  
+        # perspective
         self.CreateControl(panel, 'persp')
         gridSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("Perspective:")),
                       pos=(1, 0), flag=wx.ALIGN_CENTER)
@@ -359,6 +409,7 @@
         gridSizer.Add(item=self.FindWindowById(self.win['persp']['spin']), pos=(3, 0),
                       flag=wx.ALIGN_CENTER)        
 
+        # twist
         self.CreateControl(panel, 'twist')
         gridSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("Twist:")),
                       pos=(1, 1), flag=wx.ALIGN_CENTER)
@@ -366,7 +417,9 @@
         gridSizer.Add(item=self.FindWindowById(self.win['twist']['spin']), pos=(3, 1),
                       flag=wx.ALIGN_CENTER)        
 
+        # height + z-exag
         self.CreateControl(panel, 'height', sliderHor=False)
+        self.CreateControl(panel, 'z-exag', sliderHor=False)
         heightSizer = wx.GridBagSizer(vgap=3, hgap=3)
         heightSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("Height:")),
                       pos=(0, 0), flag=wx.ALIGN_LEFT, span=(1, 2))
@@ -375,8 +428,53 @@
         heightSizer.Add(item=self.FindWindowById(self.win['height']['spin']),
                         flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT | wx.TOP |
                         wx.BOTTOM | wx.RIGHT, pos=(1, 1))
+        heightSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY, label=_("Z-exag:")),
+                      pos=(0, 2), flag=wx.ALIGN_LEFT, span=(1, 2))
+        heightSizer.Add(item=self.FindWindowById(self.win['z-exag']['slider']),
+                        flag=wx.ALIGN_RIGHT, pos=(1, 2))
+        heightSizer.Add(item=self.FindWindowById(self.win['z-exag']['spin']),
+                        flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT | wx.TOP |
+                        wx.BOTTOM | wx.RIGHT, pos=(1, 3))
+
         gridSizer.Add(item=heightSizer, pos=(0, 1), flag=wx.ALIGN_RIGHT)
 
+        # view setup + reset
+        viewSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+        viewSizer.Add(item=wx.StaticText(panel, id=wx.ID_ANY,
+                                         label=_("Look at:")),
+                      flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=5)
+        
+        viewType = wx.Choice (parent=panel, id=wx.ID_ANY, size=(125, -1),
+                              choices = [_("top"),
+                                         _("north"),
+                                         _("south"),
+                                         _("east"),
+                                         _("west"),
+                                         _("north-west"),
+                                         _("north-east"),
+                                         _("south-east"),
+                                         _("south-west")])
+        viewType.SetSelection(0)
+        viewType.Bind(wx.EVT_CHOICE, self.OnLookAt)
+        # self.win['lookAt'] = viewType.GetId()
+        viewSizer.Add(item=viewType, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL,
+                      border=5)
+
+        reset = wx.Button(panel, id=wx.ID_ANY, label=_("Reset"))
+        reset.SetToolTipString(_("Reset to default view"))
+        # self.win['reset'] = reset.GetId()
+        reset.Bind(wx.EVT_BUTTON, self.OnResetView)
+
+        viewSizer.Add(item=reset, proportion=1,
+                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT,
+                      border=5)
+
+        gridSizer.AddGrowableCol(3)
+        gridSizer.Add(item=viewSizer, pos=(4, 0), span=(1, 2),
+                      flag=wx.EXPAND)
+
+        # body
         pageSizer.Add(item=gridSizer, proportion=1,
                       flag=wx.EXPAND | wx.ALL,
                       border=5)
@@ -408,6 +506,11 @@
                            initial=self.settings[name]['value'],
                            min=self.settings[name]['min'],
                            max=self.settings[name]['max'])
+        #         spin = wx.SpinButton(parent=parent, id=wx.ID_ANY)
+        #         spin.SetValue (self.settings[name]['value'])
+        #         spin.SetRange(self.settings[name]['min'],
+        #                      self.settings[name]['max'])
+
         spin.Bind(wx.EVT_SPINCTRL, self.OnChangeValue)
         self.win[name]['spin'] = spin.GetId()
 
@@ -416,18 +519,27 @@
         """Update dialog settings"""
         for control in ('height',
                         'persp',
-                        'twist'):
+                        'twist',
+                        'z-exag'):
             for win in self.win[control].itervalues():
                 self.FindWindowById(win).SetValue(self.settings[control]['value'])
 
+        self.FindWindowById(self.win['pos']).Draw()
+        self.FindWindowById(self.win['pos']).Refresh(False)
+        
         self.Refresh(False)
         
     def OnChangeValue(self, event):
         # find control
         winName = ''
         for name in self.win.iterkeys():
-            for win in self.win[name].itervalues():
-                if win == event.GetId():
+            if type(self.win[name]) is type({}):
+                for win in self.win[name].itervalues():
+                    if win == event.GetId():
+                        winName = name
+                        break
+            else:
+                if self.win[name] == event.GetId():
                     winName = name
                     break
 
@@ -438,8 +550,43 @@
         for win in self.win[winName].itervalues():
             self.FindWindowById(win).SetValue(self.settings[winName]['value'])
 
-        self.parent.MapWindow3D.Refresh(False)
 
+        self.settings[winName]['update'] = True
+
+        self.mapWindow.Refresh(False)
+
+    def OnResetView(self, event):
+        """Reset to default view"""
+        self.mapWindow.ResetView()
+        self.UpdateSettings()
+        self.mapWindow.Refresh(False)
+
+    def OnLookAt(self, event):
+        """Look at"""
+        sel = event.GetSelection()
+        if sel == 0: # top
+            self.settings['pos']['value'] = (0.5, 0.5)
+        elif sel == 1: # north
+            self.settings['pos']['value'] = (0.5, 0.0)
+        elif sel == 2: # south
+            self.settings['pos']['value'] = (0.5, 1.0)
+        elif sel == 3: # east
+            self.settings['pos']['value'] = (1.0, 0.5)
+        elif sel == 4: # west
+            self.settings['pos']['value'] = (0.0, 0.5)
+        elif sel == 5: # north-west
+            self.settings['pos']['value'] = (0.0, 0.0)
+        elif sel == 6: # north-east
+            self.settings['pos']['value'] = (1.0, 0.0)
+        elif sel == 7: # south-east
+            self.settings['pos']['value'] = (1.0, 1.0)
+        elif sel == 8: # south-west
+            self.settings['pos']['value'] = (0.0, 1.0)
+
+        self.settings['pos']['update'] = True
+        self.UpdateSettings()
+        self.mapWindow.Refresh(False)
+
 class ViewPositionWindow(wx.Window):
     """Position control window (for NvizToolWindow)"""
     def __init__(self, parent, id, mapwindow,
@@ -465,19 +612,17 @@
 
     def Draw(self, pos=None):
         w, h = self.GetClientSize()
-        w2 = w / 2
-        h2 = h / 2
 
         if pos is None:
             x, y = self.settings['pos']['value']
-            x = x * w2 + w2
-            y = y * h2 + h2
+            x = x * w
+            y = y * h
         else:
             x, y = pos
 
         self.pdc.Clear()
         self.pdc.BeginDrawing()
-        self.pdc.DrawLine(w2, h2, x, y)
+        self.pdc.DrawLine(w / 2, h / 2, x, y)
         self.pdc.DrawCircle(x, y, 5)
         self.pdc.EndDrawing()
 
@@ -495,10 +640,126 @@
             self.Draw(pos=(x, y))
             self.Refresh(False)
             w, h = self.GetClientSize()
-            w2 = w / 2.
-            h2 = h / 2.
-            x = x / w2 - 1
-            y = y / h2 - 1
+            x = float(x) / w
+            y = float(y) / h
             self.settings['pos']['value'] = (x, y)
+            self.settings['pos']['update'] = True
 
-            self.mapWindow.Refresh(False)
+            self.mapWindow.Refresh(eraseBackground=False)
+            # self.mapWindow.UpdateMap()
+            # self.mapWindow.OnPaint(None)
+
+class RasterPropertiesDialog(wx.Dialog):
+    """Nviz raster properties
+
+    @todo integrate into Nviz tools window or d.rast dialog ?
+    """
+    def __init__(self, parent, map,
+                 pos=wx.DefaultPosition,
+                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
+
+        self.map = map
+
+        wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY,
+                           style=style, pos=pos)
+        self.SetTitle(_("Properties of raster map <%s>") % self.map)
+
+        self.panel = wx.Panel(self, id=wx.ID_ANY)
+
+        bodySizer = wx.BoxSizer(wx.VERTICAL)
+
+        #
+        # surface attributes box
+        #
+        bodySizer.Add(item=self.__surfaceAttributes(), proportion=1,
+                      flag=wx.ALL | wx.EXPAND, border=3)
+
+        #
+        # button (see menuform)
+        #
+        btnsizer = wx.BoxSizer(orient=wx.HORIZONTAL)
+        # cancel
+        btn_cancel = wx.Button(parent=self.panel, id=wx.ID_CANCEL)
+        btn_cancel.SetToolTipString(_("Cancel the command settings and ignore changes"))
+        btnsizer.Add(item=btn_cancel, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=10)
+        btn_cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
+        btn_apply = wx.Button(parent=self.panel, id=wx.ID_APPLY)
+        btn_ok = wx.Button(parent=self.panel, id=wx.ID_OK)
+        btn_ok.SetDefault()
+        
+        btnsizer.Add(item=btn_apply, proportion=0,
+                     flag=wx.ALL | wx.ALIGN_CENTER,
+                     border=10)
+        btnsizer.Add(item=btn_ok, proportion=0,
+                     flag=wx.ALL | wx.ALIGN_CENTER,
+                     border=10)
+        
+        btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)
+        btn_ok.Bind(wx.EVT_BUTTON, self.OnOK)
+
+        bodySizer.Add(item=btnsizer, proportion=0, flag=wx.ALIGN_CENTER)
+
+        self.panel.SetSizer(bodySizer)
+        bodySizer.Fit(self.panel)
+        
+    def __surfaceAttributes(self):
+        """Sourface attributes section"""
+        box = wx.StaticBox (parent=self.panel, id=wx.ID_ANY,
+                            label=" %s " % (_("Surface attributes")))
+        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+
+        gridSizer = wx.GridBagSizer(vgap=3, hgap=3)
+
+        # labels
+        col = 0
+        for type in (_("Attribute"),
+                     _("Use"),
+                     _("Map"),
+                     _("Constant")):
+            gridSizer.Add(item=wx.StaticText(parent=self.panel, id=wx.ID_ANY,
+                                             label=type),
+                          pos=(0, col))
+            col += 1
+
+        # type 
+        row = 1
+        for attr in (_("Topography"),
+                     _("Color"),
+                     _("Mask"),
+                     _("Transparency"),
+                     _("Shininess"),
+                     _("Emission")):
+            gridSizer.Add(item=wx.StaticText(parent=self.panel, id=wx.ID_ANY,
+                                             label=attr + ':'),
+                          pos=(row, 0), flag=wx.ALIGN_CENTER_VERTICAL)
+            use = wx.Choice (parent=self.panel, id=wx.ID_ANY, size=(100, -1),
+                             choices = [_("map"),
+                                        _("constant")])
+            gridSizer.Add(item=use, flag=wx.ALIGN_CENTER_VERTICAL,
+                          pos=(row, 1))
+
+
+            row += 1
+
+        sizer.Add(item=gridSizer, proportion=1,
+                  flag=wx.ALL | wx.EXPAND, border=3)
+
+        return sizer
+
+    def OnCancel(self, event):
+        """Cancel button pressed"""
+        self.Close()
+
+    def OnOK(self, event):
+        """OK button pressed
+        
+        Update map and close dialog
+        """
+        pass
+    
+    def OnApply(self, event):
+        """Apply button pressed
+        
+        Update map (don't close dialog)
+        """
+        pass

Added: grass-addons/visualization/nviz2/wxpython/wxgui_utils.py
===================================================================
--- grass-addons/visualization/nviz2/wxpython/wxgui_utils.py	                        (rev 0)
+++ grass-addons/visualization/nviz2/wxpython/wxgui_utils.py	2008-06-26 14:05:06 UTC (rev 31854)
@@ -0,0 +1,1157 @@
+"""
+MODULE:     wxgui_utils.py
+
+CLASSES:
+    * AbstractLayer
+    * Layer
+    * LayerTree
+
+PURPOSE:    Utility classes for GRASS wxPython GUI. Main functions include tree control
+            for GIS map layer management, command console, and command parsing.
+
+AUTHORS:    The GRASS Development Team
+            Michael Barton (Arizona State University)
+            Jachym Cepicky (Mendel University of Agriculture)
+            Martin Landa <landa.martin gmail.com>
+
+COPYRIGHT:  (C) 2007-2008 by the GRASS Development Team
+            This program is free software under the GNU General Public
+            License (>=v2). Read the file COPYING that comes with GRASS
+            for details.
+"""
+
+import os
+import sys
+import string
+
+import wx
+import wx.lib.customtreectrl as CT
+import wx.combo
+import wx.lib.newevent
+
+import globalvar
+import menuform
+import mapdisp
+import render
+import gcmd
+import grassenv
+import histogram
+import utils
+import profile
+from debug import Debug as Debug
+from icon import Icons as Icons
+from preferences import globalSettings as UserSettings
+try:
+    import subprocess
+except:
+    from compat import subprocess
+
+TREE_ITEM_HEIGHT = 25
+
+class LayerTree(CT.CustomTreeCtrl):
+    """
+    Creates layer tree structure
+    """
+    #	def __init__(self, parent, id, pos, size, style):
+    def __init__(self, parent,
+                 id=wx.ID_ANY, pos=wx.DefaultPosition,
+                 size=wx.DefaultSize, 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_EDIT_LABELS | CT.TR_MULTIPLE,
+                 idx=None, gismgr=None, notebook=None, auimgr=None, showMapDisplay=True):
+        CT.CustomTreeCtrl.__init__(self, parent, id, pos, size, style, ctstyle)
+
+        ### 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.Map = render.Map()    # instance of render.Map to be associated with display
+        self.root = None           # ID of layer tree root node
+        self.groupnode = 0         # index value for layers
+        self.optpage = {}          # dictionary of notebook option pages for each map layer
+        self.layer_selected = None # ID of currently selected layer
+        self.saveitem = {}         # dictionary to preserve layer attributes for drag and drop
+        self.first = True          # indicates if a layer is just added or not
+        self.drag = False          # flag to indicate a drag event is in process
+        self.disp_idx = idx
+        self.gismgr = gismgr
+        self.notebook = notebook   # GIS Manager notebook for layer tree
+        self.treepg = parent       # notebook page holding layer tree
+        self.auimgr = auimgr       # aui manager
+
+        # init associated map display
+        self.mapdisplay = mapdisp.MapFrame(self,
+                                           id=wx.ID_ANY, pos=wx.DefaultPosition,
+                                           size=globalvar.MAP_WINDOW_SIZE,
+                                           style=wx.DEFAULT_FRAME_STYLE,
+                                           tree=self, notebook=self.notebook,
+                                           gismgr=self.gismgr, page=self.treepg,
+                                           Map=self.Map, auimgr=self.auimgr)
+
+        # title
+        self.mapdisplay.SetTitle(_("GRASS GIS Map Display: " +
+                                   str(self.disp_idx + 1) + 
+                                   " - Location: " + grassenv.GetGRASSVariable("LOCATION_NAME")))
+
+        # show new display
+        if showMapDisplay is True:
+            self.mapdisplay.Show()
+            self.mapdisplay.Refresh()
+            self.mapdisplay.Update()
+
+        self.root = self.AddRoot(_("Map Layers"))
+        self.SetPyData(self.root, (None,None))
+
+        #create image list to use with layer tree
+        il = wx.ImageList(16, 16, mask=False)
+
+        trart = wx.ArtProvider.GetBitmap(wx.ART_FOLDER_OPEN, wx.ART_OTHER, (16, 16))
+        self.folder_open = il.Add(trart)
+        trart = wx.ArtProvider.GetBitmap(wx.ART_FOLDER, wx.ART_OTHER, (16, 16))
+        self.folder = il.Add(trart)
+
+        bmpsize = (16, 16)
+        trgif = Icons["addrast"].GetBitmap(bmpsize)
+        self.rast_icon = il.Add(trgif)
+
+        trgif = Icons["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_BEGIN_DRAG,       self.OnBeginDrag)
+        self.Bind(wx.EVT_TREE_END_DRAG,         self.OnEndDrag)
+        self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnLayerContextMenu)
+        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)
+
+    def OnKeyUp(self, event):
+        """Key pressed"""
+        key = event.GetKeyCode()
+        if key == wx.WXK_DELETE and self.gismgr:
+            self.gismgr.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)
+
+        ## pos = event.GetPosition()
+        ## pos = self.ScreenToClient(pos)
+
+        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.popupMenu = wx.Menu()
+        # general item
+        self.popupMenu.Append(self.popupID1, text=_("Remove"))
+        self.Bind(wx.EVT_MENU, self.gismgr.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)
+
+        # map layer items
+        if ltype != "group" and \
+                ltype != "command": # properties
+            self.popupMenu.AppendSeparator()
+            self.popupMenu.Append(self.popupID8, text=_("Change opacity level"), kind=wx.ITEM_CHECK)
+            if self.FindWindowById(self.GetPyData(self.layer_selected)[0]['ctrl']).GetName() == 'spinCtrl':
+                checked = True
+            else:
+                checked = False
+            self.popupMenu.Check(self.popupID8, checked)
+            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)
+            self.popupMenu.Append(self.popupID9, text=_("Zoom to selected map"))
+            self.Bind(wx.EVT_MENU, self.mapdisplay.MapWindow.ZoomToMap, id=self.popupID9)
+            self.popupMenu.Append(self.popupID10, text=_("Set computational region from selected map"))
+            self.Bind(wx.EVT_MENU, self.OnSetCompRegFromMap, id=self.popupID10)
+
+        # 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.gismgr.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
+            digit = self.mapdisplay.toolbars['vdigit']
+            if layer.GetMapset() != grassenv.GetGRASSVariable("MAPSET"):
+                # only vector map in current mapset can be edited
+                self.popupMenu.Enable (self.popupID5, False)
+                self.popupMenu.Enable (self.popupID6, False)
+            elif digit and digit.layerSelectedID != None:
+                # vector map already edited
+                if digit.layers[digit.layerSelectedID] == layer:
+                    self.popupMenu.Enable (self.popupID5, False)
+                    self.popupMenu.Enable(self.popupID6, True)
+                    self.popupMenu.Enable(self.popupID1, False)
+                else:
+                    self.popupMenu.Enable(self.popupID5, False)
+                    self.popupMenu.Enable(self.popupID6, False)
+            self.popupMenu.Append(self.popupID7, _("Metadata"))
+            self.Bind (wx.EVT_MENU, self.OnMetadata, id=self.popupID7)
+
+        #
+        # raster layers (specific items)
+        #
+        elif mltype and mltype == "raster":
+            self.popupMenu.AppendSeparator()
+            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)
+
+        ## 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.gismgr.goutput.RunCmd(cmd)
+
+    def OnSetCompRegFromMap(self, event):
+        """Set computational region from selected map"""
+        mapLayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        mltype = self.GetPyData(self.layer_selected)[0]['type']
+
+        cmd = ['g.region',
+               '-p'] # print by default
+
+        # TODO: other elements
+        if mltype == 'raster':
+            cmd.append('rast=%s' % mapLayer.name)
+        elif mltype == 'vector':
+            cmd.append('vect=%s' % mapLayer.name)
+
+        # print output to command log area
+        self.gismgr.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 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(['d.histogram', 'map=%s' % 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
+        """
+        try:
+            maplayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        except:
+            event.Skip()
+            return
+
+        if not self.mapdisplay.toolbars['vdigit']: # enable tool
+            self.mapdisplay.AddToolbar("digit")
+        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
+        """
+        try:
+            maplayer = self.GetPyData(self.layer_selected)[0]['maplayer']
+        except:
+            event.Skip()
+            return
+
+        self.mapdisplay.toolbars['vdigit'].OnExit()
+        self.mapdisplay.imgVectorMap = None
+
+    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()
+
+        opacity = self.GetPyData(self.layer_selected)[0]['maplayer'].GetOpacity()
+        if type == 'staticText':
+            ctrl = wx.SpinCtrl(self, id=wx.ID_ANY, value="",
+                               style=wx.SP_ARROW_KEYS, initial=100, min=0, max=100,
+                               name='spinCtrl')
+            ctrl.SetValue(opacity)
+            self.Bind(wx.EVT_SPINCTRL, self.OnOpacity, ctrl)
+        else:
+            ctrl = wx.StaticText(self, id=wx.ID_ANY,
+                                 name='staticText')
+            if opacity < 100:
+                ctrl.SetLabel('   (' + str(opacity) + '%)')
+                
+        self.GetPyData(self.layer_selected)[0]['ctrl'] = ctrl.GetId()
+        self.layer_selected.SetWindow(ctrl)
+
+        self.RefreshSelected()
+        self.Refresh()
+
+    def OnNvizProperties(self, event):
+        """Nviz-related properties (raster/vector/volume)
+
+        @todo vector/volume
+        """
+        import nviz
+        dlg = nviz.RasterPropertiesDialog(parent=self,
+                                          map=self.GetPyData(self.layer_selected)[0]['maplayer'].name)
+        dlg.Show()
+
+    def RenameLayer (self, event):
+        """Rename layer"""
+        self.EditLabel(self.layer_selected)
+
+    def AddLayer(self, ltype, lname=None, lchecked=None,
+                 lopacity=None, lcmd=None, lgroup=None):
+        """Add new item to the layer tree, create corresponding MapLayer instance.
+        Launch property dialog if needed (raster, vector, etc.)
+
+        Note: lcmd is given as a list
+        """
+
+        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=(250,25),
+                               style=wx.TE_MULTILINE|wx.TE_WORDWRAP)
+            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:
+            # all other items (raster, vector, ...)
+            if UserSettings.Get(group='manager', key='changeOpacityLevel', subkey='enabled'):
+                ctrl = wx.SpinCtrl(self, id=wx.ID_ANY, value="",
+                                   style=wx.SP_ARROW_KEYS, initial=100, min=0, max=100,
+                                   name='spinCtrl')
+                
+                self.Bind(wx.EVT_SPINCTRL, self.OnOpacity, ctrl)
+            else:
+                ctrl = wx.StaticText(self, id=wx.ID_ANY,
+                                     name='staticText')
+        # 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':
+                if lgroup is False:
+                    # last child of root
+                    layer = self.AppendItem(parentId=self.root,
+                                            text='', ct_type=1, wnd=ctrl)
+                elif lgroup is None or lgroup is True:
+                    # insert item as last child
+                    parent = self.GetItemParent(self.layer_selected)
+                    # layer = self.InsertItem(parentId=parent, input=self.GetPrevSibling(self.layer_selected),
+                    #                        text='', ct_type=1, wnd=ctrl)
+                    layer = self.AppendItem(parentId=parent,
+                                            text='', ct_type=1, wnd=ctrl)
+
+            else: # group (first child of self.layer_selected)
+                layer = self.PrependItem(parent=self.layer_selected,
+                                         text='', ct_type=1, wnd=ctrl)
+                self.Expand(self.layer_selected)
+        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 == '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 lopacity:
+                opacity = lopacity
+                if UserSettings.Get(group='manager', key='changeOpacityLevel', subkey='enabled'):
+                    ctrl.SetValue(int(lopacity * 100))
+                else:
+                    if opacity < 1.0:
+                        ctrl.SetLabel('   (' + str(int(opacity * 100)) + '%)')
+            else:
+                opacity = 1.0
+            if lcmd and len(lcmd) > 1:
+                cmd = lcmd
+                render = False
+                name = utils.GetLayerNameFromCmd(lcmd)
+            else:
+                cmd = []
+                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,
+                                    'prowin' : None}, 
+                                   None))
+
+            maplayer = self.Map.AddLayer(type=ltype, command=self.GetPyData(layer)[0]['cmd'], name=name,
+                                         l_active=checked, l_hidden=False,
+                                         l_opacity=opacity, l_render=render)
+            self.GetPyData(layer)[0]['maplayer'] = maplayer
+
+            # run properties dialog if no properties given
+            if len(cmd) == 0:
+                self.PropertiesDialog(layer, show=True)
+
+        else: # group
+            self.SetPyData(layer, ({'cmd': None,
+                                    'type' : ltype,
+                                    'ctrl' : None,
+                                    'maplayer' : None,
+                                    'prowin' : None}, 
+                                   None))
+
+        # use predefined layer name if given
+        if lname:
+            if ltype != 'command':
+                self.SetItemText(layer, lname)
+            else:
+                ctrl.SetValue(lname)
+
+        # updated progress bar range (mapwindow statusbar)
+        if checked is True:
+            self.mapdisplay.onRenderGauge.SetRange(len(self.Map.GetListOfLayers(l_active=True)))
+
+        # layer.SetHeight(TREE_ITEM_HEIGHT)
+
+        return layer
+
+    def PropertiesDialog (self, layer, show=True):
+        """Launch the properties dialog"""
+        if self.GetPyData(layer)[0].has_key('propwin') and \
+                self.GetPyData(layer)[0]['propwin'] is not None:
+            # recycle GUI dialogs
+            if self.GetPyData(layer)[0]['propwin'].IsShown():
+                self.GetPyData(layer)[0]['propwin'].SetFocus()
+            else:
+                self.GetPyData(layer)[0]['propwin'].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='rasterOverlay', subkey='enabled'):
+                cmd.append('-o')
+            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':
+            menuform.GUI().ParseCommand(['d.vect'], 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):
+        """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
+        if self.mapdisplay.autoRender.GetValue(): 
+            self.mapdisplay.OnRender(None)
+
+        if self.mapdisplay.toolbars['vdigit']:
+            self.mapdisplay.toolbars['vdigit'].UpdateListOfLayers (updateTool=True)
+
+        # update progress bar range (mapwindow statusbar)
+        self.mapdisplay.onRenderGauge.SetRange(len(self.Map.GetListOfLayers(l_active=True)))
+
+        event.Skip()
+
+    def OnLayerChecked(self, event):
+        """Enable/disable given layer item"""
+        item    = event.GetItem()
+        checked = item.IsChecked()
+        
+        if self.drag == False and 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)
+                    self.Map.ChangeLayerActive(self.GetPyData(child)[0]['maplayer'], checked)
+                    child = self.GetNextSibling(child)
+            else:
+                self.Map.ChangeLayerActive(self.GetPyData(item)[0]['maplayer'], checked)
+
+        # update progress bar range (mapwindow statusbar)
+        self.mapdisplay.onRenderGauge.SetRange(len(self.Map.GetListOfLayers(l_active=True)))
+
+        # redraw map if auto-rendering is enabled
+        if self.mapdisplay.autoRender.GetValue(): 
+            self.mapdisplay.OnRender(None)
+
+    def OnCmdChanged(self, event):
+        """Change command string"""
+        ctrl = event.GetEventObject().GetId()
+        cmd = event.GetString()
+        layer = None
+
+        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
+        if layer and self.drag == False:
+            self.ChangeLayer(layer)
+            self.GetPyData(layer)[0]['cmd'] = cmd.split(' ')
+            maplayer = self.GetPyData(layer)[0]['maplayer']
+            for option in maplayer.GetCmd():
+                if 'map=' in option:
+                    mapname = option.split('=')[1]
+                    self.Map.ChangeLayerName(maplayer, mapname)
+
+        event.Skip()
+
+    def OnOpacity(self, event):
+        """
+        Set opacity level for map layer
+        """
+        Debug.msg (3, "LayerTree.OnOpacity(): %s" % event.GetInt())
+
+        ctrl = event.GetEventObject().GetId()
+        maplayer = None
+
+        vislayer = self.GetFirstVisibleItem()
+
+        layer = None
+        for item in range(0, self.GetCount()):
+            if self.GetPyData(vislayer)[0]['ctrl'] == ctrl:
+                layer = vislayer
+
+            if not self.GetNextVisible(vislayer):
+                break
+            else:
+                vislayer = self.GetNextVisible(vislayer)
+
+        if layer:
+            maplayer = self.GetPyData(layer)[0]['maplayer']
+
+        opacity = event.GetInt() / 100.
+        # change opacity parameter for item in layers list in render.Map
+        if maplayer and self.drag == False:
+            self.Map.ChangeOpacity(maplayer, opacity)
+
+        # redraw map if auto-rendering is enabled
+        if self.mapdisplay.autoRender.GetValue(): 
+            self.mapdisplay.OnRender(None)
+
+    def OnChangeSel(self, event):
+        """Selection changed"""
+        oldlayer = event.GetOldItem()
+        layer = event.GetItem()
+        self.layer_selected = layer
+        try:
+            self.RefreshLine(oldlayer)
+            self.RefreshLine(layer)
+        except:
+            pass
+
+    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 OnBeginDrag(self, event):
+        """
+        Drag and drop of tree nodes
+        """
+
+        item  = event.GetItem()
+        Debug.msg (3, "LayerTree.OnBeginDrag(): layer=%s" % \
+                   (self.GetItemText(item)))
+
+        event.Allow()
+        self.drag = True
+        self.DoSelectItem(item, unselect_others=True)
+
+        # save everthing associated with item to drag
+        self.dragItem = item
+
+    def RecreateItem (self, event, oldItem, parent=None):
+        """
+        Recreate item (needed for OnEndDrag())
+        """
+        Debug.msg (4, "LayerTree.RecreateItem(): layer=%s" % \
+                   self.GetItemText(oldItem))
+
+        # fetch data (olditem)
+        text    = self.GetItemText(oldItem)
+        image   = self.GetItemImage(oldItem, 0)
+        if self.GetPyData(oldItem)[0]['ctrl']:
+            oldctrl = self.FindWindowById(self.GetPyData(oldItem)[0]['ctrl'])
+        else:
+            oldctrl = None
+        checked = self.IsItemChecked(oldItem)
+        
+        # recreate spin/text control for layer
+        if self.GetPyData(oldItem)[0]['type'] == 'command':
+            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(oldItem)[0]['maplayer'].GetCmd(string=True))
+            except:
+                pass
+            newctrl.Bind(wx.EVT_TEXT_ENTER, self.OnCmdChanged)
+            newctrl.Bind(wx.EVT_TEXT,       self.OnCmdChanged)
+        elif self.GetPyData(oldItem)[0]['type'] == 'group' or oldctrl is None:
+            newctrl = None
+        else:
+            opacity = self.GetPyData(oldItem)[0]['maplayer'].GetOpacity()
+            if oldctrl.GetName() == 'staticText':
+                newctrl = wx.StaticText(self, id=wx.ID_ANY,
+                                        name='staticText')
+                if opacity < 100:
+                    newctrl.SetLabel('   (' + str(opacity) + '%)')
+            else:
+                newctrl = wx.SpinCtrl(self, id=wx.ID_ANY, value="",
+                                      style=wx.SP_ARROW_KEYS, min=0, max=100,
+                                      name='spinCtrl')
+                newctrl.SetValue(opacity)
+                self.Bind(wx.EVT_SPINCTRL, self.OnOpacity, newctrl)
+
+        # decide where to put new layer and put it there
+        if not parent:
+            flag = self.HitTest(event.GetPoint())[1]
+        else:
+            flag = 0
+
+        if self.GetPyData(oldItem)[0]['type'] == 'group':
+            windval = None
+            data    = None
+        else:
+            windval = self.GetPyData(self.layer_selected)[0]['maplayer'].GetOpacity()
+            data    = self.GetPyData(oldItem)
+
+        # create GenericTreeItem instance
+        if flag & wx.TREE_HITTEST_ABOVE:
+            newItem = self.PrependItem(self.root, text=text, \
+                                   ct_type=1, wnd=newctrl, image=image, \
+                                   data=data)
+        elif (flag &  wx.TREE_HITTEST_BELOW) or (flag & wx.TREE_HITTEST_NOWHERE) \
+                 or (flag & wx.TREE_HITTEST_TOLEFT) or (flag & wx.TREE_HITTEST_TORIGHT):
+            newItem = self.AppendItem(self.root, text=text, \
+                                  ct_type=1, wnd=newctrl, image=image, \
+                                  data=data)
+        else:
+            if parent:
+                afteritem = parent
+            else:
+                afteritem = event.GetItem()
+
+            if  self.GetPyData(afteritem)[0]['type'] == 'group':
+                parent = afteritem
+                newItem = self.AppendItem(parent, text=text, \
+                                      ct_type=1, wnd=newctrl, image=image, \
+                                      data=data)
+                self.Expand(afteritem)
+            else:
+                parent = self.GetItemParent(afteritem)
+                newItem = self.InsertItem(parent, afteritem, text=text, \
+                                      ct_type=1, wnd=newctrl, image=image, \
+                                      data=data)
+
+        # add layer at new position
+        self.SetPyData(newItem, self.GetPyData(oldItem))
+        if newctrl:
+            self.GetPyData(newItem)[0]['ctrl'] = newctrl.GetId()
+        else:
+            self.GetPyData(newItem)[0]['ctrl'] = None
+            
+        self.CheckItem(newItem, checked=checked)
+
+        # newItem.SetHeight(TREE_ITEM_HEIGHT)
+
+        return newItem
+
+    def OnEndDrag(self, event):
+        """
+        Insert copy of layer in new
+        position and delete original at old position
+        """
+
+        self.drag = True
+        try:
+            old = self.dragItem  # make sure this member exists
+        except:
+            return
+
+        Debug.msg (4, "LayerTree.OnEndDrag(): layer=%s" % \
+                   (self.GetItemText(self.dragItem)))
+
+        newItem  = self.RecreateItem (event, self.dragItem)
+
+        if  self.GetPyData(newItem)[0]['type'] == 'group':
+            (child, cookie) = self.GetFirstChild(self.dragItem)
+            if child:
+                while child:
+                    self.RecreateItem(event, child, 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()
+
+        # select new item
+        self.SelectItem(newItem)
+
+        # completed drag and drop
+        self.drag = False
+
+    def GetOptData(self, dcmd, layer, params, propwin):
+        """Process layer data"""
+
+        # set layer text to map name
+        if dcmd:
+            mapname = utils.GetLayerNameFromCmd(dcmd)
+            self.SetItemText(layer, mapname)
+
+        # 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
+        
+        # check layer as active
+        # self.CheckItem(layer, checked=True)
+
+        # change parameters for item in layers list in render.Map
+        self.ChangeLayer(layer)
+
+    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:
+            return
+
+        itemList = ""
+
+        for item in range(0, 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.ReoderLayers(): items=%s" % \
+                   (itemList))
+
+        # reorder map layers
+        treelayers.reverse()
+        self.Map.ReorderLayers(treelayers)
+
+    def ChangeLayer(self, item):
+        """Change layer"""
+
+        type = self.GetPyData(item)[0]['type']
+
+        if type == 'command':
+            win = self.FindWindowById(self.GetPyData(item)[0]['ctrl'])
+            if win.GetValue() != None:
+                cmdlist = win.GetValue().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)
+        maplayer = self.Map.ChangeLayer(layer=self.GetPyData(item)[0]['maplayer'], type=type,
+                                        command=cmdlist, name=self.GetItemText(item),
+                                        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
+        if self.mapdisplay.autoRender.GetValue(): 
+            self.mapdisplay.OnRender(None)
+
+        # item.SetHeight(TREE_ITEM_HEIGHT)
+
+    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 __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