[GRASS-SVN] r49357 - in grass/branches/develbranch_6: gui/icons/grass2 gui/wxpython gui/wxpython/docs gui/wxpython/gui_modules gui/wxpython/icons include lib/nviz lib/ogsf misc/m.nviz.image

svn_grass at osgeo.org svn_grass at osgeo.org
Fri Nov 25 10:10:47 EST 2011


Author: annakrat
Date: 2011-11-25 07:10:47 -0800 (Fri, 25 Nov 2011)
New Revision: 49357

Added:
   grass/branches/develbranch_6/gui/icons/grass2/3d-help.png
   grass/branches/develbranch_6/gui/icons/grass2/3d-rotate.png
   grass/branches/develbranch_6/gui/icons/grass2/3d-settings.png
   grass/branches/develbranch_6/gui/wxpython/docs/wxGUI_nviz_tools_light.jpg
   grass/branches/develbranch_6/gui/wxpython/gui_modules/nviz_animation.py
   grass/branches/develbranch_6/misc/m.nviz.image/cplane.c
Modified:
   grass/branches/develbranch_6/gui/wxpython/docs/wxGUI.Nviz.html
   grass/branches/develbranch_6/gui/wxpython/docs/wxGUI_nviz_toolbar.jpg
   grass/branches/develbranch_6/gui/wxpython/docs/wxGUI_nviz_tools_surface.jpg
   grass/branches/develbranch_6/gui/wxpython/docs/wxGUI_nviz_tools_vector.jpg
   grass/branches/develbranch_6/gui/wxpython/docs/wxGUI_nviz_tools_view.jpg
   grass/branches/develbranch_6/gui/wxpython/docs/wxGUI_nviz_tools_volume.jpg
   grass/branches/develbranch_6/gui/wxpython/gui_modules/colorrules.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/gdialogs.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/gselect.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/layertree.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/mapdisp.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/mapdisp_window.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/menuform.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/nviz.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/nviz_mapdisp.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/nviz_preferences.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/nviz_tools.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/preferences.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/render.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/toolbars.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/workspace.py
   grass/branches/develbranch_6/gui/wxpython/gui_modules/wxnviz.py
   grass/branches/develbranch_6/gui/wxpython/icons/icon.py
   grass/branches/develbranch_6/gui/wxpython/wxgui.py
   grass/branches/develbranch_6/include/gstypes.h
   grass/branches/develbranch_6/include/nviz.h
   grass/branches/develbranch_6/include/ogsf_proto.h
   grass/branches/develbranch_6/lib/nviz/change_view.c
   grass/branches/develbranch_6/lib/nviz/cplanes_obj.c
   grass/branches/develbranch_6/lib/nviz/draw.c
   grass/branches/develbranch_6/lib/nviz/lights.c
   grass/branches/develbranch_6/lib/nviz/nviz.c
   grass/branches/develbranch_6/lib/nviz/position.c
   grass/branches/develbranch_6/lib/ogsf/GS2.c
   grass/branches/develbranch_6/lib/ogsf/Gp3.c
   grass/branches/develbranch_6/lib/ogsf/gs.c
   grass/branches/develbranch_6/lib/ogsf/gsd_objs.c
   grass/branches/develbranch_6/lib/ogsf/gsd_views.c
   grass/branches/develbranch_6/misc/m.nviz.image/args.c
   grass/branches/develbranch_6/misc/m.nviz.image/description.html
   grass/branches/develbranch_6/misc/m.nviz.image/local_proto.h
   grass/branches/develbranch_6/misc/m.nviz.image/main.c
   grass/branches/develbranch_6/misc/m.nviz.image/surface.c
   grass/branches/develbranch_6/misc/m.nviz.image/vector.c
   grass/branches/develbranch_6/misc/m.nviz.image/volume.c
Log:
wxNviz backported from trunk

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


Property changes on: grass/branches/develbranch_6/gui/icons/grass2/3d-help.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

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


Property changes on: grass/branches/develbranch_6/gui/icons/grass2/3d-rotate.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

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


Property changes on: grass/branches/develbranch_6/gui/icons/grass2/3d-settings.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

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

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

Copied: grass/branches/develbranch_6/gui/wxpython/docs/wxGUI_nviz_tools_light.jpg (from rev 47650, grass/trunk/gui/wxpython/docs/wxGUI_nviz_tools_light.jpg)
===================================================================
(Binary files differ)

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

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

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

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

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/colorrules.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/colorrules.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/colorrules.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -9,7 +9,6 @@
  - ColorTable
  - RasterColorTable
  - VectorColorTable
- - ThematicVectorTable
  - BuferedWindow
 
 (C) 2008, 2010-2011 by the GRASS Development Team
@@ -316,9 +315,6 @@
                  **kwargs):
         """!Dialog for interactively entering rules for map management
         commands
-
-        @param raster True to raster otherwise vector
-        @param nviz True if ColorTable is called from nviz thematic mapping
         """
         self.parent = parent # GMFrame
         wx.Frame.__init__(self, parent, id, title, style = style, **kwargs)
@@ -1622,55 +1618,6 @@
         
         return ColorTable.OnApply(self, event)
         
-class ThematicVectorTable(VectorColorTable):
-    def __init__(self, parent, vectorType, **kwargs):
-        """!Dialog for interactively entering color/size rules
-            for vector maps for thematic mapping in nviz"""
-        self.vectorType = vectorType
-        VectorColorTable.__init__(self, parent = parent, **kwargs)
-              
-        self.SetTitle(_("Thematic mapping for vector map in 3D view"))       
-                    
-    def _initLayer(self):
-        """!Set initial layer when opening dialog"""
-        self.inmap = self.parent.GetLayerData(nvizType = 'vector', nameOnly = True)
-        self.selectionInput.SetValue(self.inmap)
-        self.selectionInput.Disable()
-        
-    def OnApply(self, event):
-        """!Apply selected color table
-
-        @return True on success otherwise False
-        """
-        ret = self.CreateColorTable()
-        if not ret:
-            gcmd.GMessage(parent = self, message = _("No valid color rules given."))
-        
-        data = self.parent.GetLayerData(nvizType = 'vector')
-        data['vector']['points']['thematic']['layer'] = int(self.properties['layer'])
-        
-        value = None
-        if self.properties['storeColumn']:
-            value = self.properties['storeColumn']
-            
-        if not self.colorTable:
-            if self.attributeType == 'color':
-                data['vector'][self.vectorType]['thematic']['rgbcolumn'] = value
-            else:
-                data['vector'][self.vectorType]['thematic']['sizecolumn'] = value
-        else:
-            if self.attributeType == 'color':
-                data['vector'][self.vectorType]['thematic']['rgbcolumn'] = None
-            else:
-                data['vector'][self.vectorType]['thematic']['sizecolumn'] = None
-        
-        data['vector'][self.vectorType]['thematic']['update'] = None
-        
-        event = wxUpdateProperties(data = data)
-        wx.PostEvent(self.parent.mapWindow, event)
-        self.parent.mapWindow.Refresh(False)
-        
-        return ret
            
 class BufferedWindow(wx.Window):
     """!A Buffered window class"""

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/gdialogs.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/gdialogs.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/gdialogs.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -528,7 +528,7 @@
             resize.SetToolTipString(_("Click and drag on the map display to set legend"
                                         " size and position and then press OK"))
             resize.SetName('resize')
-            if self.parent.toolbars['nviz']:
+            if self.parent.IsPaneShown('3d'):
                 resize.Disable()
             box.Add(item = resize, proportion = 0, flag = wx.ALIGN_CENTRE|wx.ALL, border = 5)
             sizer.Add(item = box, proportion = 0,
@@ -579,7 +579,7 @@
         sizer.Fit(self)
 
         # create overlay if doesn't exist
-        self._CreateOverlay()
+        self._createOverlay()
         
         if len(self.parent.MapWindow.overlays[self.ovlId]['cmd']) > 1:
             if name == 'legend':
@@ -592,12 +592,12 @@
                                       mapName)
             
         
-    def _CreateOverlay(self):
+    def _createOverlay(self):
+        """!Creates overlay"""
         if not self.parent.Map.GetOverlay(self.ovlId):
             self.newOverlay = self.parent.Map.AddOverlay(id = self.ovlId, type = self.name,
                                                          command = self.cmd,
                                                          l_active = False, l_render = False, l_hidden = True)
-            
             prop = { 'layer' : self.newOverlay,
                      'params' : None,
                      'propwin' : None,
@@ -607,6 +607,7 @@
             self.parent.MapWindow2D.overlays[self.ovlId] = prop
             if self.parent.MapWindow3D:
                 self.parent.MapWindow3D.overlays[self.ovlId] = prop
+                
         else:
             if self.parent.MapWindow.overlays[self.ovlId]['propwin'] == None:
                 return
@@ -615,10 +616,7 @@
 
 
     def OnOptions(self, event):
-        """        self.SetSizer(sizer)
-        sizer.Fit(self)
-
-        Sets option for decoration map overlays
+        """!Sets option for decoration map overlays
         """
         if self.parent.MapWindow.overlays[self.ovlId]['propwin'] is None:
             # build properties dialog
@@ -660,6 +658,9 @@
         self.parent.Map.GetOverlay(self.ovlId).SetActive(self.chkbox.IsChecked())
 
         # update map
+        if self.parent.IsPaneShown('3d'):
+            self.parent.MapWindow.UpdateOverlays()
+            
         self.parent.MapWindow.UpdateMap()
 
         # close dialog
@@ -710,12 +711,14 @@
             self.currClr  = self.parent.MapWindow.textdict[self.ovlId]['color']
             self.currRot  = self.parent.MapWindow.textdict[self.ovlId]['rotation']
             self.currCoords = self.parent.MapWindow.textdict[self.ovlId]['coords']
+            self.currBB = self.parent.MapWindow.textdict[self.ovlId]['bbox']
         else:
             self.currClr = wx.BLACK
             self.currText = ''
             self.currFont = self.GetFont()
             self.currRot = 0.0
             self.currCoords = [10, 10, 10, 10]
+            self.currBB = wx.Rect()
 
         self.sizer = wx.BoxSizer(wx.VERTICAL)
         box = wx.GridBagSizer(vgap = 5, hgap = 5)

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/gselect.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/gselect.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/gselect.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -279,6 +279,7 @@
                        'raster files':'rast',
                        'grid3':'rast3d',
                        'rast3d':'rast3d',
+                       '3d-raster':'rast3d',
                        'raster3D':'rast3d',
                        'raster3D files':'rast3d',
                        'vector':'vect',

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/layertree.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/layertree.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/layertree.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -233,7 +233,9 @@
             else:
                 vector = False
             if self.mapdisplay.statusbarWin['render'].GetValue():
-                self.mapdisplay.MapWindow.UpdateMap(render = True, renderVector = vector)
+                self.mapdisplay.MapWindow2D.UpdateMap(render = True, renderVector = vector)
+                if self.lmgr.IsPaneShown('toolbarNviz'): # nviz
+                    self.mapdisplay.MapWindow3D.UpdateMap(render = True)
             
             self.rerender = False
         
@@ -294,7 +296,7 @@
                 self.popupMenu.Enable(self.popupID['opacity'], False)
                 self.popupMenu.Enable(self.popupID['properties'], False)
             
-            if ltype in ('raster', 'vector', '3d-raster') and self.mapdisplay.toolbars['nviz']:
+            if ltype in ('raster', 'vector', '3d-raster') and self.lmgr.IsPaneShown('toolbarNviz'):
                 self.popupMenu.Append(self.popupID['nviz'], _("3D view properties"))
                 self.Bind (wx.EVT_MENU, self.OnNvizProperties, id = self.popupID['nviz'])
             
@@ -876,7 +878,7 @@
         # updated progress bar range (mapwindow statusbar)
         if checked is True:
             self.mapdisplay.statusbarWin['progress'].SetRange(len(self.Map.GetListOfLayers(l_active = True)))
-        
+            
         return layer
 
     def PropertiesDialog (self, layer, show = True):
@@ -1033,6 +1035,22 @@
         # update progress bar range (mapwindow statusbar)
         self.mapdisplay.statusbarWin['progress'].SetRange(len(self.Map.GetListOfLayers(l_active = True)))
 
+        #
+        # nviz
+        #
+        if self.lmgr.IsPaneShown('toolbarNviz') and \
+                self.GetPyData(item) is not None:
+            # nviz - load/unload data layer
+            mapLayer = self.GetPyData(item)[0]['maplayer']
+            self.mapdisplay.SetStatusText(_("Please wait, updating data..."), 0)
+            if mapLayer.type == 'raster':
+                self.mapdisplay.MapWindow.UnloadRaster(item)
+            elif mapLayer.type == '3d-raster':
+                self.mapdisplay.MapWindow.UnloadRaster3d(item)
+            elif mapLayer.type == 'vector':
+                self.mapdisplay.MapWindow.UnloadVector(item)
+            self.mapdisplay.SetStatusText("", 0)
+            
         event.Skip()
 
     def OnLayerChecked(self, event):
@@ -1068,7 +1086,7 @@
         self.mapdisplay.statusbarWin['progress'].SetRange(len(self.Map.GetListOfLayers(l_active = True)))
         
         # nviz
-        if self.mapdisplay.toolbars['nviz'] and \
+        if self.lmgr.IsPaneShown('toolbarNviz') and \
                 self.GetPyData(item) is not None:
             # nviz - load/unload data layer
             mapLayer = self.GetPyData(item)[0]['maplayer']
@@ -1179,7 +1197,7 @@
                                                     render = render)
         
         # update nviz tools
-        if self.mapdisplay.toolbars['nviz'] and \
+        if self.lmgr.IsPaneShown('toolbarNviz') and \
                 self.GetPyData(self.layer_selected) is not None:
             if self.layer_selected.IsChecked():
                 # update Nviz tool window
@@ -1390,7 +1408,7 @@
                                                     render = render)
 
         # update nviz session        
-        if self.mapdisplay.toolbars['nviz'] and dcmd:
+        if self.lmgr.IsPaneShown('toolbarNviz') and dcmd:
             mapLayer = self.GetPyData(layer)[0]['maplayer']
             mapWin = self.mapdisplay.MapWindow
             if len(mapLayer.GetCmd()) > 0:

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/mapdisp.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/mapdisp.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/mapdisp.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -227,7 +227,7 @@
         self.statusbarWin['projection'].SetToolTip(wx.ToolTip (_("Reproject coordinates displayed "
                                                                  "in the statusbar. Projection can be "
                                                                  "defined in GUI preferences dialog "
-                                                                 "(tab 'Display')")))
+                                                                 "(tab 'Projection')")))
         self.statusbarWin['projection'].Hide()
         
         # mask
@@ -271,7 +271,7 @@
         # Update fancy gui style
         #
         self._mgr.AddPane(self.MapWindow, wx.aui.AuiPaneInfo().CentrePane().
-                          Dockable(False).BestSize((-1,-1)).
+                          Dockable(False).BestSize((-1,-1)).Name('2d').
                           CloseButton(False).DestroyOnClose(True).
                           Layer(0))
         self._mgr.Update()
@@ -336,21 +336,23 @@
                                                 Map = self.Map, tree = self.tree,
                                                 lmgr = self._layerManager)
             self.MapWindowVDigit.Show()
+            self._mgr.AddPane(self.MapWindowVDigit, wx.aui.AuiPaneInfo().CentrePane().
+                          Dockable(False).BestSize((-1,-1)).Name('vdigit').
+                          CloseButton(False).DestroyOnClose(True).
+                          Layer(0))
         
         self.MapWindow = self.MapWindowVDigit
         
-        self._mgr.DetachPane(self.MapWindow2D)
-        self.MapWindow2D.Hide()
-        
+        if self._mgr.GetPane('2d').IsShown():
+            self._mgr.GetPane('2d').Hide()
+        elif self._mgr.GetPane('3d').IsShown():
+            self._mgr.GetPane('3d').Hide()
+        self._mgr.GetPane('vdigit').Show()
         self.toolbars['vdigit'] = toolbars.VDigitToolbar(parent = self, mapcontent = self.Map,
                                                          layerTree = self.tree,
                                                          log = log)
         self.MapWindowVDigit.SetToolbar(self.toolbars['vdigit'])
         
-        self._mgr.AddPane(self.MapWindowVDigit, wx.aui.AuiPaneInfo().CentrePane().
-                          Dockable(False).BestSize((-1,-1)).
-                          CloseButton(False).DestroyOnClose(True).
-                          Layer(0))
         self._mgr.AddPane(self.toolbars['vdigit'],
                           wx.aui.AuiPaneInfo().
                           Name("vdigittoolbar").Caption(_("Vector Digitizer Toolbar")).
@@ -365,72 +367,117 @@
         self.MapWindow.pen          = wx.Pen(colour = 'red',   width = 2, style = wx.SOLID)
         self.MapWindow.polypen      = wx.Pen(colour = 'green', width = 2, style = wx.SOLID)
 
-    def _addToolbarNviz(self):
-        """!Add 3D view mode toolbar
+    def AddNviz(self):
+        """!Add 3D view mode window
         """
         import nviz
         
         # check for GLCanvas and OpenGL
         if not nviz.haveNviz:
-            self.toolbars['map'].combo.SetValue (_("2D view"))
+            self.toolbars['map'].combo.SetValue(_("2D view"))
             gcmd.GError(parent = self,
                         message = _("Unable to switch to 3D display mode.\nThe Nviz python extension "
                                     "was not found or loaded properly.\n"
                                     "Switching back to 2D display mode.\n\nDetails: %s" % nviz.errorMsg))
             return
         
-        # add Nviz toolbar and disable 2D display mode tools
-        self.toolbars['nviz'] = toolbars.NvizToolbar(self, self.Map)
+        # disable 3D mode for other displays
+        for page in range(0, self._layerManager.gm_cb.GetPageCount()):
+            if self._layerManager.gm_cb.GetPage(page) != self._layerManager.curr_page:
+                if '3D' in self._layerManager.gm_cb.GetPage(page).maptree.mapdisplay.toolbars['map'].combo.GetString(1):
+                    self._layerManager.gm_cb.GetPage(page).maptree.mapdisplay.toolbars['map'].combo.Delete(1)
         self.toolbars['map'].Enable2D(False)
-        
+        # add rotate tool to map toolbar
+        self.toolbars['map'].InsertTool((('rotate', Icons['nviz']['rotate'],
+                                          self.OnRotate, wx.ITEM_CHECK, 7),)) # 7 is position
+        self.toolbars['map'].InsertTool((('flyThrough', Icons['nviz']['flyThrough'],
+                                          self.OnFlyThrough, wx.ITEM_CHECK, 8),)) 
+        self.toolbars['map'].ChangeToolsDesc(mode2d = False)
         # update status bar
-        self.statusbarWin['toggle'].Enable(False)
+        choice = globalvar.MAP_DISPLAY_STATUSBAR_MODE
+        self.statusbarWin['toggle'].SetItems((choice[0], choice[1], choice[2],
+                                              choice[8], choice[9]))
+        self.statusbarWin['toggle'].SetSelection(0)
         
         # erase map window
         self.MapWindow.EraseMap()
         
-        self._layerManager.goutput.WriteCmdLog(_("Starting 3D view mode..."))
+        self._layerManager.goutput.WriteCmdLog(_("Starting 3D view mode..."),
+                                               switchPage = False)
         self.statusbar.SetStatusText(_("Please wait, loading data..."), 0)
         
-        # create GL window & NVIZ toolbar
+        # create GL window
         if not self.MapWindow3D:
             self.MapWindow3D = nviz.GLWindow(self, id = wx.ID_ANY,
                                              Map = self.Map, tree = self.tree, lmgr = self._layerManager)
             self.MapWindow = self.MapWindow3D
             self.MapWindow.SetCursor(self.cursors["default"])
             
-                # add Nviz notebookpage
-            self._layerManager.AddNviz()
+            # add Nviz notebookpage
+            self._layerManager.AddNvizTools()
             
+            # switch from MapWindow to MapWindowGL
+            self._mgr.GetPane('2d').Hide()
+            self._mgr.AddPane(self.MapWindow3D, wx.aui.AuiPaneInfo().CentrePane().
+                              Dockable(False).BestSize((-1,-1)).Name('3d').
+                              CloseButton(False).DestroyOnClose(True).
+                              Layer(0))
+            
             self.MapWindow3D.OnPaint(None) # -> LoadData
             self.MapWindow3D.Show()
+            self.MapWindow3D.ResetViewHistory()            
             self.MapWindow3D.UpdateView(None)
         else:
             self.MapWindow = self.MapWindow3D
+            os.environ['GRASS_REGION'] = self.Map.SetRegion(windres = True)
+            self.MapWindow3D.GetDisplay().Init()
+            del os.environ['GRASS_REGION']
+            
+            # switch from MapWindow to MapWindowGL
+            self._mgr.GetPane('2d').Hide()
+            self._mgr.GetPane('3d').Show()
+            
             # add Nviz notebookpage
-            self._layerManager.AddNviz()
-            self._layerManager.nviz.UpdatePage('view')
-            self._layerManager.nviz.UpdatePage('light')
+            self._layerManager.AddNvizTools()
+            self.MapWindow3D.ResetViewHistory()
+            for page in ('view', 'light', 'fringe', 'constant', 'cplane', 'animation'):
+                self._layerManager.nviz.UpdatePage(page)
+                
+        self.MapWindow3D.overlays = self.MapWindow2D.overlays
+        self.MapWindow3D.textdict = self.MapWindow2D.textdict
+        # update overlays needs to be called after because getClientSize
+        # is called during update and it must give reasonable values
+        wx.CallAfter(self.MapWindow3D.UpdateOverlays)
         
-        # 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'],
-                          wx.aui.AuiPaneInfo().
-                          Name("nviztoolbar").Caption(_("3D View Toolbar")).
-                          ToolbarPane().Top().Row(1).
-                          LeftDockable(False).RightDockable(False).
-                          BottomDockable(False).TopDockable(True).
-                          CloseButton(False).Layer(2).
-                          BestSize((self.toolbars['nviz'].GetBestSize())))
-        
         self.SetStatusText("", 0)
+        self._mgr.Update()
+    
+    def RemoveNviz(self):
+        """!Restore 2D view"""
+        self.toolbars['map'].RemoveTool(self.toolbars['map'].rotate)
+        self.toolbars['map'].RemoveTool(self.toolbars['map'].flyThrough)
+        # update status bar
+        self.statusbarWin['toggle'].SetItems(globalvar.MAP_DISPLAY_STATUSBAR_MODE)
+        self.statusbarWin['toggle'].SetSelection(UserSettings.Get(group = 'display',
+                                                                  key = 'statusbarMode',
+                                                                  subkey = 'selection'))
+        self.statusbar.SetStatusText(_("Please wait, unloading data..."), 0)
+        self._layerManager.goutput.WriteCmdLog(_("Switching back to 2D view mode..."),
+                                               switchPage = False)
+        self.MapWindow3D.OnClose(event = None)
+        # switch from MapWindowGL to MapWindow
+        self._mgr.GetPane('2d').Show()
+        self._mgr.GetPane('3d').Hide()
         
+        self.MapWindow = self.MapWindow2D
+        # remove nviz notebook page
+        self._layerManager.RemoveNvizTools()
+        
+        self.MapWindow2D.overlays = self.MapWindow3D.overlays
+        self.MapWindow2D.textdict = self.MapWindow3D.textdict
+        self.MapWindow.UpdateMap()
+        self._mgr.Update()
+        
     def AddToolbar(self, name):
         """!Add defined toolbar to the window
         
@@ -439,7 +486,6 @@
          - 'vdigit'  - vector digitizer
          - 'gcpdisp' - GCP Manager Display
          - 'georect' - georectifier
-         - 'nviz'    - 3D view mode
         """
         # default toolbar
         if name == "map":
@@ -448,7 +494,7 @@
             self._mgr.AddPane(self.toolbars['map'],
                               wx.aui.AuiPaneInfo().
                               Name("maptoolbar").Caption(_("Map Toolbar")).
-                              ToolbarPane().Top().
+                              ToolbarPane().Top().Name('mapToolbar').
                               LeftDockable(False).RightDockable(False).
                               BottomDockable(False).TopDockable(True).
                               CloseButton(False).Layer(2).
@@ -469,10 +515,6 @@
                               BottomDockable(False).TopDockable(True).
                               CloseButton(False).Layer(2).
                               BestSize((self.toolbars['georect'].GetBestSize())))
-        # nviz
-        elif name == "nviz":
-            self._addToolbarNviz()
-        
         self._mgr.Update()
         
     def RemoveToolbar (self, name):
@@ -489,38 +531,22 @@
         self.toolbars[name] = None
         
         if name == 'vdigit':
-            self._mgr.DetachPane(self.MapWindowVDigit)
-            self.MapWindowVDigit.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._mgr.GetPane('vdigit').Hide()
+            self._mgr.GetPane('2d').Show()
             self.MapWindow = self.MapWindow2D
-        
-        elif name == 'nviz':
-            # 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
-            # remove nviz notebook page
-            self._layerManager.RemoveNviz()
             
-            self.MapWindow.UpdateMap()
-        
         self.toolbars['map'].combo.SetValue(_("2D view"))
         self.toolbars['map'].Enable2D(True)
         self.statusbarWin['toggle'].Enable(True)
         
         self._mgr.Update()
-
+    
+    def IsPaneShown(self, name):
+        """!Check if pane (toolbar, mapWindow ...) of given name is currently shown"""
+        if self._mgr.GetPane(name).IsOk():
+            return self._mgr.GetPane(name).IsShown()
+        return False
+    
     def _initDisplay(self):
         """!Initialize map display, set dimensions and map region
         """
@@ -543,8 +569,7 @@
         event.Skip()
         
     def OnFocus(self, event):
-        """
-        Change choicebook page to match display.
+        """!Change choicebook page to match display.
         Or set display for georectifying
         """
         if self._layerManager and \
@@ -558,6 +583,8 @@
                 pgnum = self.layerbook.GetPageIndex(self.page)
                 if pgnum > -1:
                     self.layerbook.SetSelection(pgnum)
+                    self._layerManager.curr_page = self.layerbook.GetCurrentPage()
+                    self.layerbook
         
         event.Skip()
         
@@ -679,13 +706,37 @@
         
         # change the cursor
         self.MapWindow.SetCursor(self.cursors["hand"])
-
+    
+    def OnRotate(self, event):
+        """!Rotate 3D view
+        """
+        if self.toolbars['map']:
+            self.toolbars['map'].OnTool(event)
+            self.toolbars['map'].action['desc'] = ''
+        
+        self.MapWindow.mouse['use'] = "rotate"
+        
+        # change the cursor
+        self.MapWindow.SetCursor(self.cursors["hand"])
     def OnErase(self, event):
         """
         Erase the canvas
         """
         self.MapWindow.EraseMap()
 
+    def OnFlyThrough(self, event):
+        """!Fly-through mode
+        """
+        if self.toolbars['map']:
+            self.toolbars['map'].OnTool(event)
+            self.toolbars['map'].action['desc'] = ''
+        
+        self.MapWindow.mouse['use'] = "fly"
+        
+        # change the cursor
+        self.MapWindow.SetCursor(self.cursors["hand"])
+        self.MapWindow.SetFocus()
+        
     def OnZoomRegion(self, event):
         """
         Zoom to region
@@ -831,6 +882,9 @@
                                                         precision, region['center_northing']))
             return
         
+        if self.IsPaneShown('3d'):
+            self.MapWindow.GoTo(e, n)
+            return
         
         dn = (region['nsres'] * region['rows']) / 2.
         region['n'] = region['center_northing'] + dn
@@ -859,15 +913,16 @@
         self.statusbarWin['goto'].Hide()
         self.statusbarWin['projection'].Hide()
         self.mapScaleValue = self.ppm = None
-
-        if self.statusbarWin['toggle'].GetSelection() == 0: # Coordinates
+        choice = globalvar.MAP_DISPLAY_STATUSBAR_MODE
+        
+        if self.statusbarWin['toggle'].GetStringSelection() == choice[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:
+        elif self.statusbarWin['toggle'].GetStringSelection() in (choice[1], choice[2]): # Extent
+            sel = self.statusbarWin['toggle'].GetStringSelection()
+            if sel == choice[1]:
                 region = self.Map.region
             else:
                 region = self.Map.GetRegion() # computation region
@@ -899,7 +954,7 @@
                                                  precision = precision)
                             e, n = utils.Deg2DMS(coord2[0], coord2[1], string = False,
                                                  precision = precision)
-                            if sel == 1:
+                            if sel == choice[1]:
                                 self.statusbar.SetStatusText("%s - %s, %s - %s" %
                                                              (w, e, s, n), 0)
                             else:
@@ -912,7 +967,7 @@
                         else:
                             w, s = coord1
                             e, n = coord2
-                            if sel == 1:
+                            if sel == choice[1]:
                                 self.statusbar.SetStatusText("%.*f - %.*f, %.*f - %.*f" %
                                                          (precision, w, precision, e,
                                                           precision, s, precision, n), 0)
@@ -930,7 +985,7 @@
                                          string = False, precision = precision)
                     e, n = utils.Deg2DMS(region["e"], region["n"],
                                          string = False, precision = precision)
-                    if sel == 1:
+                    if sel == choice[1]:
                         self.statusbar.SetStatusText("%s - %s, %s - %s" %
                                                      (w, e, s, n), 0)
                     else:
@@ -941,7 +996,7 @@
                 else:
                     w, s = region["w"], region["s"]
                     e, n = region["e"], region["n"]
-                    if sel == 1:
+                    if sel == choice[1]:
                         self.statusbar.SetStatusText("%.*f - %.*f, %.*f - %.*f" %
                                                      (precision, w, precision, e,
                                                       precision, s, precision, n), 0)
@@ -954,32 +1009,32 @@
             # enable long help
             self.StatusbarEnableLongHelp()
 
-        elif self.statusbarWin['toggle'].GetSelection() == 3: # Show comp. extent
+        elif self.statusbarWin['toggle'].GetStringSelection() == choice[3]: # Show comp. extent
             self.statusbar.SetStatusText("", 0)
             self.statusbarWin['region'].Show()
             # disable long help
             self.StatusbarEnableLongHelp(False)
 
-        elif self.statusbarWin['toggle'].GetSelection() == 4: # Align extent
+        elif self.statusbarWin['toggle'].GetStringSelection() == choice[4]: # Align extent
             self.statusbar.SetStatusText("", 0)
             self.statusbarWin['alignExtent'].Show()
             # disable long help
             self.StatusbarEnableLongHelp(False)
 
-        elif self.statusbarWin['toggle'].GetSelection() == 5: # Display resolution
+        elif self.statusbarWin['toggle'].GetStringSelection() == choice[5]: # Display resolution
             self.statusbar.SetStatusText("", 0)
             self.statusbarWin['resolution'].Show()
             # disable long help
             self.StatusbarEnableLongHelp(False)
 
-        elif self.statusbarWin['toggle'].GetSelection() == 6: # Display geometry
+        elif self.statusbarWin['toggle'].GetStringSelection() == choice[6]: # 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() == 7: # Map scale
+        elif self.statusbarWin['toggle'].GetStringSelection() == choice[7]: # Map scale
             # TODO: need to be fixed...
             ### screen X region problem
             ### user should specify ppm
@@ -1028,7 +1083,7 @@
             # disable long help
             self.StatusbarEnableLongHelp(False)
 
-        elif self.statusbarWin['toggle'].GetSelection() == 8: # go to
+        elif self.statusbarWin['toggle'].GetStringSelection() == choice[8]: # go to
             self.statusbar.SetStatusText("")
             region = self.Map.GetCurrentRegion()
             precision = int(UserSettings.Get(group = 'projection', key = 'format',
@@ -1069,7 +1124,7 @@
             # disable long help
             self.StatusbarEnableLongHelp(False)
         
-        elif self.statusbarWin['toggle'].GetSelection() == 9: # projection
+        elif self.statusbarWin['toggle'].GetStringSelection() == choice[9]: # projection
             self.statusbar.SetStatusText("")
             epsg = UserSettings.Get(group = 'projection', key = 'statusbar', subkey = 'epsg')
             if epsg:
@@ -1131,10 +1186,10 @@
     def SaveToFile(self, event):
         """!Save map to image
         """
-        if self.toolbars['nviz']:
+        if self.IsPaneShown('3d'):
             filetype = "PPM file (*.ppm)|*.ppm|TIF file (*.tif)|*.tif"
-            ltype = [{ 'ext' : 'ppm', 'type' : -1 },
-                     { 'ext' : 'tif', 'type' : wx.BITMAP_TYPE_TIF }]
+            ltype = [{ 'ext' : 'ppm', 'type' : 'ppm' },
+                     { 'ext' : 'tif', 'type' : 'tif' }]
         else:
             img = self.MapWindow.img
             if not img:
@@ -1212,10 +1267,9 @@
             maplayer = self.toolbars['vdigit'].GetLayer()
             if maplayer:
                 self.toolbars['vdigit'].OnExit()
+        if self.IsPaneShown('3d'):
+            self.RemoveNviz()
         
-        if self.toolbars['nviz']:
-            self.toolbars['nviz'].OnExit()
-        
         if not self._layerManager:
             self.Destroy()
         elif self.page:
@@ -1231,29 +1285,7 @@
     def GetWindow(self):
         """!Get map window"""
         return self.MapWindow
-    
-    def OnNvizQuerySurface(self, event):
-        """!Query current surface in 3D view mode"""
-        if self.toolbars['map'].GetAction() == 'nvizQuerySurface':
-            self.toolbars['map'].SelectDefault(event)
-            return
         
-        self.toolbars['map'].action['desc'] = 'nvizQuerySurface'
-        
-        self.MapWindow.mouse['use'] = "nvizQuerySurface"
-        self._OnQuery()
-
-    def OnNvizQueryVector(self, event):
-        """!Query current vector in 3D view mode"""
-        if self.toolbars['map'].GetAction() == 'nvizQueryVector':
-            self.toolbars['map'].SelectDefault(event)
-            return
-        
-        self.toolbars['map'].action['desc'] = 'nvizQueryVector'
-        
-        self.MapWindow.mouse['use'] = "nvizQueryVector"
-        self._OnQuery()
-        
     def QueryMap(self, x, y):
         """!Query raster or vector map layers by r/v.what
         
@@ -1298,7 +1330,9 @@
                         rast.append(iname)
                 elif ltype in ('vector', 'thememap', 'themechart'):
                     vect.append(name)
-        
+        # rasters are not queried this way in 3D, we don't want them now
+        if self.IsPaneShown('3d'):
+            rast = list()
         # use display region settings instead of computation region settings
         self.tmpreg = os.getenv("GRASS_REGION")
         os.environ["GRASS_REGION"] = self.Map.SetRegion(windres = False)
@@ -1413,25 +1447,28 @@
         
         cats = self.dialogs['attributes'].GetCats()
         
-        try:
-            qlayer = self.Map.GetListOfLayers(l_name = globalvar.QUERYLAYER)[0]
-        except IndexError:
-            qlayer = None
+        qlayer = None
+        if not self.IsPaneShown('3d'):
+            try:
+                qlayer = self.Map.GetListOfLayers(l_name = globalvar.QUERYLAYER)[0]
+            except IndexError:
+                pass
         
         if self.dialogs['attributes'].mapDBInfo and cats:
-            # 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.IsPaneShown('3d'):
+                # highlight feature & re-draw map
+                if qlayer:
+                    qlayer.SetCmd(self.AddTmpVectorMapLayer(mapName, cats,
+                                                            useId = False,
+                                                            addLayer = False))
+                else:
+                    qlayer = self.AddTmpVectorMapLayer(mapName, cats, useId = False)
+                
+                # set opacity based on queried layer
+                opacity = self.tree.GetPyData(self.tree.layer_selected)[0]['maplayer'].GetOpacity(float = True)
+                qlayer.SetOpacity(opacity)
+                
+                self.MapWindow.UpdateMap(render = False, renderVector = False)
             if not self.dialogs['attributes'].IsShown():
                 self.dialogs['attributes'].Show()
         else:
@@ -1446,40 +1483,20 @@
         if self.toolbars['map']:
             self.toolbars['map'].OnTool(event)
             action = self.toolbars['map'].GetAction()
-        
-        if self.toolbars['nviz']:
-            toolsmenu = wx.Menu()
-            raster = wx.MenuItem(parentMenu = toolsmenu, id = wx.ID_ANY,
-                                 text = _("Query surface (raster map)"),
-                                 kind = wx.ITEM_CHECK)
-            toolsmenu.AppendItem(raster)
-            self.Bind(wx.EVT_MENU, self.OnNvizQuerySurface, raster)
-            if action == "nvizQuerySurface":
-                raster.Check(True)
-            vector = wx.MenuItem(parentMenu = toolsmenu, id = wx.ID_ANY,
-                                 text = _("Query vector map"),
-                                 kind = wx.ITEM_CHECK)
-            toolsmenu.AppendItem(vector)
-            self.Bind(wx.EVT_MENU, self.OnNvizQueryVector, vector)
-            if action == "nvizQueryVector":
-                vector.Check(True)
-
-            self.PopupMenu(toolsmenu)
-            toolsmenu.Destroy()
-        else:
-            self.toolbars['map'].action['desc'] = 'queryMap'
-            self.MapWindow.mouse['use'] = "query"
             
-            if not self.IsStandalone():
-                # switch to output console to show query results
-                self._layerManager.notebook.SetSelectionByName('output')
-            
-            self.MapWindow.mouse['box'] = "point"
-            self.MapWindow.zoomtype = 0
-            
-            # change the cursor
-            self.MapWindow.SetCursor(self.cursors["cross"])
+        self.toolbars['map'].action['desc'] = 'queryMap'
+        self.MapWindow.mouse['use'] = "query"
         
+        if not self.IsStandalone():
+            # switch to output console to show query results
+            self._layerManager.notebook.SetSelectionByName('output')
+        
+        self.MapWindow.mouse['box'] = "point"
+        self.MapWindow.zoomtype = 0
+        
+        # change the cursor
+        self.MapWindow.SetCursor(self.cursors["cross"])
+        
     def AddTmpVectorMapLayer(self, name, cats, useId = False, addLayer = True):
         """!Add temporal vector map layer to map composition
 
@@ -1735,6 +1752,13 @@
         AddScale.SetBitmap(icons["addBarscale"].GetBitmap(self.iconsize))
         decmenu.AppendItem(AddScale)
         self.Bind(wx.EVT_MENU, self.OnAddBarscale, AddScale)
+        # temporary
+        if self.IsPaneShown('3d'):
+            AddScale.Enable(False)
+            AddArrow = wx.MenuItem(decmenu, wx.ID_ANY, _("Add north arrow"))
+            AddArrow.SetBitmap(icons["addBarscale"].GetBitmap(self.iconsize))
+            decmenu.AppendItem(AddArrow)
+            self.Bind(wx.EVT_MENU, self.OnAddArrow, AddArrow)
         
         AddLegend = wx.MenuItem(decmenu, wx.ID_ANY, icons["addLegend"].GetLabel())
         AddLegend.SetBitmap(icons["addLegend"].GetBitmap(self.iconsize))
@@ -1818,13 +1842,14 @@
         """
         if self.MapWindow.dragid > -1:
             id = self.MapWindow.dragid
+            self.MapWindow.dragid = -1
         else:
             # index for overlay layer in render
             if len(self.MapWindow.textdict.keys()) > 0:
-                id = self.MapWindow.textdict.keys()[-1] + 1
+                id = max(self.MapWindow.textdict.keys()) + 1
             else:
                 id = 101
-
+        
         self.dialogs['text'] = gdialogs.TextLayerDialog(parent = self, ovlId = id, 
                                                         title = _('Add text layer'),
                                                         size = (400, 200))
@@ -1834,29 +1859,43 @@
         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)
+                    self.MapWindow2D.pdc.ClearId(id)
+                    self.MapWindow2D.pdc.RemoveId(id)
                     del self.MapWindow.textdict[id]
+                    if self.IsPaneShown('3d'):
+                        self.MapWindow3D.UpdateOverlays()
+                        self.MapWindow.UpdateMap()
+                    else:
+                        self.MapWindow2D.UpdateMap(render = False, renderVector = False)
                 except:
                     pass
                 return
 
-            self.MapWindow.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)
+            if self.IsPaneShown('3d'):
+                self.MapWindow3D.UpdateOverlays()
+                self.MapWindow3D.UpdateMap()
+            else:
+                self.MapWindow2D.pdc.ClearId(id)
+                self.MapWindow2D.pdc.SetId(id)
+                self.MapWindow2D.UpdateMap(render = False, renderVector = False)
             
-            self.MapWindow.UpdateMap(render = False, renderVector = False)
-            
         self.MapWindow.mouse['use'] = 'pointer'
-
+    
+    def OnAddArrow(self, event):
+        """!Handler for north arrow menu selection.
+            Opens Appearance page of nviz notebook.
+        """
+        
+        self._layerManager.nviz.SetPage('decoration')
+        self.MapWindow3D.SetDrawArrow((70, 70))
+        
     def GetOptData(self, dcmd, type, params, propwin):
         """!Callback method for decoration overlay command generated by
         dialog created in menuform.py

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/mapdisp_window.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/mapdisp_window.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/mapdisp_window.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -163,6 +163,7 @@
                 e, n = self.Pixel2Cell(event.GetPositionTuple())
             except (TypeError, ValueError):
                 self.parent.statusbar.SetStatusText("", 0)
+                event.Skip()
                 return
             
             updated = False
@@ -479,12 +480,12 @@
             w, h = self.GetFullTextExtent(img['text'])[0:2]
             pdc.SetFont(img['font'])
             pdc.SetTextForeground(img['color'])
-            coords, w, h = self.TextBounds(img)
+            coords, bbox = self.TextBounds(img)
             if rotation == 0:
                 pdc.DrawText(img['text'], coords[0], coords[1])
             else:
                 pdc.DrawRotatedText(img['text'], coords[0], coords[1], rotation)
-            pdc.SetIdBounds(drawid, wx.Rect(coords[0], coords[1], w, h))
+            pdc.SetIdBounds(drawid, bbox)
         
         pdc.EndDrawing()
         
@@ -492,11 +493,15 @@
         
         return drawid
     
-    def TextBounds(self, textinfo):
+    def TextBounds(self, textinfo, relcoords = False):
         """!Return text boundary data
         
         @param textinfo text metadata (text, font, color, rotation)
         @param coords reference point
+        
+        @return coords of nonrotated text bbox (TL corner)
+        @return bbox of rotated text bbox (wx.Rect)
+        @return relCoords are text coord inside bbox
         """
         if 'rotation' in textinfo:
             rotation = float(textinfo['rotation'])
@@ -504,7 +509,8 @@
             rotation = 0.0
         
         coords = textinfo['coords']
-        
+        bbox = wx.Rect(coords[0], coords[1], 0, 0)
+        relCoords = (0, 0)
         Debug.msg (4, "BufferedWindow.TextBounds(): text=%s, rotation=%f" % \
                    (textinfo['text'], rotation))
         
@@ -515,15 +521,31 @@
         w, h = self.GetTextExtent(textinfo['text'])
         
         if rotation == 0:
-            coords[2], coords[3] = coords[0] + w, coords[1] + h
-            return coords, w, h
+            bbox[2], bbox[3] = w, h
+            if relcoords:
+                return coords, bbox, relCoords
+            else:
+                return coords, bbox
         
         boxh = math.fabs(math.sin(math.radians(rotation)) * w) + h
         boxw = math.fabs(math.cos(math.radians(rotation)) * w) + h
-        coords[2] = coords[0] + boxw
-        coords[3] = coords[1] + boxh
-        
-        return coords, boxw, boxh
+        if rotation > 0 and rotation < 90:
+            bbox[1] -= boxh
+            relCoords = (0, boxh)
+        elif rotation >= 90 and rotation < 180:
+            bbox[0] -= boxw
+            bbox[1] -= boxh
+            relCoords = (boxw, boxh)
+        elif rotation >= 180 and rotation < 270:
+            bbox[0] -= boxw
+            relCoords = (boxw, 0)
+        bbox[2] = boxw
+        bbox[3] = boxh
+        bbox.Inflate(h,h)
+        if relcoords:
+            return coords, bbox, relCoords
+        else:
+            return coords, bbox
 
     def OnPaint(self, event):
         """!Draw PseudoDC's to buffered paint DC
@@ -960,7 +982,9 @@
         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
+            self.textdict[id]['bbox'] = r2
+            self.textdict[id]['coords'][0] += dx
+            self.textdict[id]['coords'][1] += dy
         r = r.Union(r2)
         r.Inflate(4,4)
         self.RefreshRect(r, False)
@@ -1348,7 +1372,7 @@
             if self.dragid < 99 and self.dragid in self.overlays:
                 self.overlays[self.dragid]['coords'] = self.pdc.GetIdBounds(self.dragid)
             elif self.dragid > 100 and self.dragid in self.textdict:
-                self.textdict[self.dragid]['coords'] = self.pdc.GetIdBounds(self.dragid)
+                self.textdict[self.dragid]['bbox'] = self.pdc.GetIdBounds(self.dragid)
             else:
                 pass
             self.dragid = None

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/menuform.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/menuform.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/menuform.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -2000,7 +2000,15 @@
             self.notebookPages[kwargs['name']] = kwargs['page']
             del kwargs['name']
         super(GNotebook, self).AddPage(**kwargs)
-
+        
+    def InsertPage(self, **kwargs):
+        """!Insert a new page
+        """
+        if 'name' in kwargs:
+            self.notebookPages[kwargs['name']] = kwargs['page']
+            del kwargs['name']
+        super(GNotebook, self).InsertPage(**kwargs)
+        
     def SetSelectionByName(self, page):
         """!Set notebook
         

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/nviz.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/nviz.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/nviz.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -8,13 +8,14 @@
 Map Display supports standard 2D view mode ('mapdisp' module) and
 2.5/3D mode ('nviz_mapdisp' module).
 
-(C) 2008, 2010 by the GRASS Development Team
+(C) 2008, 2010-2011 by the GRASS Development Team
 
 This program is free software under the GNU General Public
 License (>=v2). Read the file COPYING that comes with GRASS
 for details.
 
 @author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
+ at author Anna Kratochvilova <KratochAnna seznam.cz> (Google SoC 2011)
 """
 
 errorMsg = ''

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

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

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/nviz_preferences.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/nviz_preferences.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/nviz_preferences.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -6,16 +6,18 @@
 Classes:
  - NvizPreferencesDialog
 
-(C) 2008-2010 by the GRASS Development Team
+(C) 2008-2011 by the GRASS Development Team
 
 This program is free software under the GNU General Public License
 (>=v2). Read the file COPYING that comes with GRASS for details.
 
 @author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
- at author Enhancements by Michael Barton <michael.barton at asu.edu>
+ at author Enhancements by Michael Barton <michael.barton asu.edu>
+ at author Anna Kratochvilova <KratochAnna seznam.cz> (Google SoC 2011)
 """
 
 import types
+import copy
 
 import wx
 import wx.lib.colourselect as csel
@@ -30,18 +32,21 @@
                  settings = UserSettings):
         PreferencesBaseDialog.__init__(self, parent = parent, title = title,
                                        settings = settings)
-        self.toolWin = self.parent.GetLayerManager().nviz
-        self.win = dict()
+        self.toolWin = self.parent.nviz
         
         # create notebook pages
         self._createViewPage(self.notebook)
+        self._createFlyPage(self.notebook)
+        self._createLightPage(self.notebook)
+        self._createSurfacePage(self.notebook)
         self._createVectorPage(self.notebook)
         
         self.SetMinSize(self.GetBestSize())
         self.SetSize(self.size)
+        self.btnDefault.SetToolTipString(_("Revert settings to default, changes are not applied"))
         
     def _createViewPage(self, notebook):
-        """!Create notebook page for general settings"""
+        """!Create notebook page for view settings"""
         panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
         
         notebook.AddPage(page = panel,
@@ -49,152 +54,111 @@
         
         pageSizer = wx.BoxSizer(wx.VERTICAL)
         
-        self.win['general'] = {}
-        self.win['view'] = {}
         box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
                             label = " %s " % (_("View")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
         gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
-        
+        row = 0
         # perspective
-        self.win['view']['persp'] = {}
         pvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'persp')
         ipvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'persp', internal = True)
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
                                          label = _("Perspective:")),
-                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+                      pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(value)")),
-                      pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+                                         label = _("value:")),
+                      pos = (row, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
         
         pval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
                            initial = pvals['value'],
                            min = ipvals['min'],
                            max = ipvals['max'])
-        self.win['view']['persp']['value'] = pval.GetId()
-        gridSizer.Add(item = pval, pos = (0, 2),
+        self.winId['nviz:view:persp:value'] = pval.GetId()
+        gridSizer.Add(item = pval, pos = (row, 2),
                       flag = wx.ALIGN_CENTER_VERTICAL)
         
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(step)")),
-                      pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+                                         label = _("step:")),
+                      pos = (row, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
         
         pstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
                            initial = pvals['step'],
                            min = ipvals['min'],
                            max = ipvals['max']-1)
-        self.win['view']['persp']['step'] = pstep.GetId()
-        gridSizer.Add(item = pstep, pos = (0, 4),
+        self.winId['nviz:view:persp:step'] = pstep.GetId()
+        gridSizer.Add(item = pstep, pos = (row, 4),
                       flag = wx.ALIGN_CENTER_VERTICAL)
+        row += 1
         
         # position
-        self.win['view']['pos'] = {}
         posvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'position')
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
                                          label = _("Position:")),
-                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+                      pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(x)")),
-                      pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+                                         label = _("x:")),
+                      pos = (row, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
         
         px = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
                            initial = posvals['x'] * 100,
                            min = 0,
                            max = 100)
-        self.win['view']['pos']['x'] = px.GetId()
-        gridSizer.Add(item = px, pos = (1, 2),
+        self.winId['nviz:view:position:x'] = px.GetId()
+        gridSizer.Add(item = px, pos = (row, 2),
                       flag = wx.ALIGN_CENTER_VERTICAL)
         
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = "(y)"),
-                      pos = (1, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+                                         label = "y:"),
+                      pos = (row, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
         
         py = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
                            initial = posvals['y'] * 100,
                            min = 0,
                            max = 100)
-        self.win['view']['pos']['y'] = py.GetId()
-        gridSizer.Add(item = py, pos = (1, 4),
+        self.winId['nviz:view:position:y'] = py.GetId()
+        gridSizer.Add(item = py, pos = (row, 4),
                       flag = wx.ALIGN_CENTER_VERTICAL)
+        row += 1
         
-        # height
-        self.win['view']['height'] = {}
-        hvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'height')
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Height:")),
-                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(step)")),
-                      pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        # height is computed dynamically
         
-        hstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                           initial = hvals['step'],
-                           min = 1,
-                           max = 1e6)
-        self.win['view']['height']['step'] = hstep.GetId()
-        gridSizer.Add(item = hstep, pos = (2, 2),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
         # twist
-        self.win['view']['twist'] = {}
         tvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'twist')
         itvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'twist', internal = True)
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
                                          label = _("Twist:")),
-                      pos = (3, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+                      pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(value)")),
-                      pos = (3, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+                                         label = _("value:")),
+                      pos = (row, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
         
         tval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
                            initial = tvals['value'],
                            min = itvals['min'],
                            max = itvals['max'])
-        self.win['view']['twist']['value'] = tval.GetId()
-        gridSizer.Add(item = tval, pos = (3, 2),
+        self.winId['nviz:view:twist:value'] = tval.GetId()
+        gridSizer.Add(item = tval, pos = (row, 2),
                       flag = wx.ALIGN_CENTER_VERTICAL)
+        row += 1
         
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(step)")),
-                      pos = (3, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-        
-        tstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                           initial = tvals['step'],
-                           min = itvals['min'],
-                           max = itvals['max']-1)
-        self.win['view']['twist']['step'] = tstep.GetId()
-        gridSizer.Add(item = tstep, pos = (3, 4),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
         # z-exag
-        self.win['view']['z-exag'] = {}
         zvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'z-exag')
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
                                          label = _("Z-exag:")),
-                      pos = (4, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+                      pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(value)")),
-                      pos = (4, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+                                         label = _("value:")),
+                      pos = (row, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
         
         zval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = zvals['value'],
                            min = -1e6,
                            max = 1e6)
-        self.win['view']['z-exag']['value'] = zval.GetId()
-        gridSizer.Add(item = zval, pos = (4, 2),
+        self.winId['nviz:view:z-exag:value'] = zval.GetId()
+        gridSizer.Add(item = zval, pos = (row, 2),
                       flag = wx.ALIGN_CENTER_VERTICAL)
         
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(step)")),
-                      pos = (4, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
         
-        zstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                           initial = zvals['step'],
-                           min = -1e6,
-                           max = 1e6)
-        self.win['view']['z-exag']['step'] = zstep.GetId()
-        gridSizer.Add(item = zstep, pos = (4, 4),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
         boxSizer.Add(item = gridSizer, proportion = 1,
                   flag = wx.ALL | wx.EXPAND, border = 3)
         pageSizer.Add(item = boxSizer, proportion = 0,
@@ -213,24 +177,283 @@
                       pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
         
         color = csel.ColourSelect(panel, id = wx.ID_ANY,
-                                  colour = UserSettings.Get(group = 'nviz', key = 'settings',
-                                                            subkey = ['general', 'bgcolor']),
+                                  colour = UserSettings.Get(group = 'nviz', key = 'view',
+                                                            subkey = ['background', 'color']),
                                   size = globalvar.DIALOG_COLOR_SIZE)
-        self.win['general']['bgcolor'] = color.GetId()
+        color.SetName('GetColour')
+        self.winId['nviz:view:background:color'] = color.GetId()
         gridSizer.Add(item = color, pos = (0, 1))
         
         boxSizer.Add(item = gridSizer, proportion = 1,
-                  flag = wx.ALL | wx.EXPAND, border = 3)
+                  flag = wx.ALL | wx.EXPAND, border = 5)
         pageSizer.Add(item = boxSizer, proportion = 0,
                       flag = wx.EXPAND | wx.ALL,
-                      border = 3)
+                      border = 5)
         
         panel.SetSizer(pageSizer)
         
         return panel
+        
+    def _createFlyPage(self, notebook):
+        """!Create notebook page for view settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        
+        notebook.AddPage(page = panel,
+                         text = " %s " % _("Fly-through"))
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        # fly throuhg mode
+        box = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+                           label = " %s " % (_("Fly-through mode")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(0)
+        
+        # move exag
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Move exag:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        moveExag = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 20, 
+                                  initial = UserSettings.Get(group = 'nviz', key = 'fly',
+                                                             subkey = ['exag', 'move']),
+                                  size = (65, -1))
+        self.winId['nviz:fly:exag:move'] = moveExag.GetId()
+        gridSizer.Add(item = moveExag, pos = (0, 1))
+        
+        # turn exag
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Turn exag:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        turnExag = wx.SpinCtrl(panel, id = wx.ID_ANY, min = 1, max = 20, 
+                                  initial = UserSettings.Get(group = 'nviz', key = 'fly',
+                                                             subkey = ['exag', 'turn']),
+                                  size = (65, -1))
+        self.winId['nviz:fly:exag:turn'] = turnExag.GetId()
+        gridSizer.Add(item = turnExag, pos = (1, 1))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 5)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
+        
+    def _createLightPage(self, notebook):
+        """!Create notebook page for light settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        
+        notebook.AddPage(page = panel,
+                         text = " %s " % _("Lighting"))
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Light")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+
+        
+        # position
+        posvals = UserSettings.Get(group = 'nviz', key = 'light', subkey = 'position')
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Position:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("x:")),
+                      pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        px = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = posvals['x'] * 100,
+                           min = -100,
+                           max = 100)
+        self.winId['nviz:light:position:x'] = px.GetId()
+        gridSizer.Add(item = px, pos = (0, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = "y:"),
+                      pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        py = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = posvals['y'] * 100,
+                           min = -100,
+                           max = 100)
+        self.winId['nviz:light:position:y'] = py.GetId()
+        gridSizer.Add(item = py, pos = (0, 4),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+                    
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("z:")),
+                      pos = (0, 5), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
+        
+        pz = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = posvals['z'],
+                           min = 0,
+                           max = 100)
+        self.winId['nviz:light:position:z'] = pz.GetId()
+        gridSizer.Add(item = pz, pos = (0, 6),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+                    
+        # brightness
+        brightval = UserSettings.Get(group = 'nviz', key = 'light', subkey = 'bright')
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Brightness:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        bright = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = brightval,
+                           min = 0,
+                           max = 100)
+        self.winId['nviz:light:bright'] = bright.GetId()
+        gridSizer.Add(item = bright, pos = (1, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+                    
+        # ambient
+        ambval = UserSettings.Get(group = 'nviz', key = 'light', subkey = 'ambient')
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Ambient:")),
+                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        amb = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = ambval,
+                           min = 0,
+                           max = 100)
+        self.winId['nviz:light:ambient'] = amb.GetId()
+        gridSizer.Add(item = amb, pos = (2, 2),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+                    
+        # light color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Color:")),
+                      pos = (3, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = UserSettings.Get(group = 'nviz', key = 'light',
+                                                            subkey = 'color'),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        color.SetName('GetColour')
+        self.winId['nviz:light:color'] = color.GetId()
+        gridSizer.Add(item = color, pos = (3, 2))
+        
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 5)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
     
+    def _createSurfacePage(self, notebook):
+        """!Create notebook page for surface settings"""
+        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
+        
+        notebook.AddPage(page = panel,
+                         text = " %s " % _("Surface"))
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        # draw
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Draw")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
+        # mode
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                        label = _("Mode:")), flag = wx.ALIGN_CENTER_VERTICAL,
+                        pos = (0, 0))
+        mode = wx.Choice(parent = panel, id = wx.ID_ANY, size = (-1, -1),
+                          choices = [_("coarse"),
+                                     _("fine"),
+                                     _("both")])
+        self.winId['nviz:surface:draw:mode'] = mode.GetId()
+        mode.SetName('GetSelection')
+        mode.SetSelection(UserSettings.Get(group = 'nviz', key = 'surface',
+                                            subkey = ['draw', 'mode']))
+        gridSizer.Add(item = mode, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (0, 1))
+                    
+        # fine
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                        label = _("Fine mode:")), flag = wx.ALIGN_CENTER_VERTICAL,
+                        pos = (1, 0))
+        res = UserSettings.Get(group = 'nviz', key = 'surface', subkey = ['draw','res-fine'])
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                        label = _("resolution:")), flag = wx.ALIGN_CENTER_VERTICAL,
+                        pos = (1, 1))
+        fine = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = res,
+                           min = 1,
+                           max = 100)
+        self.winId['nviz:surface:draw:res-fine'] = fine.GetId()
+        
+        gridSizer.Add(item = fine, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (1, 2))
+                    
+        # coarse
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                        label = _("Coarse mode:")), flag = wx.ALIGN_CENTER_VERTICAL,
+                        pos = (2, 0))
+        res = UserSettings.Get(group = 'nviz', key = 'surface', subkey = ['draw','res-coarse'])
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                        label = _("resolution:")), flag = wx.ALIGN_CENTER_VERTICAL,
+                        pos = (2, 1))
+        coarse = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = res,
+                           min = 1,
+                           max = 100)
+        self.winId['nviz:surface:draw:res-coarse'] = coarse.GetId()
+        
+        gridSizer.Add(item = coarse, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (2, 2))
+        #style
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+            label = _("style:")), flag = wx.ALIGN_CENTER_VERTICAL,
+            pos = (3, 1))
+        style = wx.Choice(parent = panel, id = wx.ID_ANY, size = (-1, -1),
+                          choices = [_("wire"),
+                                     _("surface")])
+        self.winId['nviz:surface:draw:style'] = style.GetId()
+        style.SetName('GetSelection')
+        style.SetSelection(UserSettings.Get(group = 'nviz', key = 'surface',
+                                            subkey = ['draw', 'style']))
+        self.winId['nviz:surface:draw:style'] = style.GetId()
+        
+        gridSizer.Add(item = style, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (3, 2))
+        #wire color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+            label = _("wire color:")), flag = wx.ALIGN_CENTER_VERTICAL,
+            pos = (4, 1))
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = UserSettings.Get(group = 'nviz', key = 'surface',
+                                                            subkey = ['draw', 'wire-color']),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        color.SetName('GetColour')
+        self.winId['nviz:surface:draw:wire-color'] = color.GetId() 
+        gridSizer.Add(item = color, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (4, 2))
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                  flag = wx.ALL | wx.EXPAND, border = 5)            
+        pageSizer.Add(item = boxSizer, proportion = 0,
+              flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+              border = 5)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
+    
     def _createVectorPage(self, notebook):
-        """!Create notebook page for general settings"""
+        """!Create notebook page for vector settings"""
         panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
         
         notebook.AddPage(page = panel,
@@ -239,46 +462,53 @@
         pageSizer = wx.BoxSizer(wx.VERTICAL)
         
         # vector lines
-        self.win['vector'] = {}
-        self.win['vector']['lines'] = {}
         box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
                             label = " %s " % (_("Vector lines")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
         gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
         
-        # show
         row = 0
-        showLines = wx.CheckBox(parent = panel, id = wx.ID_ANY,
-                                label = _("Show lines"))
-        self.win['vector']['lines']['show'] = showLines.GetId()
-        showLines.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
-                                            subkey = ['lines', 'show']))
-        gridSizer.Add(item = showLines, pos = (row, 0))
+        # icon size
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Width:")),
+                      pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
         
+        iwidth = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                            initial = 12,
+                            min = 1,
+                            max = 100)
+        self.winId['nviz:vector:lines:width'] = iwidth.GetId()
+        iwidth.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
+                                        subkey = ['lines', 'width']))
+        gridSizer.Add(item = iwidth, pos = (row, 1),
+                      flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        # icon color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Color:")),
+                      pos = (row, 4), flag = wx.ALIGN_CENTER_VERTICAL)
+        icolor = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        icolor.SetName('GetColour')
+        self.winId['nviz:vector:lines:color'] = icolor.GetId()
+        icolor.SetColour(UserSettings.Get(group = 'nviz', key = 'vector',
+                                          subkey = ['lines', 'color']))
+        gridSizer.Add(item = icolor, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (row, 5))
         boxSizer.Add(item = gridSizer, proportion = 1,
-                  flag = wx.ALL | wx.EXPAND, border = 3)
+                  flag = wx.ALL | wx.EXPAND, border = 5)
         pageSizer.Add(item = boxSizer, proportion = 0,
                       flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border = 3)
+                      border = 5)
         
         # vector points
-        self.win['vector']['points'] = {}
         box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
                             label = " %s " % (_("Vector points")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
         gridSizer = wx.GridBagSizer(vgap = 3, hgap = 5)
         
-        # show
         row = 0
-        showPoints = wx.CheckBox(parent = panel, id = wx.ID_ANY,
-                                 label = _("Show points"))
-        showPoints.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
-                                             subkey = ['points', 'show']))
-        self.win['vector']['points']['show'] = showPoints.GetId()
-        gridSizer.Add(item = showPoints, pos = (row, 0), span = (1, 8))
-        
         # icon size
-        row += 1 
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
                                          label = _("Size:")),
                       pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
@@ -287,189 +517,113 @@
                             initial = 100,
                             min = 1,
                             max = 1e6)
-        self.win['vector']['points']['size'] = isize.GetId()
+        self.winId['nviz:vector:points:size'] = isize.GetId()
         isize.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
                                         subkey = ['points', 'size']))
         gridSizer.Add(item = isize, pos = (row, 1),
                       flag = wx.ALIGN_CENTER_VERTICAL)
         
-        # icon width
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Width:")),
-                      pos = (row, 2), flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        iwidth = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                            initial = 2,
-                            min = 1,
-                            max = 1e6)
-        self.win['vector']['points']['width'] = isize.GetId()
-        iwidth.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
-                                         subkey = ['points', 'width']))
-        gridSizer.Add(item = iwidth, pos = (row, 3),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
         # icon symbol
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
                                          label = _("Marker:")),
-                      pos = (row, 4), flag = wx.ALIGN_CENTER_VERTICAL)
+                      pos = (row, 2), flag = wx.ALIGN_CENTER_VERTICAL)
         isym = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
                           choices = UserSettings.Get(group = 'nviz', key = 'vector',
                                                    subkey = ['points', 'marker'], internal = True))
-        isym.SetName("selection")
-        self.win['vector']['points']['marker'] = isym.GetId()
+        isym.SetName("GetSelection")
+        self.winId['nviz:vector:points:marker'] = isym.GetId()
         isym.SetSelection(UserSettings.Get(group = 'nviz', key = 'vector',
                                            subkey = ['points', 'marker']))
         gridSizer.Add(item = isym, flag = wx.ALIGN_CENTER_VERTICAL,
-                      pos = (row, 5))
+                      pos = (row, 3))
         
         # icon color
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
                                          label = _("Color:")),
-                      pos = (row, 6), flag = wx.ALIGN_CENTER_VERTICAL)
-        icolor = csel.ColourSelect(panel, id = wx.ID_ANY)
-        icolor.SetName("color")
-        self.win['vector']['points']['color'] = icolor.GetId()
+                      pos = (row, 4), flag = wx.ALIGN_CENTER_VERTICAL)
+        icolor = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                   size = globalvar.DIALOG_COLOR_SIZE)
+        icolor.SetName('GetColour')
+        self.winId['nviz:vector:points:color'] = icolor.GetId()
         icolor.SetColour(UserSettings.Get(group = 'nviz', key = 'vector',
                                           subkey = ['points', 'color']))
         gridSizer.Add(item = icolor, flag = wx.ALIGN_CENTER_VERTICAL,
-                      pos = (row, 7))
+                      pos = (row, 5))
         
         boxSizer.Add(item = gridSizer, proportion = 1,
-                  flag = wx.ALL | wx.EXPAND, border = 3)
+                  flag = wx.ALL | wx.EXPAND, border = 5)
         pageSizer.Add(item = boxSizer, proportion = 0,
                       flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border = 3)
+                      border = 5)
         
         panel.SetSizer(pageSizer)
         
         return panel
-    
+
     def OnDefault(self, event):
-        """Restore default settings"""
-        settings = copy.deepcopy(UserSettings.GetDefaultSettings()['nviz'])
-        UserSettings.Set(group = 'nviz',
-                         value = settings)
+        """!Button 'Set to default' pressed"""
+        self.settings.userSettings = copy.deepcopy(self.settings.defaultSettings)
         
-        for subgroup, key in settings.iteritems(): # view, surface, vector...
-            if subgroup != 'view':
-                continue
-            for subkey, value in key.iteritems():
-                for subvalue in value.keys():
-                    win = self.FindWindowById(self.win[subgroup][subkey][subvalue])
-                    val = settings[subgroup][subkey][subvalue]
-                    if subkey == 'position':
-                        val = int(val * 100)
-                    
-                    win.SetValue(val)
+        # update widgets
+        for gks in self.winId.keys():
+            subkey1 = None
+            try:
+                group, key, subkey = gks.split(':')
+                value = self.settings.Get(group, key, subkey)
+            except ValueError:
+                group, key, subkey, subkey1 = gks.split(':')
+                value = self.settings.Get(group, key, [subkey, subkey1])
+            if subkey == 'position':
+                if subkey1 in ('x', 'y'):
+                    value = float(value) * 100
+            win = self.FindWindowById(self.winId[gks])
+            if win.GetName() == 'GetSelection':
+                value = win.SetSelection(value)
+            else:
+                value = win.SetValue(value)        
         
-        event.Skip()
-        
     def OnApply(self, event):
         """Apply Nviz settings for current session"""
-        settings = UserSettings.Get(group = 'nviz')
-        for subgroup, key in settings.iteritems(): # view, surface, vector...
-            for subkey, value in key.iteritems():
-                if type(value) == types.DictType:
-                    for subvalue in value.keys():
-                        try: # TODO
-                            win = self.FindWindowById(self.win[subgroup][subkey][subvalue])
-                        except:
-                            # print 'e', subgroup, subkey, subvalue
-                            continue
-                        
-                        if win.GetName() == "selection":
-                            value = win.GetSelection()
-                        elif win.GetName() == "color":
-                            value = tuple(win.GetColour())
-                        else:
-                            value = win.GetValue()
-                        if subkey == 'pos':
-                            value = float(value) / 100
-                            
-                        settings[subgroup][subkey][subvalue] = value
+        for item in self.winId.keys():
+            try:
+                group, key, subkey = item.split(':')
+                subkey1 = None
+            except ValueError:
+                group, key, subkey, subkey1 = item.split(':')
+            
+            id = self.winId[item]
+            win = self.FindWindowById(id)
+            if win.GetName() == 'GetSelection':
+                value = win.GetSelection()
+            elif win.GetName() == 'GetColour':
+                value = tuple(win.GetValue())
+            else:
+                value = win.GetValue()
+            
+            if subkey == 'position':
+                if subkey1 in ('x', 'y'):
+                    value = float(value) / 100
+            if subkey1:
+                self.settings.Set(group, value, key, [subkey, subkey1])
+            else:
+                self.settings.Set(group, value, key, subkey)
+                
+        self.toolWin.LoadSettings()
         
-    def OnSave(self, event):
-        """!Apply changes, update map and save settings of selected
-        layer
-        """
-        # apply changes
-        self.OnApply(None)
         
-        if self.GetSelection() == self.page['id']:
-            fileSettings = {}
-            UserSettings.ReadSettingsFile(settings = fileSettings)
-            fileSettings['nviz'] = UserSettings.Get(group = 'nviz')
-            file = UserSettings.SaveToFile(fileSettings)
-            self.parent.goutput.WriteLog(_('Nviz settings saved to file <%s>.') % file)
-        
-    def OnLoad(self, event):
-        """!Apply button pressed"""
-        self.LoadSettings()
-        
-        if event:
-            event.Skip()
-
-    def LoadSettings(self):
-        """!Load saved Nviz settings and apply to current session"""
-        UserSettings.ReadSettingsFile()
-        settings = copy.deepcopy(UserSettings.Get(group = 'nviz'))
-        
-        for subgroup, key in settings.iteritems(): # view, surface, vector...
-            for subkey, value in key.iteritems():
-                for subvalue in value.keys():
-                    if subvalue == 'step':
-                        continue
-                    else:
-                        insetting = value[subvalue]                                                    
-                    if subgroup == 'view':
-                        for viewkey, viewitem in self.mapWindow.view[subkey].iteritems(): 
-                            if viewkey == subvalue:
-                                self.mapWindow.view[subkey][viewkey] = insetting 
-                            else:
-                                continue
-                    else:
-                        for otherkey, otheritem in self.win[subgroup][subkey].iteritems():
-                            if type(otheritem) == data:
-                                for endkey, enditem in otheritem.iteritems():
-                                    if endkey == subvalue:
-                                        paramwin = self.FindWindowById(enditem)
-                                    else:
-                                        continue
-                            else:
-                                if otherkey == subvalue:
-                                    paramwin = self.FindWindowById(otheritem)
-                                else:
-                                    continue
-                            if type(insetting) in [tuple, list] and len(insetting) > 2:
-                                insetting = tuple(insetting)
-                                paramwin.SetColour(insetting)
-                            else:
-                                try:
-                                    paramwin.SetValue(insetting)
-                                except:
-                                    try:
-                                        paramwin.SetStringSelection(insetting)
-                                    except:
-                                        continue
-                                
-        self.toolWin.UpdateSettings()
-        self.FindWindowById(self.win['view']['pos']).Draw()
-        self.FindWindowById(self.win['view']['pos']).Refresh(False)
-        
-        self.mapWindow.render['quick'] = False
-        self.mapWindow.Refresh(False)
-        
     def OnSave(self, event):
         """!Save button pressed
         
-        Save settings to configuration file
+        Apply changes and save settings to configuration file
         """
+        self.OnApply(None)
         fileSettings = {}
         UserSettings.ReadSettingsFile(settings = fileSettings)
         fileSettings['nviz'] = UserSettings.Get(group = 'nviz')
         
-        fileName = UserSettings.SaveToFile(fileSettings)
-        self.parent.GetLayerManager().goutput.WriteLog(_('3D view settings saved to file <%s>.') % fileName)
+        UserSettings.SaveToFile(fileSettings)
+        self.parent.goutput.WriteLog(
+                _('3D view settings saved to file <%s>.') % UserSettings.filePath)
         
         self.Destroy()
         

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/nviz_tools.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/nviz_tools.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/nviz_tools.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -4,58 +4,238 @@
 @brief Nviz (3D view) tools window
 
 Classes:
+ - ScrolledPanel
+ - NTCValidator
+ - NumTextCtrl
+ - FloatSlider
+ - SymbolButton
  - NvizToolWindow
  - PositionWindow
  - ViewPositionWindow
  - LightPositionWindow
 
-(C) 2008-2010 by the GRASS Development Team
+(C) 2008-2011 by the GRASS Development Team
 
 This program is free software under the GNU General Public
 License (>=v2). Read the file COPYING that comes with GRASS
 for details.
 
 @author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
- at author Enhancements by Michael Barton <michael.barton at asu.edu>
+ at author Enhancements by Michael Barton <michael.barton asu.edu>
+ at author Anna Kratochvilova <kratochanna gmail.com> (Google SoC 2011)
 """
 
 import os
 import sys
 import copy
 import types
+import string
 
 import wx
-import wx.lib.colourselect as csel
+import wx.lib.colourselect  as csel
 import wx.lib.scrolledpanel as SP
+import wx.lib.filebrowsebutton as filebrowse
+
 try:
+    from wx.lib.buttons import ThemedGenBitmapTextButton as BitmapTextButton
+except ImportError: # not sure about TGBTButton version
+    from wx.lib.buttons import GenBitmapTextButton as BitmapTextButton
+    
+try:
     import wx.lib.agw.flatnotebook as FN
 except ImportError:
     import wx.lib.flatnotebook as FN
-
+try:
+    from agw import foldpanelbar as fpb
+except ImportError: # if it's not there locally, try the wxPython lib.
+    try:
+        import wx.lib.agw.foldpanelbar as fpb
+    except ImportError:
+        import wx.lib.foldpanelbar as fpb # versions <=2.5.5.1
+        
 import grass.script as grass
 
 import globalvar
 import gselect
 import gcmd
+import colorrules
 from preferences import globalSettings as UserSettings
-from preferences import PreferencesBaseDialog
+from gselect import VectorDBInfo
+
+from nviz_animation import EVT_ANIM_FIN, EVT_ANIM_UPDATE_IDX
 try:
-    from nviz_mapdisp import wxUpdateView, wxUpdateLight, wxUpdateProperties
+    from nviz_mapdisp import wxUpdateView, wxUpdateLight, wxUpdateProperties,\
+                            wxUpdateCPlane
     import wxnviz
 except ImportError:
     pass
+
 from debug import Debug
 
+
+class ScrolledPanel(SP.ScrolledPanel):
+    """!Custom ScrolledPanel to avoid strange behaviour concerning focus"""
+    def __init__(self, parent):
+        SP.ScrolledPanel.__init__(self, parent = parent, id = wx.ID_ANY)
+    def OnChildFocus(self, event):
+        pass
+        
+        
+class NTCValidator(wx.PyValidator):
+    """!validates input in textctrls, taken from wxpython demo"""
+    def __init__(self, flag = None):
+        wx.PyValidator.__init__(self)
+        self.flag = flag
+        self.Bind(wx.EVT_CHAR, self.OnChar)
+
+    def Clone(self):
+        return NTCValidator(self.flag)
+
+    def OnChar(self, event):
+        key = event.GetKeyCode()
+        if key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255:
+            event.Skip()
+            return
+        if self.flag == 'DIGIT_ONLY' and chr(key) in string.digits + '.-':
+            event.Skip()
+            return
+        if not wx.Validator_IsSilent():
+            wx.Bell()
+        # Returning without calling even.Skip eats the event before it
+        # gets to the text control
+        return  
+    
+class NumTextCtrl(wx.TextCtrl):
+    """!Class derived from wx.TextCtrl for numerical values only"""
+    def __init__(self, parent,  **kwargs):
+##        self.precision = kwargs.pop('prec')
+        wx.TextCtrl.__init__(self, parent = parent,
+            validator = NTCValidator(flag = 'DIGIT_ONLY'), **kwargs)
+        
+            
+    def SetValue(self, value):
+        super(NumTextCtrl, self).SetValue( str(value))
+        
+    def GetValue(self):
+        val = super(NumTextCtrl, self).GetValue()
+        if val == '':
+            val = '0'
+        try:
+            return float(val)
+        except ValueError:
+            val = ''.join(''.join(val.split('-')).split('.'))
+            return float(val)
+        
+    def SetRange(self, min, max):
+        pass
+   
+class FloatSlider(wx.Slider):
+    """!Class derived from wx.Slider for floats"""
+    def __init__(self, **kwargs):
+        Debug.msg(1, "FloatSlider.__init__()")
+        wx.Slider.__init__(self, **kwargs)
+        self.coef = 1.
+        #init range
+        self.minValueOrig = 0
+        self.maxValueOrig = 1
+        
+    def SetValue(self, value):
+        value *= self.coef 
+        if abs(value) < 1 and value != 0:
+            while abs(value) < 1:
+                value *= 100
+                self.coef *= 100
+            super(FloatSlider, self).SetRange(self.minValueOrig * self.coef, self.maxValueOrig * self.coef)
+        super(FloatSlider, self).SetValue(value)
+        
+        Debug.msg(4, "FloatSlider.SetValue(): value = %f" % value)
+        
+    def SetRange(self, minValue, maxValue):
+        self.coef = 1.
+        self.minValueOrig = minValue
+        self.maxValueOrig = maxValue
+        if abs(minValue) < 1 or abs(maxValue) < 1:
+            while (abs(minValue) < 1 and minValue != 0) or (abs(maxValue) < 1 and maxValue != 0):
+                minValue *= 100
+                maxValue *= 100
+                self.coef *= 100
+            super(FloatSlider, self).SetValue(super(FloatSlider, self).GetValue() * self.coef)
+        super(FloatSlider, self).SetRange(minValue, maxValue)
+        Debug.msg(4, "FloatSlider.SetRange(): minValue = %f, maxValue = %f" % (minValue, maxValue))
+            
+    def GetValue(self):
+        val = super(FloatSlider, self).GetValue()
+        Debug.msg(4, "FloatSlider.GetValue(): value = %f" % (val/self.coef))
+        return val/self.coef
+        
+        
+class SymbolButton(BitmapTextButton):
+    """!Button with symbol and label."""
+    def __init__(self, parent, usage, label, **kwargs):
+        """!Constructor
+        
+        @param parent parent (usually wx.Panel)
+        @param usage determines usage and picture
+        @param label displayed label
+        """
+        size = (15, 15)
+        buffer = wx.EmptyBitmap(*size)
+        BitmapTextButton.__init__(self, parent = parent, label = " " + label, bitmap = buffer, **kwargs)
+        
+        dc = wx.MemoryDC()
+        dc.SelectObject(buffer)
+        maskColor = wx.Color(255, 255, 255)
+        dc.SetBrush(wx.Brush(maskColor))
+        dc.Clear()
+        
+        if usage == 'record':
+            self.DrawRecord(dc, size)
+        elif usage == 'stop':
+            self.DrawStop(dc, size)
+        elif usage == 'play':
+            self.DrawPlay(dc, size)
+        elif usage == 'pause':
+            self.DrawPause(dc, size)
+
+        buffer.SetMaskColour(maskColor)
+        self.SetBitmapLabel(buffer)
+        dc.SelectObject(wx.NullBitmap)
+        
+    def DrawRecord(self, dc, size):
+        """!Draw record symbol"""
+        dc.SetBrush(wx.Brush(wx.Color(255, 0, 0)))
+        dc.DrawCircle(size[0]/2, size[1] / 2, size[0] / 2)
+        
+    def DrawStop(self, dc, size):
+        """!Draw stop symbol"""
+        dc.SetBrush(wx.Brush(wx.Color(50, 50, 50)))
+        dc.DrawRectangle(0, 0, size[0], size[1])
+        
+    def DrawPlay(self, dc, size):
+        """!Draw play symbol"""
+        dc.SetBrush(wx.Brush(wx.Color(0, 255, 0)))
+        points = (wx.Point(0, 0), wx.Point(0, size[1]), wx.Point(size[0], size[1] / 2))
+        dc.DrawPolygon(points)
+        
+    def DrawPause(self, dc, size):
+        """!Draw pause symbol"""
+        dc.SetBrush(wx.Brush(wx.Color(50, 50, 50)))
+        dc.DrawRectangle(0, 0, 2 * size[0] / 5, size[1])
+        dc.DrawRectangle(3 * size[0] / 5, 0, 2 * size[0] / 5, size[1])
+        
+        
 class NvizToolWindow(FN.FlatNotebook):
     """!Nviz (3D view) tools panel
     """
     def __init__(self, parent, display, id = wx.ID_ANY,
-                 style = globalvar.FNPageStyle, **kwargs):
+                 style = globalvar.FNPageStyle|FN.FNB_NO_X_BUTTON,
+                 **kwargs):
+        Debug.msg(5, "NvizToolWindow.__init__()")
         self.parent     = parent # GMFrame
         self.mapDisplay = display
         self.mapWindow  = display.GetWindow()
         self._display   = self.mapWindow.GetDisplay()
-        
+         
         if globalvar.hasAgw:
             kwargs['agwStyle'] = style
         else:
@@ -73,34 +253,127 @@
         # data page
         self.AddPage(page = self._createDataPage(),
                      text = " %s " % _("Data"))
-
+        
         # appearance page
         self.AddPage(page = self._createAppearancePage(),
                      text = " %s " % _("Appearance"))
+                    
+        # analysis page
+        self.AddPage(page = self._createAnalysisPage(),
+                     text = " %s " % _("Analysis"))
+        # view page
+        self.AddPage(page = self._createAnimationPage(),
+                     text = " %s " % _("Animation"))
         
         self.UpdateSettings()
+        
+        self.mapWindow.SetToolWin(self)
+        
         self.pageChanging = False
+        self.vetoGSelectEvt = False #when setting map, event is invoked
         self.mapWindow.render['quick'] = False
         self.mapWindow.Refresh(False)
         
         # bindings
-        self.Bind(wx.EVT_CLOSE, self.OnClose)
         self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged)
+        self.Bind(wx.EVT_SIZE, self.OnSize)
         
+        self.Bind(EVT_ANIM_FIN, self.OnAnimationFinished)
+        self.Bind(EVT_ANIM_UPDATE_IDX, self.OnAnimationUpdateIndex)
+        
+        Debug.msg(3, "NvizToolWindow.__init__()")
+        
         self.Update()
         wx.CallAfter(self.SetPage, 'view')
-        wx.CallAfter(self.notebookData.SetSelection, 0)
-        wx.CallAfter(self.notebookAppearance.SetSelection, 0)
+        wx.CallAfter(self.UpdateScrolling, (self.foldpanelData, self.foldpanelAppear,
+                                            self.foldpanelAnalysis))       
+        wx.CallAfter(self.SetInitialMaps)
         
+    def SetInitialMaps(self):
+        """!Set initial raster and vector map"""
+        for l_type in ('raster', 'vector', '3d-raster'):
+            selectedLayer = self.mapWindow.GetSelectedLayer()
+            layers = self.mapWindow.Map.GetListOfLayers(l_type = l_type, l_active = True)
+            if selectedLayer in layers:
+                selection = selectedLayer.GetName()
+            else:
+                try:
+                    selection = layers[0].GetName()
+                except:
+                    continue
+            if l_type == 'raster':
+                self.FindWindowById(self.win['surface']['map']).SetValue(selection)
+                self.FindWindowById(self.win['fringe']['map']).SetValue(selection)
+            elif l_type == 'vector':
+                self.FindWindowById(self.win['vector']['map']).SetValue(selection)
+            elif l_type == '3d-raster':
+                self.FindWindowById(self.win['volume']['map']).SetValue(selection)
+               
+    def UpdateState(self, **kwargs):
+        if 'view' in kwargs:
+            self.mapWindow.view = kwargs['view']
+            self.FindWindowById(self.win['view']['position']).data = kwargs['view']
+            self.FindWindowById(self.win['view']['position']).PostDraw()
+        if 'iview' in kwargs:
+            self.mapWindow.iview = kwargs['iview']
+        if 'light' in kwargs:
+            self.mapWindow.light = kwargs['light']  
+            self.FindWindowById(self.win['light']['position']).data = kwargs['light']  
+            self.FindWindowById(self.win['light']['position']).PostDraw()
+        if 'fly' in kwargs:
+            self.mapWindow.fly['exag'] = kwargs['fly']['exag']
+    
+    def LoadSettings(self):
+        """!Load Nviz settings and apply to current session"""
+        view = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'view')) # copy
+        light = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'light')) # copy
+        fly = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'fly')) # copy
+        self.UpdateState(view = view, light = light, fly = fly)
+        self.PostViewEvent(zExag = True)
+        self.PostLightEvent()
+        self.UpdatePage('view')
+        self.UpdatePage('light')
+        
+        self.mapWindow.ReloadLayersData()
+        self.UpdatePage('surface')
+        self.UpdatePage('vector')
+        self.UpdateSettings()
+               
     def OnPageChanged(self, event):
         new = event.GetSelection()
         # self.ChangeSelection(new)
-    
+        
     def PostViewEvent(self, zExag = False):
         """!Change view settings"""
         event = wxUpdateView(zExag = zExag)
         wx.PostEvent(self.mapWindow, event)
-
+        
+    def PostLightEvent(self, refresh = False): 
+        """!Change light settings"""   
+        event = wxUpdateLight(refresh = refresh)
+        wx.PostEvent(self.mapWindow, event)
+        
+    def OnSize(self, event):
+        """!After window is resized, update scrolling"""
+        # workaround to resize captionbars of foldpanelbar
+        wx.CallAfter(self.UpdateScrolling, (self.foldpanelData, self.foldpanelAppear,
+                                            self.foldpanelAnalysis)) 
+        event.Skip()
+           
+    def OnPressCaption(self, event):
+        """!When foldpanel item collapsed/expanded, update scrollbars"""
+        foldpanel = event.GetBar().GetGrandParent().GetParent()
+        wx.CallAfter(self.UpdateScrolling, (foldpanel,))
+        event.Skip()
+        
+    def UpdateScrolling(self, foldpanels):
+        """!Update scrollbars in foldpanel"""
+        for foldpanel in foldpanels:
+            length = foldpanel.GetPanelsLength(collapsed = 0, expanded = 0)
+            # virtual width is set to fixed value to suppress GTK warning
+            foldpanel.GetParent().SetVirtualSize((100, length[2]))
+            foldpanel.GetParent().Layout()
+        
     def _createViewPage(self):
         """!Create view settings page"""
         panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
@@ -112,115 +385,106 @@
         box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
                             label = " %s " % (_("Control View")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 10)
         
         self.win['view'] = {}
         
         # position
         posSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
-        posSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("W")),
-                     pos = (1, 0), flag = wx.ALIGN_CENTER)
-        posSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("N")),
-                     pos = (0, 1), flag = wx.ALIGN_CENTER | wx.ALIGN_BOTTOM)
+        
+        self._createCompass(panel = panel, sizer = posSizer, type = 'view')
+        
         view = ViewPositionWindow(panel, size = (175, 175),
                                   mapwindow = self.mapWindow)
         self.win['view']['position'] = view.GetId()
         posSizer.Add(item = view,
                      pos = (1, 1), flag = wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL)
-        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
         # set initial defaults here (or perhaps in a default values file), not in user settings
+        #todo: consider setting an absolute max at 360 instead of undefined. (leave the default max value at pi)
         self._createControl(panel, data = self.win['view'], name = 'persp',
-                            range = (1,100),
-                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedSpin))
+                            range = (1,180),
+                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedText))
+        
         gridSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Perspective:")),
                       pos = (1, 0), flag = wx.ALIGN_CENTER)
-        gridSizer.Add(item = self.FindWindowById(self.win['view']['persp']['slider']), pos = (2, 0))
-        gridSizer.Add(item = self.FindWindowById(self.win['view']['persp']['spin']), pos = (3, 0),
+        gridSizer.Add(item = self.FindWindowById(self.win['view']['persp']['slider']), pos = (2, 0),
+                      flag = wx.ALIGN_CENTER)
+        gridSizer.Add(item = self.FindWindowById(self.win['view']['persp']['text']), pos = (3, 0),
                       flag = wx.ALIGN_CENTER)        
         
         # twist
         self._createControl(panel, data = self.win['view'], name = 'twist',
                             range = (-180,180),
-                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedSpin))
+                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedText))
         gridSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Twist:")),
                       pos = (1, 1), flag = wx.ALIGN_CENTER)
         gridSizer.Add(item = self.FindWindowById(self.win['view']['twist']['slider']), pos = (2, 1))
-        gridSizer.Add(item = self.FindWindowById(self.win['view']['twist']['spin']), pos = (3, 1),
+        gridSizer.Add(item = self.FindWindowById(self.win['view']['twist']['text']), pos = (3, 1),
                       flag = wx.ALIGN_CENTER)        
         
         # height + z-exag
         self._createControl(panel, data = self.win['view'], name = 'height', sliderHor = False,
                             range = (0, 1),
-                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedSpin))
-        
+                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedText))
         self._createControl(panel, data = self.win['view'], name = 'z-exag', sliderHor = False,
-                            range = (0, 5),
-                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedSpin))
+                            range = (0, 10), floatSlider = True,
+                            bind = (self.OnViewChange, self.OnViewChanged, self.OnViewChangedText))
         self.FindWindowById(self.win['view']['z-exag']['slider']).SetValue(1)
-        self.FindWindowById(self.win['view']['z-exag']['spin']).SetValue(1)
+        self.FindWindowById(self.win['view']['z-exag']['text']).SetValue(1)
         
         heightSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
         heightSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Height:")),
-                      pos = (0, 0), flag = wx.ALIGN_LEFT, span = (1, 2))
+                      pos = (0, 0), flag = wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL, span = (1, 2))
         heightSizer.Add(item = self.FindWindowById(self.win['view']['height']['slider']),
                         flag = wx.ALIGN_RIGHT, pos = (1, 0))
-        heightSizer.Add(item = self.FindWindowById(self.win['view']['height']['spin']),
-                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT | wx.TOP |
-                        wx.BOTTOM | wx.RIGHT, pos = (1, 1))
+        heightSizer.Add(item = self.FindWindowById(self.win['view']['height']['text']),
+                        flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT, pos = (1, 1))
         heightSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Z-exag:")),
-                      pos = (0, 2), flag = wx.ALIGN_LEFT, span = (1, 2))
+                      pos = (0, 2), flag = wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL, span = (1, 2))
         heightSizer.Add(item = self.FindWindowById(self.win['view']['z-exag']['slider']),
                         flag = wx.ALIGN_RIGHT, pos = (1, 2))
-        heightSizer.Add(item = self.FindWindowById(self.win['view']['z-exag']['spin']),
+        heightSizer.Add(item = self.FindWindowById(self.win['view']['z-exag']['text']),
                         flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT | wx.TOP |
                         wx.BOTTOM | wx.RIGHT, pos = (1, 3))
         
-        gridSizer.Add(item = heightSizer, pos = (0, 1), flag = wx.ALIGN_RIGHT)
+        gridSizer.Add(item = heightSizer, pos = (0, 1), flag = wx.ALIGN_CENTER)
         
         # view setup + reset
         viewSizer = wx.BoxSizer(wx.HORIZONTAL)
-        
         viewSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY,
-                                           label = _("Look at:")),
+                                           label = _("Look:")),
                       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,
+        here = wx.ToggleButton(panel, id = wx.ID_ANY, label = _("here"))
+        here.Bind(wx.EVT_TOGGLEBUTTON, self.OnLookAt)
+        here.SetName('here')
+        viewSizer.Add(item = here, flag = wx.TOP|wx.BOTTOM|wx.LEFT|wx.ALIGN_CENTER_VERTICAL,
                       border = 5)
-        
-        reset = wx.Button(panel, id = wx.ID_ANY, label = _("Reset"))
+                    
+        center = wx.Button(panel, id = wx.ID_ANY, label = _("center"))
+        center.Bind(wx.EVT_BUTTON, self.OnLookAt)
+        center.SetName('center')
+        viewSizer.Add(item = center, flag = wx.TOP|wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL,
+                      border = 5)
+                    
+        top = wx.Button(panel, id = wx.ID_ANY, label = _("top"))
+        top.Bind(wx.EVT_BUTTON, self.OnLookAt)
+        top.SetName('top')
+        viewSizer.Add(item = top, flag = wx.TOP|wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL,
+                      border = 5)
+                    
+        reset = wx.Button(panel, id = wx.ID_ANY, label = _("reset"))
         reset.SetToolTipString(_("Reset to default view"))
-        # self.win['reset'] = reset.GetId()
         reset.Bind(wx.EVT_BUTTON, self.OnResetView)
-        
-        viewSizer.Add(item = wx.Size(-1, -1), proportion = 1,
-                      flag = wx.EXPAND)
         viewSizer.Add(item = reset, proportion = 0,
-                      flag = wx.ALL | wx.ALIGN_RIGHT,
+                      flag = wx.TOP|wx.BOTTOM|wx.RIGHT| wx.ALIGN_RIGHT,
                       border = 5)
         
         gridSizer.AddGrowableCol(2)
-        gridSizer.Add(item = viewSizer, pos = (4, 0), span = (1, 2),
+        gridSizer.Add(item = viewSizer, pos = (4, 0), span = (1, 3),
                       flag = wx.EXPAND)
         
         # body
@@ -237,6 +501,7 @@
         gridSizer.AddGrowableCol(0)
         
         # background color
+        self.win['view']['background'] = {}
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
                                          label = _("Background color:")),
                       pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
@@ -245,7 +510,7 @@
                                   colour = UserSettings.Get(group = 'nviz', key = 'view',
                                                             subkey = ['background', 'color']),
                                   size = globalvar.DIALOG_COLOR_SIZE)
-        self.win['view']['bgcolor'] = color.GetId()
+        self.win['view']['background']['color'] = color.GetId()
         color.Bind(csel.EVT_COLOURSELECT, self.OnBgColor)
         gridSizer.Add(item = color, pos = (0, 1))
         
@@ -258,60 +523,280 @@
         panel.SetSizer(pageSizer)
         
         return panel
-
+        
+    def _createAnimationPage(self):
+        """!Create view settings page"""
+        panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
+        panel.SetupScrolling(scroll_x = False)
+        self.page['animation'] = { 'id' : 0,
+                                   'notebook' : self.GetId()}
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Animation")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        hSizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        self.win['anim'] = {}
+        # animation help text
+        help = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                             label = _("Press 'Record' button and start changing the view. "
+                                       "It is recommended to use fly-through mode "
+                                       "(Map Display toolbar) to achieve smooth motion."))
+        self.win['anim']['help'] = help.GetId()
+        hSizer.Add(item = help, proportion = 0)
+        boxSizer.Add(item = hSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 5)
+                     
+        # animation controls
+        hSizer = wx.BoxSizer(wx.HORIZONTAL)
+        record = SymbolButton(parent = panel, id = wx.ID_ANY,
+                                    usage = "record", label = _("Record"))
+        play = SymbolButton(parent = panel, id = wx.ID_ANY,
+                                  usage = "play", label = _("Play"))
+        pause = SymbolButton(parent = panel, id = wx.ID_ANY,
+                                   usage = "pause", label = _("Pause"))
+        stop = SymbolButton(parent = panel, id = wx.ID_ANY,
+                                  usage = "stop", label = _("Stop"))
+        
+        self.win['anim']['record'] = record.GetId()
+        self.win['anim']['play'] = play.GetId()
+        self.win['anim']['pause'] = pause.GetId()
+        self.win['anim']['stop'] = stop.GetId()
+                            
+        self._createControl(panel, data = self.win['anim'], name = 'frameIndex',
+                            range = (0, 1), floatSlider = False,
+                            bind = (self.OnFrameIndex, None, self.OnFrameIndexText))
+        frameSlider = self.FindWindowById(self.win['anim']['frameIndex']['slider'])
+        frameText = self.FindWindowById(self.win['anim']['frameIndex']['text'])
+        infoLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Total number of frames :"))
+        info = wx.StaticText(parent = panel, id = wx.ID_ANY)
+        self.win['anim']['info'] = info.GetId()
+        
+        fpsLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("Frame rate (FPS):"))
+        fps = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = UserSettings.Get(group = 'nviz', key = 'animation', subkey = 'fps'),
+                           min = 1,
+                           max = 50)
+        self.win['anim']['fps'] = fps.GetId()
+        fps.SetToolTipString(_("Frames are recorded with given frequency (FPS). "))
+                            
+        record.Bind(wx.EVT_BUTTON, self.OnRecord)
+        play.Bind(wx.EVT_BUTTON, self.OnPlay)
+        stop.Bind(wx.EVT_BUTTON, self.OnStop)
+        pause.Bind(wx.EVT_BUTTON, self.OnPause)
+        fps.Bind(wx.EVT_SPINCTRL, self.OnFPS)
+        
+        hSizer.Add(item = record, proportion = 0)
+        hSizer.Add(item = play, proportion = 0)
+        hSizer.Add(item = pause, proportion = 0)
+        hSizer.Add(item = stop, proportion = 0)
+        boxSizer.Add(item = hSizer, proportion = 0,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        
+        sliderBox = wx.BoxSizer(wx.HORIZONTAL)
+        sliderBox.Add(item = frameSlider, proportion = 1, border = 5, flag = wx.EXPAND | wx.RIGHT)
+        sliderBox.Add(item = frameText, proportion = 0, border = 5, flag = wx.EXPAND| wx.RIGHT | wx.LEFT)
+        boxSizer.Add(item = sliderBox, proportion = 0, flag = wx.EXPAND)
+        
+        # total number of frames
+        hSizer = wx.BoxSizer(wx.HORIZONTAL)
+        hSizer.Add(item = infoLabel, proportion = 0, flag = wx.RIGHT, border = 5)
+        hSizer.Add(item = info, proportion = 0, flag = wx.LEFT, border = 5)
+        
+        boxSizer.Add(item = hSizer, proportion = 0,
+                     flag = wx.ALL | wx.EXPAND, border = 5)
+                     
+        # frames per second
+        hSizer = wx.BoxSizer(wx.HORIZONTAL)
+        hSizer.Add(item = fpsLabel, proportion = 0, flag = wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, border = 5)
+        hSizer.Add(item = fps, proportion = 0, flag = wx.LEFT | wx.ALIGN_CENTER_VERTICAL, border = 5)
+        
+        boxSizer.Add(item = hSizer, proportion = 0,
+                     flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 5)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+                      
+        # save animation
+        self.win['anim']['save'] = {}
+        self.win['anim']['save']['image'] = {}
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Save image sequence")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        vSizer = wx.BoxSizer(wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 10)
+        
+        pwd = os.getcwd()
+        dir = filebrowse.DirBrowseButton(parent = panel, id = wx.ID_ANY,
+                                         labelText = _("Choose a directory:"),
+                                         dialogTitle = _("Choose a directory for images"),
+                                         buttonText = _('Browse'),
+                                         startDirectory = pwd)
+        dir.SetValue(pwd)
+        prefixLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("File prefix:"))
+        prefixCtrl = wx.TextCtrl(parent = panel, id = wx.ID_ANY, size = (100, -1),
+                                 value = UserSettings.Get(group = 'nviz',
+                                                          key = 'animation', subkey = 'prefix'))
+        prefixCtrl.SetToolTipString(_("Generated files names will look like this: prefix_1.ppm, prefix_2.ppm, ..."))
+        fileTypeLabel = wx.StaticText(parent = panel, id = wx.ID_ANY, label = _("File format:"))
+        fileTypeCtrl = wx.Choice(parent = panel, id = wx.ID_ANY, choices = ["PPM", "TIF"])
+        
+        save = wx.Button(parent = panel, id = wx.ID_ANY,
+                         label = "Save")
+                         
+        self.win['anim']['save']['image']['dir'] = dir.GetId()
+        self.win['anim']['save']['image']['prefix'] = prefixCtrl.GetId()
+        self.win['anim']['save']['image']['format'] = fileTypeCtrl.GetId()
+        self.win['anim']['save']['image']['confirm'] = save.GetId()
+        
+        boxSizer.Add(item = dir, proportion = 0, flag = wx.ALL | wx.EXPAND, border = 3)
+        
+        gridSizer.Add(item = prefixLabel, pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+        gridSizer.Add(item = prefixCtrl, pos = (0, 1), flag = wx.EXPAND )
+        gridSizer.Add(item = fileTypeLabel, pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
+        gridSizer.Add(item = fileTypeCtrl, pos = (1, 1), flag = wx.EXPAND )
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 5)
+        boxSizer.Add(item = save, proportion = 0, flag = wx.ALL | wx.ALIGN_RIGHT, border = 5)
+        
+        save.Bind(wx.EVT_BUTTON, self.OnSaveAnimation)
+        
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 
+                      border = 3)
+        
+        panel.SetSizer(pageSizer)
+        
+        return panel
+        
     def _createDataPage(self):
         """!Create data (surface, vector, volume) settings page"""
-        if globalvar.hasAgw:
-            self.notebookData = FN.FlatNotebook(parent = self, id = wx.ID_ANY,
-                                                agwStyle = globalvar.FNPageDStyle)
-        else:
-            self.notebookData = FN.FlatNotebook(parent = self, id = wx.ID_ANY,
-                                                style = globalvar.FNPageDStyle)
+
+        self.mainPanelData = ScrolledPanel(parent = self)
+        self.mainPanelData.SetupScrolling(scroll_x = False)
+##        style = fpb.CaptionBarStyle()
+##        style.SetCaptionStyle(fpb.CAPTIONBAR_FILLED_RECTANGLE)
+##        style.SetFirstColour(wx.Color(250,250,250))
+        try:# wxpython <= 2.8.10
+            self.foldpanelData = fpb.FoldPanelBar(parent = self.mainPanelData, id = wx.ID_ANY,
+                                                  style = fpb.FPB_DEFAULT_STYLE,
+                                                  extraStyle = fpb.FPB_SINGLE_FOLD)
+        except:
+            try:# wxpython >= 2.8.11
+                self.foldpanelData = fpb.FoldPanelBar(parent = self.mainPanelData, id = wx.ID_ANY,                               
+                                                      agwStyle = fpb.FPB_SINGLE_FOLD)
+            except: # to be sure
+                self.foldpanelData = fpb.FoldPanelBar(parent = self.mainPanelData, id = wx.ID_ANY,                               
+                                                      style = fpb.FPB_SINGLE_FOLD)
+            
+                     
+        self.foldpanelData.Bind(fpb.EVT_CAPTIONBAR, self.OnPressCaption)
+
+
         
         # surface page
-        self.notebookData.AddPage(page = self._createSurfacePage(),
-                                  text = " %s " % _("Surface"))
-        self.EnablePage('surface', False)
+        self.surfacePanel = self.foldpanelData.AddFoldPanel(_("Surface"), collapsed = False)
+        self.foldpanelData.AddFoldPanelWindow(self.surfacePanel, 
+            window = self._createSurfacePage(parent = self.surfacePanel), flags = fpb.FPB_ALIGN_WIDTH)
+        self.EnablePage("surface", enabled = False)
         
+        # constant page
+        constantPanel = self.foldpanelData.AddFoldPanel(_("Constant surface"), collapsed = True)
+        self.foldpanelData.AddFoldPanelWindow(constantPanel,
+            window = self._createConstantPage(parent = constantPanel), flags = fpb.FPB_ALIGN_WIDTH)
+        self.EnablePage("constant", enabled = False)
         # vector page
-        self.notebookData.AddPage(page = self._createVectorPage(),
-                                  text = " %s " % _("Vector"))
-        self.EnablePage('vector', False)
+        vectorPanel = self.foldpanelData.AddFoldPanel(_("Vector"), collapsed = True)
+        self.foldpanelData.AddFoldPanelWindow(vectorPanel, 
+            window = self._createVectorPage(parent = vectorPanel), flags = fpb.FPB_ALIGN_WIDTH)
+        self.EnablePage("vector", enabled = False)
         
         # volume page
-        self.notebookData.AddPage(page = self._createVolumePage(),
-                                  text = " %s " % _("Volume"))
-        self.EnablePage('volume', False)
+        volumePanel = self.foldpanelData.AddFoldPanel(_("Volume"), collapsed = True)
+        self.foldpanelData.AddFoldPanelWindow(volumePanel,
+            window = self._createVolumePage(parent = volumePanel), flags = fpb.FPB_ALIGN_WIDTH)
+        self.EnablePage("volume", enabled = False)
         
-        return self.notebookData
-    
+##        self.foldpanelData.ApplyCaptionStyleAll(style)
+        
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        sizer.Add(self.foldpanelData, proportion = 1, flag = wx.EXPAND)
+        self.mainPanelData.SetSizer(sizer)
+        self.mainPanelData.Layout()
+        self.mainPanelData.Fit()
+        
+        return self.mainPanelData
+        
+        
     def _createAppearancePage(self):
         """!Create data (surface, vector, volume) settings page"""
-        if globalvar.hasAgw:
-            self.notebookAppearance = FN.FlatNotebook(parent = self, id = wx.ID_ANY,
-                                                      agwStyle = globalvar.FNPageDStyle)
-        else:
-            self.notebookAppearance = FN.FlatNotebook(parent = self, id = wx.ID_ANY,
-                                                      style = globalvar.FNPageDStyle)
+        self.mainPanelAppear = ScrolledPanel(parent = self)
+        self.mainPanelAppear.SetupScrolling(scroll_x = False)
         
+        try:# wxpython <= 2.8.10
+            self.foldpanelAppear = fpb.FoldPanelBar(parent = self.mainPanelAppear, id = wx.ID_ANY,
+                                                  style = fpb.FPB_DEFAULT_STYLE,
+                                                  extraStyle = fpb.FPB_SINGLE_FOLD)
+        except:
+            try:# wxpython >= 2.8.11
+                self.foldpanelAppear = fpb.FoldPanelBar(parent = self.mainPanelAppear, id = wx.ID_ANY,                               
+                                                      agwStyle = fpb.FPB_SINGLE_FOLD)
+            except: # to be sure
+                self.foldpanelAppear = fpb.FoldPanelBar(parent = self.mainPanelAppear, id = wx.ID_ANY,                               
+                                                      style = fpb.FPB_SINGLE_FOLD)
+            
+        self.foldpanelAppear.Bind(fpb.EVT_CAPTIONBAR, self.OnPressCaption)
         # light page
-        self.notebookAppearance.AddPage(page = self._createLightPage(),
-                                        text = " %s " % _("Lighting"))
+        lightPanel = self.foldpanelAppear.AddFoldPanel(_("Lighting"), collapsed = False)
+        self.foldpanelAppear.AddFoldPanelWindow(lightPanel, 
+            window = self._createLightPage(parent = lightPanel), flags = fpb.FPB_ALIGN_WIDTH)
     
         # fringe page
-        self.notebookAppearance.AddPage(page = self._createFringePage(),
-                                        text = " %s " % _("Fringe"))
+        fringePanel = self.foldpanelAppear.AddFoldPanel(_("Fringe"), collapsed = True)
+        self.foldpanelAppear.AddFoldPanelWindow(fringePanel, 
+            window = self._createFringePage(parent = fringePanel), flags = fpb.FPB_ALIGN_WIDTH)
+        
         self.EnablePage('fringe', False)
-
-        return self.notebookAppearance
+        
+        # decoration page
+        decorationPanel = self.foldpanelAppear.AddFoldPanel(_("Decorations"), collapsed = True)
+        self.foldpanelAppear.AddFoldPanelWindow(decorationPanel, 
+            window = self._createDecorationPage(parent = decorationPanel), flags = fpb.FPB_ALIGN_WIDTH)
+        
+        
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        sizer.Add(self.foldpanelAppear, proportion = 1, flag = wx.EXPAND)
+        self.mainPanelAppear.SetSizer(sizer)
+        self.mainPanelAppear.Layout()
+        self.mainPanelAppear.Fit()
+        return self.mainPanelAppear
     
-    def _createSurfacePage(self):
+    def _createAnalysisPage(self):
+        """!Create data analysis (cutting planes, ...) page"""
+        self.mainPanelAnalysis = ScrolledPanel(parent = self)
+        self.mainPanelAnalysis.SetupScrolling(scroll_x = False)
+        self.foldpanelAnalysis = fpb.FoldPanelBar(parent = self.mainPanelAnalysis, id = wx.ID_ANY,
+                                                  style = fpb.FPB_SINGLE_FOLD)
+        self.foldpanelAnalysis.Bind(fpb.EVT_CAPTIONBAR, self.OnPressCaption)
+        # cutting planes page
+        cplanePanel = self.foldpanelAnalysis.AddFoldPanel(_("Cutting planes"), collapsed = False)
+        self.foldpanelAnalysis.AddFoldPanelWindow(cplanePanel, 
+            window = self._createCPlanePage(parent = cplanePanel), flags = fpb.FPB_ALIGN_WIDTH)
+        
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        sizer.Add(self.foldpanelAnalysis, proportion = 1, flag = wx.EXPAND)
+        self.mainPanelAnalysis.SetSizer(sizer)
+        self.mainPanelAnalysis.Layout()
+        self.mainPanelAnalysis.Fit()
+        return self.mainPanelAnalysis
+        
+    def _createSurfacePage(self, parent):
         """!Create view settings page"""
-        panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
-        panel.SetupScrolling(scroll_x = False)
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
         self.page['surface'] = { 'id' : 0,
-                                 'panel' : panel.GetId(),
-                                 'notebook' : self.notebookData.GetId() }
+                                 'notebook' : self.foldpanelData.GetId() }
         pageSizer = wx.BoxSizer(wx.VERTICAL)
         
         self.win['surface'] = {}
@@ -337,99 +822,14 @@
                       border = 3)
         
         #
-        # surface attributes
-        #
-        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
-                            label = " %s " % (_("Surface attributes")))
-        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
-        
-        # type 
-        self.win['surface']['attr'] = {}
-        row = 0
-        for code, attrb in (('topo', _("Topography")),
-                           ('color', _("Color")),
-                           ('mask', _("Mask")),
-                           ('transp', _("Transparency")),
-                           ('shine', _("Shininess")),
-                           ('emit', _("Emission"))):
-            self.win['surface'][code] = {} 
-            gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                             label = attrb + ':'),
-                          pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
-            use = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
-                             choices = [_("map")])
-            
-            if code not in ('topo', 'color', 'shine'):
-                use.Insert(item = _("unset"), pos = 0)
-                self.win['surface'][code]['required'] = False
-            else:
-                self.win['surface'][code]['required'] = True
-            if code != 'mask':
-                use.Append(item = _('constant'))
-            self.win['surface'][code]['use'] = use.GetId()
-            use.Bind(wx.EVT_CHOICE, self.OnMapObjUse)
-            gridSizer.Add(item = use, flag = wx.ALIGN_CENTER_VERTICAL,
-                          pos = (row, 1))
-            
-            map = gselect.Select(parent = panel, id = wx.ID_ANY,
-                                 # size = globalvar.DIALOG_GSELECT_SIZE,
-                                 size = (200, -1),
-                                 type = "raster")
-            self.win['surface'][code]['map'] = map.GetId() - 1 # FIXME
-            map.Bind(wx.EVT_TEXT, self.OnSurfaceMap)
-            # changing map topography not allowed
-            if code == 'topo':
-                map.Enable(False)
-            gridSizer.Add(item = map, flag = wx.ALIGN_CENTER_VERTICAL,
-                          pos = (row, 2))
-            
-            if code == 'color':
-                value = csel.ColourSelect(panel, id = wx.ID_ANY,
-                                          colour = (0,0,0),
-                                          size = globalvar.DIALOG_COLOR_SIZE)
-                value.Bind(csel.EVT_COLOURSELECT, self.OnSurfaceMap)
-            elif code == 'mask':
-                value = None
-            else:
-                value = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                                    initial = 0)
-                if code == 'topo':
-                    value.SetRange(minVal = -1e9, maxVal = 1e9)
-                elif code in ('shine', 'transp', 'emit'):
-                    value.SetRange(minVal = 0, maxVal = 255)
-                else:
-                    value.SetRange(minVal = 0, maxVal = 100)
-                value.Bind(wx.EVT_TEXT, self.OnSurfaceMap)
-            
-            if value:
-                self.win['surface'][code]['const'] = value.GetId()
-                value.Enable(False)
-                gridSizer.Add(item = value, flag = wx.ALIGN_CENTER_VERTICAL,
-                              pos = (row, 3))
-            else:
-                self.win['surface'][code]['const'] = None
-            
-            self.SetMapObjUseMap(nvizType = 'surface',
-                                 attrb = code) # -> enable map / disable constant
-                
-            row += 1
-        
-        boxSizer.Add(item = gridSizer, proportion = 1,
-                  flag = wx.ALL | wx.EXPAND, border = 3)
-        pageSizer.Add(item = boxSizer, proportion = 0,
-                      flag = wx.EXPAND | wx.ALL,
-                      border = 3)
-        
-        #
         # draw
         #
         self.win['surface']['draw'] = {}
         box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
                             label = " %s " % (_("Draw")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
-        gridSizer.AddGrowableCol(6)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(3)
         
         # mode
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
@@ -439,103 +839,88 @@
                           choices = [_("coarse"),
                                      _("fine"),
                                      _("both")])
-        mode.SetSelection(0)
         mode.SetName("selection")
         mode.Bind(wx.EVT_CHOICE, self.OnSurfaceMode)
         self.win['surface']['draw']['mode'] = mode.GetId()
-        gridSizer.Add(item = mode, flag = wx.ALIGN_CENTER_VERTICAL,
-                      pos = (0, 1))
+        gridSizer.Add(item = mode, flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
+                      pos = (0, 1),span = (1, 2))
         
         # shading
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
                                          label = _("Shading:")),
-                      pos = (0, 2), flag = wx.ALIGN_CENTER_VERTICAL)
-        shade = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
+                      pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
+        shade = wx.Choice (parent = panel, id = wx.ID_ANY, size = (-1, -1),
                            choices = [_("flat"),
                                       _("gouraud")])
         shade.SetName("selection")
         self.win['surface']['draw']['shading'] = shade.GetId()
         shade.Bind(wx.EVT_CHOICE, self.OnSurfaceMode)
         gridSizer.Add(item = shade, flag = wx.ALIGN_CENTER_VERTICAL,
-                      pos = (0, 3))
+                      pos = (0, 4))
         
         # set to all
         all = wx.Button(panel, id = wx.ID_ANY, label = _("Set to all"))
         all.SetToolTipString(_("Use draw settings for all loaded surfaces"))
         all.Bind(wx.EVT_BUTTON, self.OnSurfaceModeAll)
         gridSizer.Add(item = all, flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
-                      pos = (0, 4), span = (1,2), border = 3 )
+                      pos = (3, 4))
+        self.win['surface']['all'] = all.GetId()
         
         # resolution coarse
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Coarse:")),
-                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
-        resSizer = wx.BoxSizer(wx.HORIZONTAL)
-        resSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                        label = _("res.")),
-                     flag = wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 
-                     border = 3)
+                                         label = _("Coarse mode:")),
+                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                        label = _("resolution:")),
+                     pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL)
         resC = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
                            initial = 6,
                            min = 1,
                            max = 100)
         resC.SetName("value")
-        resC.SetValue(6)
-        
         self.win['surface']['draw']['res-coarse'] = resC.GetId()
         resC.Bind(wx.EVT_SPINCTRL, self.OnSurfaceResolution)
-        resSizer.Add(item = resC, flag = wx.ALL | wx.ALIGN_LEFT | 
-                      wx.ALIGN_CENTER_VERTICAL, border = 3)
-        gridSizer.Add(item = resSizer, pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = resC, pos = (2, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
         
         # Coarse style
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("style")),
-                      pos = (1, 2), flag = wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
+                                         label = _("style:")),
+                      pos = (3, 1), flag = wx.ALIGN_CENTER_VERTICAL)
         style = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
                           choices = [_("wire"),
                                      _("surface")])
         style.SetName("selection")
         self.win['surface']['draw']['style'] = style.GetId()
         style.Bind(wx.EVT_CHOICE, self.OnSurfaceMode)
-        gridSizer.Add(item = style, flag = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL,
-                      pos = (1, 3))
+        gridSizer.Add(item = style, flag = wx.ALIGN_CENTER_VERTICAL,
+                      pos = (3, 2))
         
         # color
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("wire color")),
-                      pos = (1, 4), flag = wx.ALIGN_CENTER_VERTICAL | 
-                      wx.ALIGN_RIGHT | wx.LEFT, border = 3)
         color = csel.ColourSelect(panel, id = wx.ID_ANY,
                                   size = globalvar.DIALOG_COLOR_SIZE)
-        color.SetColour((136,136,136))
         color.SetName("colour")
         color.Bind(csel.EVT_COLOURSELECT, self.OnSurfaceWireColor)
+        color.SetToolTipString(_("Change wire color"))
         self.win['surface']['draw']['wire-color'] = color.GetId()
         gridSizer.Add(item = color, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT,
-                      pos = (1, 5))
+                      pos = (3, 3))
         
         # resolution fine
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Fine:")),
-                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+                                         label = _("Fine mode:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
         
-        resSizer = wx.BoxSizer(wx.HORIZONTAL)
-        resSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                        label = _("res.")),
-                     flag = wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 
-                     border = 3)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                        label = _("resolution:")),
+                     pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
         resF = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
                            initial = 3,
                            min = 1,
                            max = 100)
         resF.SetName("value")
-        resF.SetValue(3)
         self.win['surface']['draw']['res-fine'] = resF.GetId()
         resF.Bind(wx.EVT_SPINCTRL, self.OnSurfaceResolution)
-        resSizer.Add(item = resF, flag = wx.ALL | wx.ALIGN_LEFT | 
-                      wx.ALIGN_CENTER_VERTICAL, border = 3)
-        gridSizer.Add(item = resSizer, pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = resF, pos = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
         
         boxSizer.Add(item = gridSizer, proportion = 1,
                   flag = wx.ALL | wx.EXPAND, border = 3)
@@ -544,33 +929,80 @@
                       border = 3)
         
         #
-        # mask
+        # surface attributes
         #
         box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
-                            label = " %s " % (_("Mask")))
+                            label = " %s " % (_("Surface attributes")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(2)
         
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Mask zeros:")),
-                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        elev = wx.CheckBox(parent = panel, id = wx.ID_ANY,
-                           label = _("by elevation"))
-        elev.Enable(False) # TODO: not implemented yet
-        gridSizer.Add(item = elev, pos = (0, 1))
-        
-        color = wx.CheckBox(parent = panel, id = wx.ID_ANY,
-                           label = _("by color"))
-        color.Enable(False) # TODO: not implemented yet
-        gridSizer.Add(item = color, pos = (0, 2))
-        
-        boxSizer.Add(item = gridSizer, proportion = 1,
+        # type 
+        self.win['surface']['attr'] = {}
+        row = 0
+        for code, attrb in (('color', _("Color")),
+                           ('mask', _("Mask")),
+                           ('transp', _("Transparency")),
+                           ('shine', _("Shininess"))):
+            self.win['surface'][code] = {} 
+            gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                             label = attrb + ':'),
+                          pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+            use = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
+                             choices = [_("map")])
+            
+            if code not in ('color', 'shine'):
+                use.Insert(item = _("unset"), pos = 0)
+                self.win['surface'][code]['required'] = False
+            else:
+                self.win['surface'][code]['required'] = True
+            if code != 'mask':
+                use.Append(item = _('constant'))
+            self.win['surface'][code]['use'] = use.GetId()
+            use.Bind(wx.EVT_CHOICE, self.OnMapObjUse)
+            gridSizer.Add(item = use, flag = wx.ALIGN_CENTER_VERTICAL,
+                          pos = (row, 1))
+            
+            map = gselect.Select(parent = panel, id = wx.ID_ANY,
+                                 # size = globalvar.DIALOG_GSELECT_SIZE,
+                                 size = (-1, -1),
+                                 type = "raster")
+            self.win['surface'][code]['map'] = map.GetId() - 1 # FIXME
+            map.Bind(wx.EVT_TEXT, self.OnSurfaceMap)
+            gridSizer.Add(item = map, flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND,
+                          pos = (row, 2))
+            
+            if code == 'color':
+                color = UserSettings.Get(group = 'nviz', key = 'surface', subkey = ['color', 'value'])
+                value = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                          colour = color,
+                                          size = globalvar.DIALOG_COLOR_SIZE)
+                value.Bind(csel.EVT_COLOURSELECT, self.OnSurfaceMap)
+            elif code == 'mask':
+                value = None
+            else:
+                value = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                                    initial = 0)
+                value.SetRange(minVal = 0, maxVal = 100)
+                value.Bind(wx.EVT_TEXT, self.OnSurfaceMap)
+            
+            if value:
+                self.win['surface'][code]['const'] = value.GetId()
+                value.Enable(False)
+                gridSizer.Add(item = value, flag = wx.ALIGN_CENTER_VERTICAL,
+                              pos = (row, 3))
+            else:
+                self.win['surface'][code]['const'] = None
+            
+            self.SetMapObjUseMap(nvizType = 'surface',
+                                 attrb = code) # -> enable map / disable constant
+                
+            row += 1
+        boxSizer.Add(item = gridSizer, proportion = 0,
                   flag = wx.ALL | wx.EXPAND, border = 3)
         pageSizer.Add(item = boxSizer, proportion = 0,
-                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      flag = wx.EXPAND | wx.ALL,
                       border = 3)
-        
         #
         # position
         #
@@ -578,49 +1010,314 @@
         box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
                             label = " %s " % (_("Position")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(3)
         
         # position
         self._createControl(panel, data = self.win['surface'], name = 'position',
-                            range = (-10000, 10000),
-                            bind = (self.OnSurfacePosition, self.OnSurfacePosition, self.OnSurfacePosition))
+                            range = (-10000, 10000), floatSlider = True,
+                            bind = (self.OnSurfacePosition, self.OnSurfacePositionChanged, self.OnSurfacePositionText))
         
         axis = wx.Choice (parent = panel, id = wx.ID_ANY, size = (75, -1),
                           choices = ["X",
                                      "Y",
                                      "Z"])
+                                    
+        reset = wx.Button(panel, id = wx.ID_ANY, label = _("Reset"))
+        reset.SetToolTipString(_("Reset to default position"))
+        reset.Bind(wx.EVT_BUTTON, self.OnResetSurfacePosition)
+        self.win['surface']['position']['reset'] = reset.GetId()
         
         self.win['surface']['position']['axis'] = axis.GetId()
         axis.SetSelection(0)
         axis.Bind(wx.EVT_CHOICE, self.OnSurfaceAxis)
         
         pslide = self.FindWindowById(self.win['surface']['position']['slider'])
-        pspin = self.FindWindowById(self.win['surface']['position']['spin'])
+        ptext = self.FindWindowById(self.win['surface']['position']['text'])
+        ptext.SetValue('0')
         
         gridSizer.Add(item = axis, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
         gridSizer.Add(item = pslide, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 1))
-        gridSizer.Add(item = pspin, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 2))
+        gridSizer.Add(item = ptext, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 2))
+        gridSizer.Add(item = reset, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, pos = (0, 3))
         
         boxSizer.Add(item = gridSizer, proportion = 1,
                   flag = wx.ALL | wx.EXPAND, border = 3)
         box.SetSizer(boxSizer)
         box.Layout()
         
-        pageSizer.Add(item = boxSizer, proportion = 0,
+        pageSizer.Add(item = boxSizer, proportion = 1,
                       flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
                       border = 3)
+        #
+        # mask
+        #
+##        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+##                            label = " %s " % (_("Mask")))
+##        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+##        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+##        
+##        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+##                                         label = _("Mask zeros:")),
+##                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+##        
+##        elev = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+##                           label = _("by elevation"))
+##        elev.Enable(False) # TODO: not implemented yet
+##        gridSizer.Add(item = elev, pos = (0, 1))
+##        
+##        color = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+##                           label = _("by color"))
+##        color.Enable(False) # TODO: not implemented yet
+##        gridSizer.Add(item = color, pos = (0, 2))
+##        
+##        boxSizer.Add(item = gridSizer, proportion = 1,
+##                  flag = wx.ALL | wx.EXPAND, border = 3)
+##        pageSizer.Add(item = boxSizer, proportion = 0,
+##                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+##                      border = 3)
         
+        
         panel.SetSizer(pageSizer)
+
+        panel.Layout()
+        panel.Fit()
         
         return panel
+    def _createCPlanePage(self, parent):
+        """!Create cutting planes page"""  
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        self.page['cplane'] = { 'id' : 4, 
+                                'notebook' : self.foldpanelData.GetId() }
+        self.win['cplane'] = {}
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Cutting planes")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        horSizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        # planes
+        horSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Active cutting plane:")),
+                     flag = wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+        choice = wx.Choice(parent = panel, id = wx.ID_ANY, choices = [])
+        self.win['cplane']['planes'] = choice.GetId()
+        choice.Bind(wx.EVT_CHOICE, self.OnCPlaneSelection)
+        horSizer.Add(item = choice, flag = wx.ALL, border = 5)
+        
+        # shading
+        horSizer.Add(item = wx.Size(-1, -1), proportion = 1)
+        horSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Shading:")),
+                     flag = wx.ALIGN_CENTER_VERTICAL|wx.ALL, border = 5)
+        choices = [_("clear"),
+                   _("top color"),
+                   _("bottom color"),
+                   _("blend"),
+                   _("shaded")]
+        choice = wx.Choice(parent = panel, id = wx.ID_ANY, choices = choices)
+        self.win['cplane']['shading'] = choice.GetId()
+        choice.Bind(wx.EVT_CHOICE, self.OnCPlaneShading)
+        horSizer.Add(item = choice, flag = wx.ALL, border = 5)
+        boxSizer.Add(item = horSizer, flag = wx.EXPAND)
+        
+        gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        
+        # cutting plane horizontal x position
+        self.win['cplane']['position'] = {}
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Horizontal X:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        self._createControl(panel, data = self.win['cplane']['position'], name = 'x', size = 250,
+                            range = (-1000, 1000), sliderHor = True, floatSlider = True,
+                            bind = (self.OnCPlaneChanging, self.OnCPlaneChangeDone, self.OnCPlaneChangeText))
+        self.FindWindowById(self.win['cplane']['position']['x']['slider']).SetValue(0)
+        self.FindWindowById(self.win['cplane']['position']['x']['text']).SetValue(0)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['position']['x']['slider']),
+                      pos = (0, 1),  flag = wx.EXPAND|wx.ALIGN_RIGHT)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['position']['x']['text']),
+                      pos = (0, 2),
+                      flag = wx.ALIGN_CENTER)   
+        
+        # cutting plane horizontal y position
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Horizontal Y:")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        self._createControl(panel, data = self.win['cplane']['position'], name = 'y', size = 250,
+                            range = (-1000, 1000), sliderHor = True, floatSlider = True,
+                            bind = (self.OnCPlaneChanging, self.OnCPlaneChangeDone, self.OnCPlaneChangeText))
+        self.FindWindowById(self.win['cplane']['position']['y']['slider']).SetValue(0)
+        self.FindWindowById(self.win['cplane']['position']['y']['text']).SetValue(0)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['position']['y']['slider']),
+                      pos = (1, 1),  flag = wx.EXPAND|wx.ALIGN_RIGHT)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['position']['y']['text']),
+                      pos = (1, 2),
+                      flag = wx.ALIGN_CENTER)                         
+        
+        # cutting plane rotation
+        self.win['cplane']['rotation'] = {}
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Rotation:")),
+                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        self._createControl(panel, data = self.win['cplane']['rotation'], name = 'rot', size = 250,
+                            range = (0, 360), sliderHor = True,
+                            bind = (self.OnCPlaneChanging, self.OnCPlaneChangeDone, self.OnCPlaneChangeText))
+        self.FindWindowById(self.win['cplane']['rotation']['rot']['slider']).SetValue(0)
+        self.FindWindowById(self.win['cplane']['rotation']['rot']['text']).SetValue(0)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['rotation']['rot']['slider']),
+                      pos = (2, 1),  flag = wx.EXPAND|wx.ALIGN_RIGHT)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['rotation']['rot']['text']),
+                      pos = (2, 2),
+                      flag = wx.ALIGN_CENTER)
 
-    def _createVectorPage(self):
+        # cutting plane tilt        
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Tilt:")),
+                      pos = (3, 0), flag = wx.ALIGN_CENTER_VERTICAL)                    
+        self._createControl(panel, data = self.win['cplane']['rotation'], name = 'tilt', size = 250,
+                            range = (0, 360), sliderHor = True,
+                            bind = (self.OnCPlaneChanging, self.OnCPlaneChangeDone, self.OnCPlaneChangeText))
+        self.FindWindowById(self.win['cplane']['rotation']['tilt']['slider']).SetValue(0)
+        self.FindWindowById(self.win['cplane']['rotation']['tilt']['text']).SetValue(0)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['rotation']['tilt']['slider']),
+                      pos = (3, 1),  flag = wx.EXPAND|wx.ALIGN_RIGHT)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['rotation']['tilt']['text']),
+                      pos = (3, 2),
+                      flag = wx.ALIGN_CENTER)          
+        
+        # cutting pland height
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Height:")),
+                      pos = (4, 0), flag = wx.ALIGN_CENTER_VERTICAL)        
+        self._createControl(panel, data = self.win['cplane']['position'], name = 'z', size = 250,
+                            range = (-1000, 1000), sliderHor = True,
+                            bind = (self.OnCPlaneChanging, self.OnCPlaneChangeDone, self.OnCPlaneChangeText))
+        self.FindWindowById(self.win['cplane']['position']['z']['slider']).SetValue(0)
+        self.FindWindowById(self.win['cplane']['position']['z']['text']).SetValue(0)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['position']['z']['slider']),
+                      pos = (4, 1),  flag = wx.EXPAND|wx.ALIGN_RIGHT)
+        gridSizer.Add(item = self.FindWindowById(self.win['cplane']['position']['z']['text']),
+                      pos = (4, 2),
+                      flag = wx.ALIGN_CENTER)
+        
+        boxSizer.Add(gridSizer, proportion = 0, flag = wx.EXPAND|wx.ALL, border = 5)
+                    
+        horSizer = wx.BoxSizer(wx.HORIZONTAL)
+        horSizer.Add(item = wx.Size(-1, -1), proportion = 1, flag = wx.ALL, border = 5)  
+        # reset
+        reset = wx.Button(parent = panel, id = wx.ID_ANY, label = _("Reset"))
+        self.win['cplane']['reset'] = reset.GetId()
+        reset.Bind(wx.EVT_BUTTON, self.OnCPlaneReset)
+        horSizer.Add(item = reset, flag = wx.ALL, border = 5)
+        boxSizer.Add(horSizer, proportion = 0, flag = wx.EXPAND)            
+        
+        
+        pageSizer.Add(boxSizer, proportion = 0, flag = wx.EXPAND)
+        
+        panel.SetSizer(pageSizer)
+        panel.Fit()    
+        
+        return panel
+        
+    def _createConstantPage(self, parent):
+        """!Create constant page"""
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        self.page['constant'] = { 'id' : 1, 
+                                'notebook' : self.foldpanelData.GetId() }
+        self.win['constant'] = {}
+        
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Constant surface")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        horsizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        surface = wx.ComboBox(parent = panel, id = wx.ID_ANY, 
+                              style = wx.CB_SIMPLE | wx.CB_READONLY,
+                              choices = [])
+        self.win['constant']['surface'] = surface.GetId()
+        surface.Bind(wx.EVT_COMBOBOX, self.OnConstantSelection)
+        horsizer.Add(surface, proportion = 1, flag = wx.EXPAND|wx.RIGHT, border = 20)
+
+        addNew = wx.Button(panel, id = wx.ID_ANY, label = _("New"))
+        addNew.Bind(wx.EVT_BUTTON, self.OnNewConstant)
+        self.win['constant']['new'] = addNew.GetId()
+
+        delete = wx.Button(panel, id = wx.ID_ANY, label = _("Delete"))
+        delete.Bind(wx.EVT_BUTTON, self.OnDeleteConstant)
+        self.win['constant']['delete'] = delete.GetId()
+        
+        horsizer.Add(item = addNew, proportion = 0, flag = wx.RIGHT|wx.LEFT, border = 3)
+        horsizer.Add(item = delete, proportion = 0, flag = wx.RIGHT|wx.LEFT, border = 3)
+    
+        boxSizer.Add(item = horsizer, proportion = 0, flag = wx.ALL|wx.EXPAND,
+                      border = 5)
+        
+        gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        # fine resolution
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Fine resolution:")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        resF = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           initial = 3,
+                           min = 1,
+                           max = 100)
+        resF.SetName("value")
+        self.win['constant']['resolution'] = resF.GetId()
+        resF.Bind(wx.EVT_SPINCTRL, self.OnSetConstantProp)
+        gridSizer.Add(item = resF, pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
+        # value 
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Value:")), pos = (1, 0),
+                                         flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        value = wx.SpinCtrl(panel, id = wx.ID_ANY,
+                                  min = -1e9, max = 1e9,
+                                  size = (65, -1))
+        self.win['constant']['value'] = value.GetId()
+        value.Bind(wx.EVT_SPINCTRL, self.OnSetConstantProp)
+        gridSizer.Add(item = value, pos = (1, 1))
+        
+        # transparency 
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Transparency:")), pos = (2, 0),
+                                         flag = wx.ALIGN_CENTER_VERTICAL)
+        
+        transp = wx.SpinCtrl(panel, id = wx.ID_ANY,
+                                  min = 0, max = 100,
+                                  size = (65, -1))
+        self.win['constant']['transp'] = transp.GetId()
+        transp.Bind(wx.EVT_SPINCTRL, self.OnSetConstantProp)
+        gridSizer.Add(item = transp, pos = (2, 1))
+        
+        # color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Color:")), pos = (3, 0),
+                                         flag = wx.ALIGN_CENTER_VERTICAL)
+        color = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                  colour = (0,0,0),
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        self.win['constant']['color'] = color.GetId()
+        color.Bind(csel.EVT_COLOURSELECT, self.OnSetConstantProp)
+        gridSizer.Add(item = color, pos = (3, 1))
+        boxSizer.Add(item = gridSizer, proportion = 0, flag = wx.ALL,
+                      border = 5)
+        pageSizer.Add(item = boxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 3)
+        
+        panel.SetSizer(pageSizer)
+        panel.Fit()    
+        
+        return panel
+        
+    def _createVectorPage(self, parent):
         """!Create view settings page"""
-        panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
-        panel.SetupScrolling(scroll_x = False)
-        self.page['vector'] = { 'id' : 1,
-                                'panel' : panel.GetId(),
-                                'notebook' : self.notebookData.GetId() }
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        self.page['vector'] = { 'id' : 2,
+                                'notebook' : self.foldpanelData.GetId() }
         pageSizer = wx.BoxSizer(wx.VERTICAL)
         
         self.win['vector'] = {}
@@ -664,13 +1361,14 @@
                             label = " %s " % (_("Vector lines")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
         gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        gridSizer.AddGrowableCol(5)
         
         # width
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
                                          label = _("Line:")),
                       pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("width")),
+                                         label = _("width:")),
                       pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL | 
                       wx.ALIGN_RIGHT)
         
@@ -686,7 +1384,7 @@
         
         # color
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("color")),
+                                         label = _("color:")),
                       pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL |
                       wx.ALIGN_RIGHT)
         
@@ -701,43 +1399,42 @@
         
         # display
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("display")),
-                      pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL |
-                      wx.ALIGN_RIGHT)
+                                         label = _("Display")),
+                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL |
+                      wx.ALIGN_LEFT)
         
-        display = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
-                             choices = [_("on surface"),
+        display = wx.Choice (parent = panel, id = wx.ID_ANY, size = (-1, -1),
+                             choices = [_("on surface(s):"),
                                         _("flat")])
         self.win['vector']['lines']['flat'] = display.GetId()
         display.Bind(wx.EVT_CHOICE, self.OnVectorDisplay)
         
         gridSizer.Add(item = display, flag = wx.ALIGN_CENTER_VERTICAL | 
-                      wx.ALIGN_LEFT, pos = (1, 2), span = (1,2))
+                      wx.ALIGN_LEFT|wx.EXPAND, pos = (1, 1), span = (1,4))
         
         # height
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
                                          label = _("Height above surface:")),
-                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL,
-                      span = (1, 3))
+                      pos = (2, 5), flag = wx.ALIGN_BOTTOM|wx.EXPAND)
         
-        surface = wx.ComboBox(parent = panel, id = wx.ID_ANY, size = (250, -1),
-                              style = wx.CB_SIMPLE | wx.CB_READONLY,
-                              choices = [])
-        surface.Bind(wx.EVT_COMBOBOX, self.OnVectorSurface)
+        surface = wx.CheckListBox(parent = panel, id = wx.ID_ANY, size = (-1, 60),
+                                  choices = [], style = wx.LB_NEEDED_SB)
+        surface.Bind(wx.EVT_CHECKLISTBOX, self.OnVectorSurface)
+        
         self.win['vector']['lines']['surface'] = surface.GetId()
         gridSizer.Add(item = surface, 
-                      pos = (2, 3), span = (1, 6),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
+                      pos = (2, 0), span = (3, 5),
+                      flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
         
-        self._createControl(panel, data = self.win['vector']['lines'], name = 'height', size = 300,
-                            range = (0, 1000),
-                            bind = (self.OnVectorHeight, self.OnVectorHeightFull, self.OnVectorHeightSpin))
+        self._createControl(panel, data = self.win['vector']['lines'], name = 'height', size = -1,
+                            range = (0, 500), sliderHor = True,
+                            bind = (self.OnVectorHeight, self.OnVectorHeightFull, self.OnVectorHeightText))
         self.FindWindowById(self.win['vector']['lines']['height']['slider']).SetValue(0)
-        self.FindWindowById(self.win['vector']['lines']['height']['spin']).SetValue(0)
+        self.FindWindowById(self.win['vector']['lines']['height']['text']).SetValue(0)
         gridSizer.Add(item = self.FindWindowById(self.win['vector']['lines']['height']['slider']),
-                      pos = (3, 0), span = (1, 7))
-        gridSizer.Add(item = self.FindWindowById(self.win['vector']['lines']['height']['spin']),
-                      pos = (3, 7),
+                      pos = (3, 5),  flag = wx.EXPAND|wx.ALIGN_RIGHT)
+        gridSizer.Add(item = self.FindWindowById(self.win['vector']['lines']['height']['text']),
+                      pos = (4, 5),
                       flag = wx.ALIGN_CENTER)
         
         boxSizer.Add(item = gridSizer, proportion = 1,
@@ -763,14 +1460,19 @@
         box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
                             label = " %s " % (_("Vector points")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        vertSizer = wx.BoxSizer(wx.VERTICAL)
         gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        gridSizer.AddGrowableCol(0)
+        gridSizer.AddGrowableCol(2)
+        gridSizer.AddGrowableCol(4)
+        gridSizer.AddGrowableCol(6)
         
         # icon size
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
                                          label = _("Icon:")),
                       pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("size")),
+                                         label = _("size:")),
                       pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL |
                       wx.ALIGN_RIGHT)
         
@@ -788,7 +1490,7 @@
         
         # icon color
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("color")),
+                                         label = _("color:")),
                       pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL |
                       wx.ALIGN_RIGHT)
         icolor = csel.ColourSelect(panel, id = wx.ID_ANY,
@@ -801,89 +1503,92 @@
                       wx.ALIGN_LEFT,
                       pos = (0, 4))
 
-        # icon width
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                           label = _("width")),
-                      pos = (0, 5), flag = wx.ALIGN_CENTER_VERTICAL |
-                      wx.ALIGN_RIGHT)
-        
-        iwidth = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                             initial = 1,
-                             min = 1,
-                             max = 1e6)
-        iwidth.SetName('value')
-        iwidth.SetValue(100)
-        self.win['vector']['points']['width'] = iwidth.GetId()
-        iwidth.Bind(wx.EVT_SPINCTRL, self.OnVectorPoints)
-        iwidth.Bind(wx.EVT_TEXT, self.OnVectorPoints)
-        gridSizer.Add(item = iwidth, pos = (0, 6),
-                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
-        
+        # icon width - seems to do nothing
+##        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+##                                           label = _("width")),
+##                      pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL |
+##                      wx.ALIGN_RIGHT)
+##        
+##        iwidth = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+##                             initial = 1,
+##                             min = 1,
+##                             max = 1e6)
+##        iwidth.SetName('value')
+##        iwidth.SetValue(100)
+##        self.win['vector']['points']['width'] = iwidth.GetId()
+##        iwidth.Bind(wx.EVT_SPINCTRL, self.OnVectorPoints)
+##        iwidth.Bind(wx.EVT_TEXT, self.OnVectorPoints)
+##        gridSizer.Add(item = iwidth, pos = (1, 2),
+##                      flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
         # icon symbol
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("symbol")),
-                      pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
+                                         label = _("symbol:")),
+                      pos = (0, 5), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
         isym = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
                           choices = UserSettings.Get(group = 'nviz', key = 'vector',
                                                    subkey = ['points', 'marker'], internal = True))
         isym.SetName("selection")
         self.win['vector']['points']['marker'] = isym.GetId()
         isym.Bind(wx.EVT_CHOICE, self.OnVectorPoints)
-        gridSizer.Add(item = isym, flag = wx.ALIGN_CENTER_VERTICAL,
-                      pos = (1, 2), span = (1,2))
+        gridSizer.Add(item = isym, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT,
+                      pos = (0, 6))
         
+        vertSizer.Add(gridSizer, proportion = 0, flag = wx.EXPAND, border = 0)
         # high
+        gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
+        gridSizer.AddGrowableCol(1)
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                         label = _("Display on surface(s):")),
+                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
                                          label = _("Height above surface:")),
-                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL,
-                      span = (1, 3))
+                      pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
         
-        surface = wx.ComboBox(parent = panel, id = wx.ID_ANY, size = (250, -1),
-                              style = wx.CB_SIMPLE | wx.CB_READONLY,
-                              choices = [])
-        surface.Bind(wx.EVT_COMBOBOX, self.OnVectorSurface)
+        surface = wx.CheckListBox(parent = panel, id = wx.ID_ANY, size = (-1, 60),
+                                  choices = [], style = wx.LB_NEEDED_SB)
+        surface.Bind(wx.EVT_CHECKLISTBOX, self.OnVectorSurface)
         self.win['vector']['points']['surface'] = surface.GetId()
         gridSizer.Add(item = surface, 
-                      pos = (2, 3), span = (1, 5),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
+                      pos = (1, 0), span = (3, 1),
+                      flag = wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
         
-        self._createControl(panel, data = self.win['vector']['points'], name = 'height', size = 300,
-                            range = (0, 1000),
-                            bind = (self.OnVectorHeight, self.OnVectorHeightFull, self.OnVectorHeightSpin))
+        self._createControl(panel, data = self.win['vector']['points'], name = 'height', size = -1,
+                            range = (0, 500),
+                            bind = (self.OnVectorHeight, self.OnVectorHeightFull, self.OnVectorHeightText))
         
         self.FindWindowById(self.win['vector']['points']['height']['slider']).SetValue(0)
-        self.FindWindowById(self.win['vector']['points']['height']['spin']).SetValue(0)
+        self.FindWindowById(self.win['vector']['points']['height']['text']).SetValue(0)
         
         gridSizer.Add(item = self.FindWindowById(self.win['vector']['points']['height']['slider']),
-                      pos = (3, 0), span = (1, 7))
-        gridSizer.Add(item = self.FindWindowById(self.win['vector']['points']['height']['spin']),
-                      pos = (3, 7),
+                      pos = (2, 1),flag = wx.EXPAND|wx.ALIGN_CENTER_VERTICAL)
+        gridSizer.Add(item = self.FindWindowById(self.win['vector']['points']['height']['text']),
+                      pos = (3, 1),
                       flag = wx.ALIGN_CENTER)
-        
-        boxSizer.Add(item = gridSizer, proportion = 1,
+                    
+        vertSizer.Add(gridSizer, proportion = 0, flag = wx.EXPAND, border = 0)
+        boxSizer.Add(item = vertSizer, proportion = 1,
                      flag = wx.ALL | wx.EXPAND, border = 3)
         pageSizer.Add(item = boxSizer, proportion = 0,
                       flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
                       border = 3)
         
         panel.SetSizer(pageSizer)
+        panel.Fit()
 
         return panel
 
     def GselectOnPopup(self, ltype, exclude = False):
         """Update gselect.Select() items"""
         maps = list()
-        for layer in self.mapWindow.Map.GetListOfLayers(l_type = ltype):
+        for layer in self.mapWindow.Map.GetListOfLayers(l_type = ltype, l_active = True):
             maps.append(layer.GetName())
         return maps, exclude
     
-    def _createVolumePage(self):
+    def _createVolumePage(self, parent):
         """!Create view settings page"""
-        panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
-        panel.SetupScrolling(scroll_x = False)
-        self.page['volume'] = { 'id' : 2,
-                                'panel' : panel.GetId(),
-                                'notebook' : self.notebookData.GetId() }
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        self.page['volume'] = { 'id' : 3,
+                                'notebook' : self.foldpanelData.GetId() }
         pageSizer = wx.BoxSizer(wx.VERTICAL)
         
         self.win['volume'] = {}
@@ -892,7 +1597,7 @@
         box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
                             label = " %s " % (_("3D raster map")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        rmaps = gselect.Select(parent = panel, type = 'raster3D',
+        rmaps = gselect.Select(parent = panel, type = '3d-raster',
                                onPopup = self.GselectOnPopup)
         rmaps.GetChildren()[0].Bind(wx.EVT_TEXT, self.OnSetRaster3D)
         self.win['volume']['map'] = rmaps.GetId()
@@ -916,18 +1621,18 @@
                             label = " %s " % (_("Draw")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
         gridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
-        gridSizer.AddGrowableCol(4)
+##        gridSizer.AddGrowableCol(4)
         
         # mode
         gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
                                          label = _("Mode:")),
                       pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
-        mode = wx.Choice (parent = panel, id = wx.ID_ANY, size = (150, -1),
+        mode = wx.Choice (parent = panel, id = wx.ID_ANY, size = (-1, -1),
                           choices = [_("isosurfaces"),
-                                     _("slides")])
+                                     _("slices")])
         mode.SetSelection(0)
         mode.SetName("selection")
-        # mode.Bind(wx.EVT_CHOICE, self.OnSurfaceMode)
+        mode.Bind(wx.EVT_CHOICE, self.OnVolumeMode)
         self.win['volume']['draw']['mode'] = mode.GetId()
         gridSizer.Add(item = mode, flag = wx.ALIGN_CENTER_VERTICAL,
                       pos = (0, 1))
@@ -941,7 +1646,7 @@
                                       _("gouraud")])
         shade.SetName("selection")
         self.win['volume']['draw']['shading'] = shade.GetId()
-        shade.Bind(wx.EVT_CHOICE, self.OnVolumeIsosurfMode)
+        shade.Bind(wx.EVT_CHOICE, self.OnVolumeDrawMode)
         gridSizer.Add(item = shade, flag = wx.ALIGN_CENTER_VERTICAL,
                       pos = (0, 3))
         
@@ -955,11 +1660,11 @@
                             max = 100)
         resol.SetName("value")
         self.win['volume']['draw']['resolution'] = resol.GetId()
-        resol.Bind(wx.EVT_SPINCTRL, self.OnVolumeIsosurfResolution)
-        resol.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfResolution)
+        resol.Bind(wx.EVT_SPINCTRL, self.OnVolumeResolution)
+        resol.Bind(wx.EVT_TEXT, self.OnVolumeResolution)
         gridSizer.Add(item = resol, pos = (0, 5))
         
-        boxSizer.Add(item = gridSizer, proportion = 1,
+        boxSizer.Add(item = gridSizer, proportion = 0,
                      flag = wx.ALL | wx.EXPAND, border = 3)
         pageSizer.Add(item = boxSizer, proportion = 0,
                       flag = wx.EXPAND | wx.ALL,
@@ -968,41 +1673,43 @@
         #
         # manage isosurfaces
         #
-        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
-                            label = " %s " % (_("List of isosurfaces")))
+        box = wx.StaticBox(parent = panel, id = wx.ID_ANY, 
+                           label = " %s " % (_("List of isosurfaces")))
+        box.SetName('listStaticBox')
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
         gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
         
         # list
         isolevel = wx.CheckListBox(parent = panel, id = wx.ID_ANY,
                                    size = (300, 150))
-        self.Bind(wx.EVT_CHECKLISTBOX, self.OnVolumeIsosurfCheck, isolevel)
-        self.Bind(wx.EVT_LISTBOX, self.OnVolumeIsosurfSelect, isolevel)
+        self.Bind(wx.EVT_CHECKLISTBOX, self.OnVolumeCheck, isolevel)
+        self.Bind(wx.EVT_LISTBOX, self.OnVolumeSelect, isolevel)
         
         self.win['volume']['isosurfs'] = isolevel.GetId()
+        self.win['volume']['slices'] = isolevel.GetId()
         gridSizer.Add(item = isolevel, pos = (0, 0), span = (4, 1))
         
         # buttons (add, delete, move up, move down)
         btnAdd = wx.Button(parent = panel, id = wx.ID_ADD)
-        self.win['volume']['btnIsosurfAdd'] = btnAdd.GetId()
-        btnAdd.Bind(wx.EVT_BUTTON, self.OnVolumeIsosurfAdd)
+        self.win['volume']['btnAdd'] = btnAdd.GetId()
+        btnAdd.Bind(wx.EVT_BUTTON, self.OnVolumeAdd)
         gridSizer.Add(item = btnAdd,
                       pos = (0, 1))
         btnDelete = wx.Button(parent = panel, id = wx.ID_DELETE)
-        self.win['volume']['btnIsosurfDelete'] = btnDelete.GetId()
-        btnDelete.Bind(wx.EVT_BUTTON, self.OnVolumeIsosurfDelete)
+        self.win['volume']['btnDelete'] = btnDelete.GetId()
+        btnDelete.Bind(wx.EVT_BUTTON, self.OnVolumeDelete)
         btnDelete.Enable(False)
         gridSizer.Add(item = btnDelete,
                       pos = (1, 1))
         btnMoveUp = wx.Button(parent = panel, id = wx.ID_UP)
-        self.win['volume']['btnIsosurfMoveUp'] = btnMoveUp.GetId()
-        btnMoveUp.Bind(wx.EVT_BUTTON, self.OnVolumeIsosurfMoveUp)
+        self.win['volume']['btnMoveUp'] = btnMoveUp.GetId()
+        btnMoveUp.Bind(wx.EVT_BUTTON, self.OnVolumeMoveUp)
         btnMoveUp.Enable(False)
         gridSizer.Add(item = btnMoveUp,
                       pos = (2, 1))
         btnMoveDown = wx.Button(parent = panel, id = wx.ID_DOWN)
-        self.win['volume']['btnIsosurfMoveDown'] = btnMoveDown.GetId()
-        btnMoveDown.Bind(wx.EVT_BUTTON, self.OnVolumeIsosurfMoveDown)
+        self.win['volume']['btnMoveDown'] = btnMoveDown.GetId()
+        btnMoveDown.Bind(wx.EVT_BUTTON, self.OnVolumeMoveDown)
         btnMoveDown.Enable(False)
         gridSizer.Add(item = btnMoveDown,
                       pos = (3, 1))
@@ -1012,117 +1719,72 @@
         pageSizer.Add(item = boxSizer, proportion = 0,
                       flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
                       border = 3)
-        
+        # isosurface/slice 
+        sizer = wx.BoxSizer()
+        self.isoPanel = self._createIsosurfacePanel(panel)
+        self.slicePanel = self._createSlicePanel(panel)
+        sizer.Add(self.isoPanel, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 0)
+        sizer.Add(self.slicePanel, proportion = 1, flag = wx.EXPAND|wx.ALL, border = 0)
+        sizer.Hide(self.slicePanel)
+        pageSizer.Add(item = sizer, proportion = 0,
+                      flag = wx.EXPAND | wx.ALL,
+                      border = 3)
         #
-        # isosurface attributes
+        # position
         #
+        self.win['volume']['position'] = {}
         box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
-                            label = " %s " % (_("Isosurface attributes")))
+                            label = " %s " % (_("Position")))
         boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
         gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(3)
         
-        self.win['volume']['attr'] = {}
-        row = 0
-        for code, attrb in (('topo', _("Topography level")),
-                            ('color', _("Color")),
-                            ('mask', _("Mask")),
-                            ('transp', _("Transparency")),
-                            ('shine', _("Shininess")),
-                            ('emit', _("Emission"))):
-            self.win['volume'][code] = {} 
-            # label
-            gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                             label = attrb + ':'),
-                          pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
-            if code != 'topo':
-                use = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
-                                 choices = [_("map")])
-            else:
-                use = None
-            # check for required properties
-            if code not in ('topo', 'color', 'shine'):
-                use.Insert(item = _("unset"), pos = 0)
-                self.win['volume'][code]['required'] = False
-            else:
-                self.win['volume'][code]['required'] = True
-            if use and code != 'mask':
-                use.Append(item = _('constant'))
-            if use:
-                self.win['volume'][code]['use'] = use.GetId()
-                use.Bind(wx.EVT_CHOICE, self.OnMapObjUse)
-                gridSizer.Add(item = use, flag = wx.ALIGN_CENTER_VERTICAL,
-                              pos = (row, 1))
-            
-            if code != 'topo':
-                map = gselect.Select(parent = panel, id = wx.ID_ANY,
-                                     # size = globalvar.DIALOG_GSELECT_SIZE,
-                                     size = (200, -1),
-                                     type = "grid3")
-                self.win['volume'][code]['map'] = map.GetId() - 1 # FIXME
-                map.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfMap)
-                gridSizer.Add(item = map, flag = wx.ALIGN_CENTER_VERTICAL,
-                              pos = (row, 2))
-            else:
-                map = None
-            
-            if code == 'color':
-                value = csel.ColourSelect(panel, id = wx.ID_ANY,
-                                          colour = (0,0,0),
-                                          size = globalvar.DIALOG_COLOR_SIZE)
-                value.Bind(csel.EVT_COLOURSELECT, self.OnVolumeIsosurfMap)
-            elif code == 'mask':
-                value = None
-            else:
-                if code == 'topo':
-                    size = (200, -1)
-                else:
-                    size = (65, -1)
-                value = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = size,
-                                    initial = 0)
-                if code == 'topo':
-                    value.SetRange(minVal = -1e9, maxVal = 1e9)
-                elif code in ('shine', 'transp', 'emit'):
-                    value.SetRange(minVal = 0, maxVal = 255)
-                else:
-                    value.SetRange(minVal = 0, maxVal = 100)
-                value.Bind(wx.EVT_SPINCTRL, self.OnVolumeIsosurfMap)
-                value.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfMap)
-            
-            if value:
-                self.win['volume'][code]['const'] = value.GetId()
-                if code == 'topo':
-                    gridSizer.Add(item = value, flag = wx.ALIGN_CENTER_VERTICAL,
-                                  pos = (row, 2))
-                else:
-                    value.Enable(False)
-                    gridSizer.Add(item = value, flag = wx.ALIGN_CENTER_VERTICAL,
-                                  pos = (row, 3))
-            else:
-                self.win['volume'][code]['const'] = None
-            
-            if code != 'topo':
-                self.SetMapObjUseMap(nvizType = 'volume',
-                                     attrb = code) # -> enable map / disable constant
-            
-            row += 1
+        # position
+        self._createControl(panel, data = self.win['volume'], name = 'position',
+                            range = (-10000, 10000), floatSlider = True,
+                            bind = (self.OnVolumePosition, self.OnVolumePositionChanged, self.OnVolumePositionText))
         
+        axis = wx.Choice (parent = panel, id = wx.ID_ANY, size = (75, -1),
+                          choices = ["X",
+                                     "Y",
+                                     "Z"])
+                                    
+        reset = wx.Button(panel, id = wx.ID_ANY, label = _("Reset"))
+        reset.SetToolTipString(_("Reset to default position"))
+        reset.Bind(wx.EVT_BUTTON, self.OnResetVolumePosition)
+        self.win['volume']['position']['reset'] = reset.GetId()
+        
+        self.win['volume']['position']['axis'] = axis.GetId()
+        axis.SetSelection(0)
+        axis.Bind(wx.EVT_CHOICE, self.OnVolumeAxis)
+        
+        pslide = self.FindWindowById(self.win['volume']['position']['slider'])
+        ptext = self.FindWindowById(self.win['volume']['position']['text'])
+        ptext.SetValue('0')
+        
+        gridSizer.Add(item = axis, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
+        gridSizer.Add(item = pslide, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 1))
+        gridSizer.Add(item = ptext, flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 2))
+        gridSizer.Add(item = reset, flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT, pos = (0, 3))
+        
         boxSizer.Add(item = gridSizer, proportion = 1,
-                     flag = wx.ALL | wx.EXPAND, border = 3)
+                  flag = wx.ALL | wx.EXPAND, border = 3)
+        
         pageSizer.Add(item = boxSizer, proportion = 0,
                       flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
                       border = 3)
-        
         panel.SetSizer(pageSizer)
+        panel.Fit()
         
         return panel
-    
-    def _createLightPage(self):
+       
+        
+    def _createLightPage(self, parent):
         """!Create light page"""
-        panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
-        panel.SetupScrolling(scroll_x = False)
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
         
         self.page['light'] = { 'id' : 0, 
-                               'notebook' : self.notebookAppearance.GetId() }
+                               'notebook' : self.foldpanelAppear.GetId() }
         self.win['light'] = {}
         
         pageSizer = wx.BoxSizer(wx.VERTICAL)
@@ -1130,12 +1792,14 @@
         show = wx.CheckBox(parent = panel, id = wx.ID_ANY,
                            label = _("Show light model"))
         show.Bind(wx.EVT_CHECKBOX, self.OnShowLightModel)
+        show.SetValue(True)
+        self._display.showLight = True
         pageSizer.Add(item = show, proportion = 0,
                       flag = wx.ALL, border = 3)
-        surface = wx.CheckBox(parent = panel, id = wx.ID_ANY,
-                              label = _("Follow source viewpoint"))
-        pageSizer.Add(item = surface, proportion = 0,
-                      flag = wx.ALL, border = 3)
+##        surface = wx.CheckBox(parent = panel, id = wx.ID_ANY,
+##                              label = _("Follow source viewpoint"))
+##        pageSizer.Add(item = surface, proportion = 0,
+##                      flag = wx.ALL, border = 3)
         
         # position
         box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
@@ -1144,36 +1808,31 @@
         
         gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
         posSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
-        posSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("W")),
-                     pos = (1, 0), flag = wx.ALIGN_CENTER)
-        posSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("N")),
-                     pos = (0, 1), flag = wx.ALIGN_CENTER | wx.ALIGN_BOTTOM)
+        
+        self._createCompass(panel = panel, sizer = posSizer, type = 'light')
+        
         pos = LightPositionWindow(panel, id = wx.ID_ANY, size = (175, 175),
                                   mapwindow = self.mapWindow)
         self.win['light']['position'] = pos.GetId()
         posSizer.Add(item = pos,
                      pos = (1, 1), flag = wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL)
-        posSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("S")),
-                     pos = (2, 1), flag = wx.ALIGN_CENTER | wx.ALIGN_TOP)
-        posSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("E")),
-                     pos = (1, 2), flag = wx.ALIGN_CENTER)
         gridSizer.Add(item = posSizer, pos = (0, 0))
         
         # height
         self._createControl(panel, data = self.win['light'], name = 'z', sliderHor = False,
                             range = (0, 100),
-                            bind = (self.OnLightChange, None, self.OnLightChange))
+                            bind = (self.OnLightChange, self.OnLightChanged, self.OnLightChange))
         
         heightSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
         heightSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Height:")),
                       pos = (0, 0), flag = wx.ALIGN_LEFT, span = (1, 2))
         heightSizer.Add(item = self.FindWindowById(self.win['light']['z']['slider']),
                         flag = wx.ALIGN_RIGHT, pos = (1, 0))
-        heightSizer.Add(item = self.FindWindowById(self.win['light']['z']['spin']),
+        heightSizer.Add(item = self.FindWindowById(self.win['light']['z']['text']),
                         flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT | wx.TOP |
                         wx.BOTTOM | wx.RIGHT, pos = (1, 1))
         
-        gridSizer.Add(item = heightSizer, pos = (0, 1), flag = wx.ALIGN_RIGHT)
+        gridSizer.Add(item = heightSizer, pos = (0, 2), flag = wx.ALIGN_RIGHT)
         
         boxSizer.Add(item = gridSizer, proportion = 1,
                      flag = wx.ALL | wx.EXPAND, border = 2)
@@ -1201,20 +1860,20 @@
                       pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
         self._createControl(panel, data = self.win['light'], name = 'bright', size = 300,
                             range = (0, 100),
-                            bind = (self.OnLightValue, None, self.OnLightValue))
+                            bind = (self.OnLightValue, self.OnLightChanged, self.OnLightValue))
         gridSizer.Add(item = self.FindWindowById(self.win['light']['bright']['slider']),
                       pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.Add(item = self.FindWindowById(self.win['light']['bright']['spin']),
+        gridSizer.Add(item = self.FindWindowById(self.win['light']['bright']['text']),
                       pos = (1, 2),
                       flag = wx.ALIGN_CENTER)
         gridSizer.Add(item = wx.StaticText(panel, id = wx.ID_ANY, label = _("Ambient:")),
                       pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
         self._createControl(panel, data = self.win['light'], name = 'ambient', size = 300,
                             range = (0, 100),
-                            bind = (self.OnLightValue, None, self.OnLightValue))
+                            bind = (self.OnLightValue, self.OnLightChanged, self.OnLightValue))
         gridSizer.Add(item = self.FindWindowById(self.win['light']['ambient']['slider']),
                       pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.Add(item = self.FindWindowById(self.win['light']['ambient']['spin']),
+        gridSizer.Add(item = self.FindWindowById(self.win['light']['ambient']['text']),
                       pos = (2, 2),
                       flag = wx.ALIGN_CENTER)
 
@@ -1238,16 +1897,17 @@
         #               flag = wx.EXPAND)
         
         panel.SetSizer(pageSizer)
+        panel.Layout()
+        panel.Fit()
         
         return panel
 
-    def _createFringePage(self):
+    def _createFringePage(self, parent):
         """!Create fringe page"""
-        panel = SP.ScrolledPanel(parent = self, id = wx.ID_ANY)
-        panel.SetupScrolling(scroll_x = False)
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
         
         self.page['fringe'] = { 'id' : 1,
-                                'notebook' : self.notebookAppearance.GetId() }
+                                'notebook' : self.foldpanelAppear.GetId() }
         self.win['fringe'] = {}
 
         pageSizer = wx.BoxSizer(wx.VERTICAL)
@@ -1323,12 +1983,114 @@
                       border = 3)
         
         panel.SetSizer(pageSizer)
+        panel.Layout()
+        panel.Fit()
 
         return panel
+    
+    def _createDecorationPage(self, parent):
+        """!Create decoration (north arrow, scalebar, legend) page"""
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        
+        self.page['decoration'] = { 'id' : 2,
+                                    'notebook' : self.foldpanelAppear.GetId()}
+        self.win['decoration'] = {}
 
-    def GetLayerData(self, nvizType):
+        pageSizer = wx.BoxSizer(wx.VERTICAL)
+        
+        # north arrow
+        self.win['decoration']['arrow'] = {}
+        nabox = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                             label = " %s " % (_("North Arrow")))
+        naboxSizer = wx.StaticBoxSizer(nabox, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        # size
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Arrow length (in map units):")),
+                      pos = (0,0), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        sizeCtrl = NumTextCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1), style = wx.TE_PROCESS_ENTER)
+        gridSizer.Add(sizeCtrl, pos = (0, 2))
+        self.win['decoration']['arrow']['size'] = sizeCtrl.GetId()
+        sizeCtrl.Bind(wx.EVT_TEXT_ENTER, self.OnDecorationProp)
+        sizeCtrl.Bind(wx.EVT_KILL_FOCUS, self.OnDecorationProp)
+        
+        # color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Arrow color:")),
+                      pos = (1,0), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        color = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        gridSizer.Add(color, pos = (1, 2))
+        self.win['decoration']['arrow']['color'] = color.GetId()
+        color.Bind(csel.EVT_COLOURSELECT, self.OnDecorationProp)
+        
+        # control
+        toggle = wx.ToggleButton(parent = panel, id = wx.ID_ANY, label = _("Place arrow"))
+        gridSizer.Add(item = toggle, pos = (2, 0))
+        toggle.Bind(wx.EVT_TOGGLEBUTTON, self.OnDecorationPlacement)
+        self.win['decoration']['arrow']['place'] = toggle.GetId()
+        toggle.SetName('placeArrow')
+
+        delete = wx.Button(parent = panel, id = wx.ID_ANY, label = _("Delete"))
+        gridSizer.Add(item = delete, pos = (2, 1))
+        delete.Bind(wx.EVT_BUTTON, self.OnArrowDelete)
+        naboxSizer.Add(item = gridSizer, proportion = 0, flag = wx.EXPAND, border = 3)
+        pageSizer.Add(item = naboxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)
+        
+        
+        # north arrow
+        self.win['decoration']['scalebar'] = {}
+        nabox = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                             label = " %s " % (_("Scale bar")))
+        naboxSizer = wx.StaticBoxSizer(nabox, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
+        # size
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Scale bar length (in map units):")),
+                      pos = (0,0), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        sizeCtrl = NumTextCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1), style = wx.TE_PROCESS_ENTER)
+        gridSizer.Add(sizeCtrl, pos = (0, 2))
+        self.win['decoration']['scalebar']['size'] = sizeCtrl.GetId()
+        sizeCtrl.Bind(wx.EVT_TEXT_ENTER, self.OnDecorationProp)
+        sizeCtrl.Bind(wx.EVT_KILL_FOCUS, self.OnDecorationProp)
+        
+        # color
+        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                           label = _("Scale bar color:")),
+                      pos = (1,0), span = (1, 2), flag = wx.ALIGN_CENTER_VERTICAL)
+        color = csel.ColourSelect(parent = panel, id = wx.ID_ANY,
+                                  size = globalvar.DIALOG_COLOR_SIZE)
+        gridSizer.Add(color, pos = (1, 2))
+        self.win['decoration']['scalebar']['color'] = color.GetId()
+        color.Bind(csel.EVT_COLOURSELECT, self.OnDecorationProp)
+        
+        # control
+        toggle = wx.ToggleButton(parent = panel, id = wx.ID_ANY, label = _("Place scalebar"))
+        gridSizer.Add(item = toggle, pos = (2, 0))
+        toggle.Bind(wx.EVT_TOGGLEBUTTON, self.OnDecorationPlacement)
+        self.win['decoration']['scalebar']['place'] = toggle.GetId()
+        toggle.SetName('placeScalebar')
+
+        delete = wx.Button(parent = panel, id = wx.ID_ANY, label = _("Delete last"))
+        gridSizer.Add(item = delete, pos = (2, 1))
+        delete.Bind(wx.EVT_BUTTON, self.OnScalebarDelete)
+        naboxSizer.Add(item = gridSizer, proportion = 0, flag = wx.EXPAND, border = 3)
+        pageSizer.Add(item = naboxSizer, proportion = 0,
+                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
+                      border = 3)      
+        panel.SetSizer(pageSizer)
+        panel.Layout()
+        panel.Fit()
+
+        return panel
+    
+    def GetLayerData(self, nvizType, nameOnly = False):
         """!Get nviz data"""
         name = self.FindWindowById(self.win[nvizType]['map']).GetValue()
+        if nameOnly:
+            return name
         
         if nvizType == 'surface' or nvizType == 'fringe':
             return self.mapWindow.GetLayerByName(name, mapType = 'raster', dataType = 'nviz')
@@ -1338,12 +2100,276 @@
             return self.mapWindow.GetLayerByName(name, mapType = '3d-raster', dataType = 'nviz')
         
         return None
+        
+    def OnRecord(self, event):
+        """!Animation: start recording"""
+        anim = self.mapWindow.GetAnimation()
+        if not anim.IsPaused():
+            if anim.Exists() and not anim.IsSaved():
+                msg = _("Do you want to record new animation without saving the previous one?")
+                dlg = wx.MessageDialog(parent = self,
+                                       message = msg,
+                                       caption =_("Animation already axists"),
+                                       style = wx.YES_NO | wx.CENTRE)
+                if dlg.ShowModal() == wx.ID_NO:
+                    dlg.Destroy()
+                    return
+                
+        
+            anim.Clear()
+            self.UpdateFrameIndex(0)
+            self.UpdateFrameCount()
+            
+        anim.SetPause(False)
+        anim.SetMode(mode = 'record')
+        anim.Start()
+        
+        self.FindWindowById(self.win['anim']['play']).Disable()
+        self.FindWindowById(self.win['anim']['record']).Disable()
+        self.FindWindowById(self.win['anim']['pause']).Enable()
+        self.FindWindowById(self.win['anim']['stop']).Enable()
+        self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+        
+    def OnPlay(self, event):
+        """!Animation: replay"""
+        anim = self.mapWindow.GetAnimation()
+        anim.SetPause(False)
+        anim.SetMode(mode = 'play')
+        anim.Start()
+        
+        self.FindWindowById(self.win['anim']['play']).Disable()
+        self.FindWindowById(self.win['anim']['record']).Disable()
+        self.FindWindowById(self.win['anim']['pause']).Enable()
+        self.FindWindowById(self.win['anim']['stop']).Enable()
+        self.FindWindowById(self.win['anim']['frameIndex']['slider']).Enable()
+        self.FindWindowById(self.win['anim']['frameIndex']['text']).Enable()
+        
+    def OnStop(self, event):
+        """!Animation: stop recording/replaying"""
+        anim = self.mapWindow.GetAnimation()
+        anim.SetPause(False)
+        if anim.GetMode() == 'save':
+            anim.StopSaving()
+        if anim.IsRunning():
+            anim.Stop()
+        
+        self.UpdateFrameIndex(0)
+        
+        self.FindWindowById(self.win['anim']['play']).Enable()
+        self.FindWindowById(self.win['anim']['record']).Enable()
+        self.FindWindowById(self.win['anim']['pause']).Disable()
+        self.FindWindowById(self.win['anim']['stop']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+        
+    def OnPause(self, event):
+        """!Pause animation"""
+        anim = self.mapWindow.GetAnimation()
+        
+        anim.SetPause(True)
+        mode = anim.GetMode()
+        if anim.IsRunning():
+            anim.Pause()
+            
+        if mode == "record":
+            self.FindWindowById(self.win['anim']['play']).Disable()
+            self.FindWindowById(self.win['anim']['record']).Enable()
+            self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+            self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+        elif mode == 'play':
+            self.FindWindowById(self.win['anim']['record']).Disable()
+            self.FindWindowById(self.win['anim']['play']).Enable()
+            self.FindWindowById(self.win['anim']['frameIndex']['slider']).Enable()
+            self.FindWindowById(self.win['anim']['frameIndex']['text']).Enable()
+        
+        self.FindWindowById(self.win['anim']['pause']).Disable()
+        self.FindWindowById(self.win['anim']['stop']).Enable()
+
+        
+    def OnFrameIndex(self, event):
+        """!Frame index changed (by slider)"""
+        index = event.GetInt()
+        self.UpdateFrameIndex(index = index, sliderWidget = False)
+        
+    def OnFrameIndexText(self, event):
+        """!Frame index changed by (textCtrl)"""
+        index = event.GetValue()
+        self.UpdateFrameIndex(index = index, textWidget = False)
+        
+    def OnFPS(self, event):
+        """!Frames per second changed"""
+        anim = self.mapWindow.GetAnimation()
+        anim.SetFPS(event.GetInt())
+        
+    def UpdateFrameIndex(self, index, sliderWidget = True, textWidget = True, goToFrame = True):
+        """!Update frame index"""
+        anim = self.mapWindow.GetAnimation()
+        
+        # check index
+        frameCount = anim.GetFrameCount()
+        if index >= frameCount:
+            index = frameCount - 1
+        if index < 0:
+            index = 0
+            
+        if sliderWidget:
+            slider = self.FindWindowById(self.win['anim']['frameIndex']['slider'])
+            slider.SetValue(index)
+        if textWidget:
+            text = self.FindWindowById(self.win['anim']['frameIndex']['text'])
+            text.SetValue(int(index))
+        
+        # if called from tool window, update frame
+        if goToFrame:
+            anim.GoToFrame(int(index))
+            
+    def UpdateFrameCount(self):
+        """!Update frame count label"""
+        anim = self.mapWindow.GetAnimation()
+        count = anim.GetFrameCount()
+        self.FindWindowById(self.win['anim']['info']).SetLabel(str(count))
+        
+    def OnAnimationFinished(self, event):
+        """!Animation finished"""
+        anim = self.mapWindow.GetAnimation()
+        self.UpdateFrameIndex(index = 0)
+        
+        slider = self.FindWindowById(self.win['anim']['frameIndex']['slider'])
+        text = self.FindWindowById(self.win['anim']['frameIndex']['text'])
+        
+        if event.mode == 'record':
+            count = anim.GetFrameCount()
+            slider.SetMax(count)
+            self.UpdateFrameCount()
+            
+        self.FindWindowById(self.win['anim']['pause']).Disable()
+        self.FindWindowById(self.win['anim']['stop']).Disable()
+        self.FindWindowById(self.win['anim']['record']).Enable()
+        self.FindWindowById(self.win['anim']['play']).Enable()
+        self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+        self.FindWindowById(self.win['anim']['save']['image']['confirm']).Enable()
+        
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.Refresh(False)
+        
+    def OnAnimationUpdateIndex(self, event):
+        """!Animation: frame index changed"""
+        if event.mode == 'record':
+            self.UpdateFrameCount()
+        elif event.mode == 'play':
+            self.UpdateFrameIndex(index = event.index, goToFrame = False)
+        
+    def OnSaveAnimation(self, event):
+        """!Save animation as a sequence of images"""
+        anim = self.mapWindow.GetAnimation()
+        
+        prefix = self.FindWindowById(self.win['anim']['save']['image']['prefix']).GetValue()
+        format = self.FindWindowById(self.win['anim']['save']['image']['format']).GetSelection()
+        dir = self.FindWindowById(self.win['anim']['save']['image']['dir']).GetValue()
+        
+        if not prefix:
+            gcmd.GMessage(parent = self,
+                          message = _("No file prefix given."))
+            return
+        elif not os.path.exists(dir):
+            gcmd.GMessage(parent = self,
+                          message = _("Directory %s does not exist.") % dir)
+            return
+            
+        self.FindWindowById(self.win['anim']['pause']).Disable()
+        self.FindWindowById(self.win['anim']['stop']).Enable()
+        self.FindWindowById(self.win['anim']['record']).Disable()
+        self.FindWindowById(self.win['anim']['play']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+        
+        self.FindWindowById(self.win['anim']['save']['image']['confirm']).Disable()
+        
+        anim.SaveAnimationFile(path = dir, prefix = prefix, format = format)
+        
+    def OnNewConstant(self, event):
+        """!Create new surface with constant value"""
+        #TODO settings
+        name = self.mapWindow.NewConstant()
+        win = self.FindWindowById(self.win['constant']['surface'])
+        name = _("constant#") + str(name)
+        win.Append(name)
+        win.SetStringSelection(name)
+        self.OnConstantSelection(None)
+        self.EnablePage(name = 'constant', enabled = True)
+        
+        self.mapWindow.Refresh(eraseBackground = False)
+        
+        # need to update list of surfaces in vector page
+        for vtype in ('points', 'lines'):
+            checklist = self.FindWindowById(self.win['vector'][vtype]['surface'])
+            checklist.Append(name)
+        win = self.FindWindowById(self.win['vector']['map'])
+        win.SetValue(win.GetValue())
+                
+
+    def OnDeleteConstant(self, event):
+        """!Delete selected constant surface"""
+        layerIdx = self.FindWindowById(self.win['constant']['surface']).GetSelection()
+        if layerIdx == wx.NOT_FOUND:
+            return
+        name = self.FindWindowById(self.win['constant']['surface']).GetStringSelection()
+        self.mapWindow.DeleteConstant(layerIdx)
+        win = self.FindWindowById(self.win['constant']['surface'])
+        win.Delete(layerIdx)
+        if win.IsEmpty():
+            win.SetValue("")
+            self.EnablePage(name = 'constant', enabled = False)
+        else:
+            win.SetSelection(0)
+            self.OnConstantSelection(None)
+            
+        # need to update list of surfaces in vector page
+        for vtype in ('points', 'lines'):
+            checklist = self.FindWindowById(self.win['vector'][vtype]['surface'])
+            checklist.Delete(checklist.FindString(name))
+            
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
     
+    def OnConstantSelection(self, event):
+        """!Constant selected"""
+        layerIdx = self.FindWindowById(self.win['constant']['surface']).GetSelection()
+        if layerIdx == wx.NOT_FOUND:
+            return
+        name = _("constant#") + str(layerIdx + 1)
+        data = self.mapWindow.constants[layerIdx]
+        for attr, value in data['constant'].iteritems():
+            if attr == 'color':
+                value = self._getColorFromString(value)
+            if attr in ('color', 'value', 'resolution', 'transp'):
+                if attr == 'transp':
+                    self.FindWindowById(self.win['constant'][attr]).SetValue(self._getPercent(value))
+                self.FindWindowById(self.win['constant'][attr]).SetValue(value)
+        
+    def OnSetConstantProp(self, event):
+        """!Change properties (color, value, resolution)
+            of currently selected constant surface"""
+        layerIdx = self.FindWindowById(self.win['constant']['surface']).GetSelection()
+        if layerIdx == wx.NOT_FOUND:
+            return
+        data = self.mapWindow.constants[layerIdx]
+        for attr in ('resolution', 'value', 'transp'):
+            data['constant'][attr] = self.FindWindowById(self.win['constant'][attr]).GetValue()
+        data['constant']['color'] = self._getColorString(
+                self.FindWindowById(self.win['constant']['color']).GetValue())
+        data['constant']['transp'] = self._getPercent(data['constant']['transp'], toPercent = False)
+        
+       # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event) 
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
     def OnFringe(self, event):
         """!Show/hide fringe"""
-        enabled = event.IsChecked()
-        win = self.FindWindowById(event.GetId())
-        
         data = self.GetLayerData('fringe')['surface']
         
         sid = data['object']['id']
@@ -1362,15 +2388,234 @@
         winName = self.__GetWindowName(win, event.GetId())
         if not winName:
             return
-        data[winName] = event.GetInt()
+        data[winName] = self.FindWindowById(event.GetId()).GetValue()
         for w in win[winName].itervalues():
             self.FindWindowById(w).SetValue(data[winName])
         
         event.Skip()
         
+    def AdjustSliderRange(self, slider, value):
+        minim, maxim = slider.GetRange()
+        if not (minim <= value <= maxim):
+            slider.SetRange(min(minim, value), max(maxim, value))
+    
+    def _createIsosurfacePanel(self, parent):
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        
+        vSizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Isosurface attributes")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        
+        self.win['volume']['attr'] = {}
+        inout = wx.CheckBox(parent = panel, id = wx.ID_ANY, 
+                            label = _("toggle normal direction"))
+        gridSizer.Add(item = inout, pos = (0,0), span = (1,2), flag = wx.ALIGN_CENTER_VERTICAL)
+        inout.Bind(wx.EVT_CHECKBOX, self.OnInOutMode)
+        self.win['volume']['inout'] = inout.GetId()
+        
+        row = 1
+        for code, attrb in (('topo', _("Isosurface value")),
+                            ('color', _("Color")),
+                            ('mask', _("Mask")),
+                            ('transp', _("Transparency")),
+                            ('shine', _("Shininess"))):
+            self.win['volume'][code] = {} 
+            # label
+            colspan = 1
+            if code == 'topo':
+                colspan = 2
+            gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                                             label = attrb + ':'),
+                          pos = (row, 0), span = (1, colspan),flag = wx.ALIGN_CENTER_VERTICAL)
+            if code != 'topo':
+                use = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
+                                 choices = [_("map")])
+            else:
+                use = None
+            # check for required properties
+            if code not in ('topo', 'color', 'shine'):
+                use.Insert(item = _("unset"), pos = 0)
+                self.win['volume'][code]['required'] = False
+            else:
+                self.win['volume'][code]['required'] = True
+            if use and code != 'mask':
+                use.Append(item = _('constant'))
+            if use:
+                self.win['volume'][code]['use'] = use.GetId()
+                use.Bind(wx.EVT_CHOICE, self.OnMapObjUse)
+                gridSizer.Add(item = use, flag = wx.ALIGN_CENTER_VERTICAL,
+                              pos = (row, 1))
+                    
+            if code != 'topo':
+                map = gselect.Select(parent = panel, id = wx.ID_ANY,
+                                     # size = globalvar.DIALOG_GSELECT_SIZE,
+                                     size = (200, -1),
+                                     type = "grid3")
+                self.win['volume'][code]['map'] = map.GetId() - 1 # FIXME
+                map.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfMap)
+                gridSizer.Add(item = map, flag = wx.ALIGN_CENTER_VERTICAL,
+                              pos = (row, 2))
+            else:
+                map = None
+            
+            if code == 'color':
+                color = UserSettings.Get(group = 'nviz', key = 'volume', subkey = ['color', 'value'])
+                value = csel.ColourSelect(panel, id = wx.ID_ANY,
+                                          colour = color,
+                                          size = globalvar.DIALOG_COLOR_SIZE)
+                value.Bind(csel.EVT_COLOURSELECT, self.OnVolumeIsosurfMap)
+                value.SetName('color')
+            elif code == 'mask':
+                value = None
+            elif code == 'topo':
+                value = NumTextCtrl(parent = panel, id = wx.ID_ANY, size = (200, -1),
+                            style = wx.TE_PROCESS_ENTER)
+                value.Bind(wx.EVT_TEXT_ENTER, self.OnVolumeIsosurfMap)
+                value.Bind(wx.EVT_KILL_FOCUS, self.OnVolumeIsosurfMap)
+##                value.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfMap)
+            else:
+                size = (65, -1)
+                value = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = size,
+                                    initial = 0)
+                if code == 'topo':
+                    value.SetRange(minVal = -1e9, maxVal = 1e9)
+                elif code in ('shine', 'transp'):
+                    value.SetRange(minVal = 0, maxVal = 100)
+                
+                value.Bind(wx.EVT_SPINCTRL, self.OnVolumeIsosurfMap)
+                value.Bind(wx.EVT_TEXT, self.OnVolumeIsosurfMap)
+            
+            if value:
+                self.win['volume'][code]['const'] = value.GetId()
+                if code == 'topo':
+                    gridSizer.Add(item = value, flag = wx.ALIGN_CENTER_VERTICAL,
+                                  pos = (row, 2))
+                else:
+                    value.Enable(False)
+                    gridSizer.Add(item = value, flag = wx.ALIGN_CENTER_VERTICAL,
+                                  pos = (row, 3))
+            else:
+                self.win['volume'][code]['const'] = None
+            
+            if code != 'topo':
+                self.SetMapObjUseMap(nvizType = 'volume',
+                                     attrb = code) # -> enable map / disable constant
+            
+            row += 1
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        vSizer.Add(item = boxSizer, proportion = 1,
+                     flag = wx.EXPAND, border = 0)
+        panel.SetSizer(vSizer)
+        
+        return panel
+    
+    def _createSlicePanel(self, parent):
+        panel = wx.Panel(parent = parent, id = wx.ID_ANY)
+        
+        vSizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
+                            label = " %s " % (_("Slice attributes")))
+        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
+        hSizer = wx.BoxSizer()
+        
+        self.win['volume']['slice'] = {}
+        hSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                      label = _("Slice parallel to axis:")), proportion = 0,
+                      flag = wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border = 3)
+        axes = wx.Choice(parent = panel, id = wx.ID_ANY, size = (65, -1), choices = ("X", "Y", "Z"))
+        hSizer.Add(axes, proportion = 0, flag = wx.ALIGN_LEFT|wx.LEFT, border = 3)
+        self.win['volume']['slice']['axes'] = axes.GetId()
+        axes.Bind(wx.EVT_CHOICE, self.OnVolumeSliceAxes)
+        boxSizer.Add(hSizer, proportion = 0, flag = wx.ALL|wx.EXPAND, border = 3)
+        
+        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
+        gridSizer.AddGrowableCol(0,1)
+        gridSizer.AddGrowableCol(1,2)
+        gridSizer.AddGrowableCol(2,2)
+        
+        # text labels
+        for i in range(2):
+            label = wx.StaticText(parent = panel, id = wx.ID_ANY)
+            label.SetName('label_edge_' + str(i))
+            gridSizer.Add(item = label, pos = (0, i + 1),
+                          flag = wx.ALIGN_CENTER)
+        for i in range(2,4):
+            label = wx.StaticText(parent = panel, id = wx.ID_ANY)
+            label.SetName('label_edge_' + str(i))
+            gridSizer.Add(item = label, pos = (3, i -1),
+                          flag = wx.ALIGN_CENTER)
+        for i in range(2):
+            label = wx.StaticText(parent = panel, id = wx.ID_ANY)
+            label.SetName('label_coord_' + str(i))
+            gridSizer.Add(item = label, pos = (i + 1, 0),
+                          flag = wx.ALIGN_CENTER_VERTICAL)
+        label = wx.StaticText(parent = panel, id = wx.ID_ANY)
+        label.SetName('label_coord_2')
+        gridSizer.Add(item = label, pos = (4, 0), 
+                          flag = wx.ALIGN_CENTER_VERTICAL)
+        # sliders
+        for i, coord in enumerate(('x1', 'x2')):
+            slider = wx.Slider(parent = panel, id = wx.ID_ANY, minValue = 0, maxValue = 100, value = 0)
+            self.win['volume']['slice']['slider_' + coord] = slider.GetId()
+            slider.Bind(wx.EVT_SPIN, self.OnSlicePositionChange)
+            slider.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.OnSlicePositionChanged)
+            gridSizer.Add(item = slider, pos = (1, i + 1), 
+                          flag = wx.ALIGN_CENTER|wx.EXPAND)
+                        
+        for i, coord in enumerate(('y1', 'y2')):
+            slider = wx.Slider(parent = panel, id = wx.ID_ANY, minValue = 0, maxValue = 100, value = 0)
+            self.win['volume']['slice']['slider_' + coord] = slider.GetId()
+            slider.Bind(wx.EVT_SPIN, self.OnSlicePositionChange)
+            slider.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.OnSlicePositionChanged)
+            gridSizer.Add(item = slider, pos = (2, i + 1), 
+                          flag = wx.ALIGN_CENTER|wx.EXPAND)
+        
+        for i, coord in enumerate(('z1', 'z2')):
+            slider = wx.Slider(parent = panel, id = wx.ID_ANY, minValue = 0, maxValue = 100, value = 0)
+            self.win['volume']['slice']['slider_' + coord] = slider.GetId()
+            slider.Bind(wx.EVT_SPIN, self.OnSlicePositionChange)
+            slider.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.OnSlicePositionChanged)
+            gridSizer.Add(item = slider, pos = (4,i+1), 
+                          flag = wx.ALIGN_CENTER|wx.EXPAND)
+                        
+        
+        boxSizer.Add(item = gridSizer, proportion = 1,
+                     flag = wx.ALL | wx.EXPAND, border = 3)
+        
+        # transparency, reset
+        hSizer = wx.BoxSizer()
+        hSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
+                      label = _("Transparency:")), proportion = 0,
+                      flag = wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, border = 7)
+        spin = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
+                           min = 0, max = 100, initial = 0)
+        spin.Bind(wx.EVT_SPINCTRL, self.OnSliceTransparency)
+        self.win['volume']['slice']['transp'] = spin.GetId()
+        hSizer.Add(item = spin, proportion = 0,
+                      flag = wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.TOP, border = 7)
+                    
+        hSizer.Add(item = wx.Size(-1, -1), proportion = 1,
+                      flag = wx.EXPAND)
+        reset = wx.Button(parent = panel, id = wx.ID_ANY, label = _("Reset"))
+        reset.Bind(wx.EVT_BUTTON, self.OnSliceReset)
+        self.win['volume']['slice']['reset'] = reset.GetId()
+        hSizer.Add(item = reset, proportion = 0,
+                      flag = wx.ALIGN_CENTER_VERTICAL|wx.TOP, border = 7)
+        
+        boxSizer.Add(hSizer, proportion = 0, flag = wx.ALL|wx.EXPAND, border = 3)
+        panel.SetSizer(boxSizer)
+        
+        return panel
+    
     def _createControl(self, parent, data, name, range, bind = (None, None, None),
-                       sliderHor = True, size = 200):
-        """!Add control (Slider + SpinCtrl)"""
+                       sliderHor = True, size = 200, floatSlider = False):
+        """!Add control (Slider + TextCtrl)"""
         data[name] = dict()
         if sliderHor:
             style = wx.SL_HORIZONTAL | wx.SL_AUTOTICKS | \
@@ -1381,31 +2626,62 @@
                 wx.SL_INVERSE
             sizeW = (-1, size)
         
-        slider = wx.Slider(parent = parent, id = wx.ID_ANY,
+        kwargs = dict(parent = parent, id = wx.ID_ANY,
                            minValue = range[0],
                            maxValue = range[1],
                            style = style,
                            size = sizeW)
+        if floatSlider:
+            slider = FloatSlider(**kwargs)
+        else:
+            slider = wx.Slider(**kwargs)
+            
         slider.SetName('slider')
         if bind[0]:
-            slider.Bind(wx.EVT_SCROLL, bind[0])
+            #EVT_SCROLL emits event after slider is released, EVT_SPIN not
+            slider.Bind(wx.EVT_SPIN, bind[0])
         
-        # slider.Bind(wx.EVT_SCROLL_THUMBRELEASE, bind[1])
         if bind[1]:
-            slider.Bind(wx.EVT_SCROLL_CHANGED, bind[1]) # this only works in MSW
+            slider.Bind(wx.EVT_SCROLL_THUMBRELEASE, bind[1]) 
         data[name]['slider'] = slider.GetId()
         
-        spin = wx.SpinCtrl(parent = parent, id = wx.ID_ANY, size = (65, -1),
-                           min = range[0],
-                           max = range[1])
+        text = NumTextCtrl(parent = parent, id = wx.ID_ANY, size = (65, -1),
+                            style = wx.TE_PROCESS_ENTER)
         
-        # no 'changed' event ... (FIXME)
-        spin.SetName('spin')
+        text.SetName('text')
         if bind[2]:
-            spin.Bind(wx.EVT_SPINCTRL, bind[2])
+            text.Bind(wx.EVT_TEXT_ENTER, bind[2])
+            text.Bind(wx.EVT_KILL_FOCUS, bind[2])
         
-        data[name]['spin'] = spin.GetId()
+        data[name]['text'] = text.GetId()
         
+    def _createCompass(self, panel, sizer, type):
+        """!Create 'compass' widget for light and view page"""
+        w = wx.Button(panel, id = wx.ID_ANY, label = _("W"))
+        n = wx.Button(panel, id = wx.ID_ANY, label = _("N"))
+        s = wx.Button(panel, id = wx.ID_ANY, label = _("S"))
+        e = wx.Button(panel, id = wx.ID_ANY, label = _("E"))
+        nw = wx.Button(panel, id = wx.ID_ANY, label = _("NW"))
+        ne = wx.Button(panel, id = wx.ID_ANY, label = _("NE"))
+        se = wx.Button(panel, id = wx.ID_ANY, label = _("SE"))
+        sw = wx.Button(panel, id = wx.ID_ANY, label = _("SW"))
+        minWidth = sw.GetTextExtent(sw.GetLabel())[0] + 15
+        for win, name in zip((w, n, s, e, nw, ne, se, sw),
+                        ('w', 'n', 's', 'e', 'nw', 'ne', 'se', 'sw')):
+            win.SetMinSize((minWidth, -1))
+            win.Bind(wx.EVT_BUTTON, self.OnLookFrom)
+            win.SetName(type + '_' + name)
+        sizer.Add(item = nw, pos = (0, 0), flag = wx.ALIGN_CENTER)
+        sizer.Add(item = n, pos = (0, 1), flag = wx.ALIGN_CENTER)
+        sizer.Add(item = ne, pos = (0, 2), flag = wx.ALIGN_CENTER)
+        sizer.Add(item = e, pos = (1, 2), flag = wx.ALIGN_CENTER)
+        sizer.Add(item = se, pos = (2, 2), flag = wx.ALIGN_CENTER)
+        sizer.Add(item = s, pos = (2, 1), flag = wx.ALIGN_CENTER)
+        sizer.Add(item = sw, pos = (2, 0), flag = wx.ALIGN_CENTER)
+        sizer.Add(item = w, pos = (1, 0), flag = wx.ALIGN_CENTER)
+        
+        
+        
     def __GetWindowName(self, data, id):
         for name in data.iterkeys():
             if type(data[name]) is type({}):
@@ -1425,16 +2701,15 @@
                         'persp',
                         'twist',
                         'z-exag'):
-            for win in self.win['view'][control].itervalues():                
-                if control == 'height':
-                    value = UserSettings.Get(group = 'nviz', key = 'view',
-                                             subkey = ['height', 'value'], internal = True)
-                else:
-                    try:
+            for win in self.win['view'][control].itervalues():             
+                try:
+                    if control == 'height':
+                        value = int(self.mapWindow.iview[control]['value'])
+                    else:
                         value = self.mapWindow.view[control]['value']
-                    except KeyError:
-                        value = -1
-                
+                except KeyError:
+                    value = -1
+                        
                 self.FindWindowById(win).SetValue(value)
         
         viewWin = self.FindWindowById(self.win['view']['position'])
@@ -1443,13 +2718,14 @@
         viewWin.Draw(pos = (x, y), scale = True)
         viewWin.Refresh(False)
         
-        # bgcolor = self.FindWindowById(self.win['settings']['general']['bgcolor']).GetColour()
-        # self.OnBgColor(event = bgcolor)
+        color = self._getColorString(self.mapWindow.view['background']['color'])
+        self._display.SetBgColor(str(color))
+        
         self.Update()
         
         self.mapWindow.Refresh(eraseBackground = False)
         self.mapWindow.render['quick'] = False
-        self.mapWindow.Refresh(False)
+        self.mapWindow.Refresh(True)
         
     def OnShowLightModel(self, event):
         """!Show light model"""
@@ -1457,49 +2733,49 @@
         self._display.DrawLightingModel()
         
     def OnLightChange(self, event):
-        """!Position of the light changed"""
+        """!Position of the light changing"""
         winName = self.__GetWindowName(self.win['light'], event.GetId())
         if not winName:
             return
         
-        val = event.GetInt()
-        self.mapWindow.light['position']['z'] = val
+        value = self.FindWindowById(event.GetId()).GetValue()
+        
+        self.mapWindow.light['position']['z'] = value
         for win in self.win['light'][winName].itervalues():
-            self.FindWindowById(win).SetValue(val)
+            self.FindWindowById(win).SetValue(value)
+            
+        self.PostLightEvent()
         
-        event = wxUpdateLight()
-        wx.PostEvent(self.mapWindow, event)
+        event.Skip()
         
-        event.Skip()
-
+    def OnLightChanged(self, event):
+        """!Light changed"""
+        self.PostLightEvent(refresh = True)
+        
     def OnLightColor(self, event):
         """!Color of the light changed"""
-        self.mapWindow.light['color'] = event.GetValue()
+        self.mapWindow.light['color'] = tuple(event.GetValue())
         
-        event = wxUpdateLight()
-        wx.PostEvent(self.mapWindow, event)
+        self.PostLightEvent(refresh = True)
         
         event.Skip()
         
     def OnLightValue(self, event):
-        """!Light brightness changed"""
+        """!Light brightness/ambient changing"""
         data = self.mapWindow.light
         self.OnScroll(event, self.win['light'], data)
         
-        event = wxUpdateLight()
-        wx.PostEvent(self.mapWindow, event)
-        
+        self.PostLightEvent()
         event.Skip()
         
     def OnBgColor(self, event):
         """!Background color changed"""
         color = event.GetValue()
-        self.mapWindow.view['background']['color'] = event.GetValue()
+        self.mapWindow.view['background']['color'] = tuple(color)
         color = str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
-        
         self._display.SetBgColor(str(color))
         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
         
     def OnSetSurface(self, event):
@@ -1522,7 +2798,7 @@
         except:
             self.EnablePage('surface', False)
             return
-        
+
         layer = self.mapWindow.GetLayerByName(name, mapType = 'raster')
         self.EnablePage('surface', True)
         self.UpdateSurfacePage(layer, data, updateName = False)
@@ -1535,7 +2811,6 @@
         except:
             self.EnablePage('vector', False)
             return
-        
         layer = self.mapWindow.GetLayerByName(name, mapType = 'vector')
         self.EnablePage('vector', True)
         self.UpdateVectorPage(layer, data, updateName = False)
@@ -1560,23 +2835,34 @@
         if not winName:
             return
         
+        value = self.FindWindowById(event.GetId()).GetValue()
+        slider = self.FindWindowById(self.win['view'][winName]['slider'])
+        self.AdjustSliderRange(slider = slider, value = value)
+        
         if winName == 'height':
             view = self.mapWindow.iview # internal
         else:
             view = self.mapWindow.view
         
-        if winName == 'z-exag' and event.GetInt() >= 0:
+        if winName == 'z-exag' and value >= 0:
             self.PostViewEvent(zExag = True)
         else:
             self.PostViewEvent(zExag = False)
         
-        view[winName]['value'] = event.GetInt()
+        if winName in ('persp', 'twist'):
+            convert = int
+        else:
+            convert = float
         
+        view[winName]['value'] = convert(value)
+            
         for win in self.win['view'][winName].itervalues():
-            self.FindWindowById(win).SetValue(view[winName]['value'])
-                
+            self.FindWindowById(win).SetValue(value)
+
+        self.mapWindow.iview['dir']['use'] = False
         self.mapWindow.render['quick'] = True
-        self.mapWindow.Refresh(False)
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
         
         event.Skip()
         
@@ -1584,68 +2870,122 @@
         """!View changed, render in full resolution"""
         self.mapWindow.render['quick'] = False
         self.mapWindow.Refresh(False)
-        
         self.UpdateSettings()
-        
-    def OnViewChangedSpin(self, event):
-        """!View changed, render in full resolution"""
+        try:# when calling event = None
+            event.Skip()
+        except AttributeError:
+            pass
+            
+    def OnViewChangedText(self, event):
+        """!View changed, render in full resolution""" 
         self.mapWindow.render['quick'] = False
         self.OnViewChange(event)
         self.OnViewChanged(None)
         self.Update()
         
         event.Skip()
-        
+    
+    def OnLookAt(self, event):
+        """!Look here/center"""
+        name = self.FindWindowById(event.GetId()).GetName()
+        if name == 'center':
+            self._display.LookAtCenter()
+            focus = self.mapWindow.iview['focus']
+            focus['x'], focus['y'], focus['z'] = self._display.GetFocus()
+            self.mapWindow.saveHistory = True
+            self.mapWindow.Refresh(False)
+        elif name == 'top':
+            self.mapWindow.view['position']['x'] = 0.5
+            self.mapWindow.view['position']['y'] = 0.5
+            self.PostViewEvent(zExag = True)
+            self.UpdateSettings()
+            self.mapWindow.Refresh(False)
+        else: # here
+            if self.FindWindowById(event.GetId()).GetValue():
+                self.mapDisplay.Raise()
+                self.mapWindow.mouse['use'] = 'lookHere'
+                self.mapWindow.SetCursor(self.mapWindow.cursors["cross"])
+            else:
+                self.mapWindow.mouse['use'] = 'default'
+                self.mapWindow.SetCursor(self.mapWindow.cursors['default'])
+            
     def OnResetView(self, event):
         """!Reset to default view (view page)"""
         self.mapWindow.ResetView()
         self.UpdateSettings()
         self.mapWindow.Refresh(False)
         
-    def OnLookAt(self, event):
-        """!Look at (view page)"""
-        sel = event.GetSelection()
-        if sel == 0: # top
-            self.mapWindow.view['position']['x'] = 0.5
-            self.mapWindow.view['position']['y'] = 0.5
-        elif sel == 1: # north
-            self.mapWindow.view['position']['x'] = 0.5
-            self.mapWindow.view['position']['y'] = 0.0
-        elif sel == 2: # south
-            self.mapWindow.view['position']['x'] = 0.5
-            self.mapWindow.view['position']['y'] = 1.0
-        elif sel == 3: # east
-            self.mapWindow.view['position']['x'] = 1.0
-            self.mapWindow.view['position']['y'] = 0.5
-        elif sel == 4: # west
-            self.mapWindow.view['position']['x'] = 0.0
-            self.mapWindow.view['position']['y'] = 0.5
-        elif sel == 5: # north-west
-            self.mapWindow.view['position']['x'] = 0.0
-            self.mapWindow.view['position']['y'] = 0.0
-        elif sel == 6: # north-east
-            self.mapWindow.view['position']['x'] = 1.0
-            self.mapWindow.view['position']['y'] = 0.0
-        elif sel == 7: # south-east
-            self.mapWindow.view['position']['x'] = 1.0
-            self.mapWindow.view['position']['y'] = 1.0
-        elif sel == 8: # south-west
-            self.mapWindow.view['position']['x'] = 0.0
-            self.mapWindow.view['position']['y'] = 1.0
+    def OnResetSurfacePosition(self, event):
+        """!Reset position of surface"""
         
-        self.PostViewEvent(zExag = True)
+        for win in self.win['surface']['position'].itervalues():
+            if win == self.win['surface']['position']['axis']:
+                self.FindWindowById(win).SetSelection(0)
+            elif win == self.win['surface']['position']['reset']:
+                continue
+            else:
+                self.FindWindowById(win).SetValue(0)
+                
+        data = self.GetLayerData('surface')
+        data['surface']['position']['x'] = 0
+        data['surface']['position']['y'] = 0
+        data['surface']['position']['z'] = 0
+        data['surface']['position']['update'] = None
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
         
-        self.UpdateSettings()
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+            
+    def OnLookFrom(self, event):
+        """!Position of view/light changed by buttons"""
+        name = self.FindWindowById(event.GetId()).GetName()
+        buttonName = name.split('_')[1]
+        if name.split('_')[0] == 'view':
+            type = 'view'
+            data = self.mapWindow.view
+        else:
+            type = 'light'
+            data = self.mapWindow.light
+        if buttonName == 'n': # north
+            data['position']['x'] = 0.5
+            data['position']['y'] = 0.0
+        elif buttonName == 's': # south
+            data['position']['x'] = 0.5
+            data['position']['y'] = 1.0
+        elif buttonName == 'e': # east
+            data['position']['x'] = 1.0
+            data['position']['y'] = 0.5
+        elif buttonName =='w': # west
+            data['position']['x'] = 0.0
+            data['position']['y'] = 0.5
+        elif buttonName == 'nw': # north-west
+            data['position']['x'] = 0.0
+            data['position']['y'] = 0.0
+        elif buttonName == 'ne': # north-east
+            data['position']['x'] = 1.0
+            data['position']['y'] = 0.0
+        elif buttonName == 'se': # south-east
+            data['position']['x'] = 1.0
+            data['position']['y'] = 1.0
+        elif buttonName == 'sw': # south-west
+            data['position']['x'] = 0.0
+            data['position']['y'] = 1.0
+        if type == 'view':    
+            self.PostViewEvent(zExag = True)
+            
+            self.UpdateSettings()
+        else:
+            self.PostLightEvent()
+            lightWin = self.FindWindowById(self.win['light']['position'])
+            x, y = lightWin.UpdatePos(self.mapWindow.light['position']['x'],
+                                     self.mapWindow.light['position']['y'])
+            lightWin.Draw(pos = (x, y), scale = True)
+            lightWin.Refresh(False)
         self.mapWindow.render['quick'] = False
         self.mapWindow.Refresh(False)
 
-    def OnClose(self, event):
-        """!Close button pressed
-        
-        Close dialog
-        """
-        self.Hide()
-        
     def OnMapObjUse(self, event):
         """!Set surface attribute -- use -- map/constant"""
         if not self.mapWindow.init:
@@ -1676,7 +3016,7 @@
                 value = self.FindWindowById(self.win[nvizType][attrb]['const']).GetColour()
                 value = self._getColorString(value)
             else:
-                value = self.FindWindowById(self.win[nvizType][attrb]['const']).GetValue()
+                value = self._getPercent(self.FindWindowById(self.win[nvizType][attrb]['const']).GetValue(), toPercent = False)
         
         self.SetMapObjUseMap(nvizType = nvizType,
                              attrb = attrb, map = useMap)
@@ -1691,26 +3031,32 @@
             data = self.mapWindow.GetLayerByName(name, mapType = '3d-raster', dataType = 'nviz')
             list = self.FindWindowById(self.win['volume']['isosurfs'])
             id = list.GetSelection()
-            data[nvizType]['isosurface'][id][attrb] = { 'map' : useMap,
-                                                        'value' : str(value),
-                                                        'update' : None }
+            if id != -1:
+                data[nvizType]['isosurface'][id][attrb] = { 'map' : useMap,
+                                                            'value' : str(value),
+                                                            'update' : None }
         
         # update properties
         event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
-        
+ 
     def EnablePage(self, name, enabled = True):
         """!Enable/disable all widgets on page"""
         for key, item in self.win[name].iteritems():
-            if key == 'map' or key == 'surface':
+            if key in ('map', 'surface', 'new','planes'):
                 continue
             if type(item) == types.DictType:
-                for sitem in self.win[name][key].itervalues():
-                    if type(sitem) == types.IntType:
-                        self.FindWindowById(sitem).Enable(enabled)
+                for skey, sitem in self.win[name][key].iteritems():
+                    if type(sitem) == types.DictType:
+                        for ssitem in self.win[name][key][skey].itervalues():
+                            if type(ssitem) == types.IntType:
+                                self.FindWindowById(ssitem).Enable(enabled)
+                    else:
+                        if type(sitem) == types.IntType:
+                            self.FindWindowById(sitem).Enable(enabled)
             else:
                 if type(item) == types.IntType:
                     self.FindWindowById(item).Enable(enabled)
@@ -1721,7 +3067,8 @@
             incSel = -1 # decrement selection (no 'unset')
         else:
             incSel = 0
-        
+        if nvizType == 'volume' and attrb == 'topo':
+            return
         if map is True: # map
             if attrb != 'topo': # changing map topography not allowed
                 # not sure why, but here must be disabled both ids, should be fixed!
@@ -1735,13 +3082,17 @@
                 self.FindWindowById(self.win[nvizType][attrb]['const']).Enable(True)
             self.FindWindowById(self.win[nvizType][attrb]['use']).SetSelection(2 + incSel)
         else: # unset
+            self.FindWindowById(self.win[nvizType][attrb]['use']).SetSelection(0)
             self.FindWindowById(self.win[nvizType][attrb]['map'] + 1).Enable(False)
             if self.win[nvizType][attrb]['const']:
                 self.FindWindowById(self.win[nvizType][attrb]['const']).Enable(False)
-            self.FindWindowById(self.win[nvizType][attrb]['use']).SetSelection(0)
+            
         
     def OnSurfaceMap(self, event):
         """!Set surface attribute"""
+        if self.vetoGSelectEvt:
+            self.vetoGSelectEvt = False
+            return
         self.SetMapObjAttrb(nvizType = 'surface', winId = event.GetId())
         
     def SetMapObjAttrb(self, nvizType, winId):
@@ -1749,32 +3100,34 @@
         if not self.mapWindow.init:
             return
         
-        attrb = self.__GetWindowName(self.win[nvizType], winId) 
+        attrb = self.__GetWindowName(self.win[nvizType], winId)
         if not attrb:
             return
         
-        if nvizType == 'volume' and attrb == 'topo':
-            return
-        
-        selection = self.FindWindowById(self.win[nvizType][attrb]['use']).GetSelection()
-        if self.win[nvizType][attrb]['required']:
-            selection += 1
-        
-        if selection == 0: # unset
+        if not (nvizType == 'volume' and attrb == 'topo'):
+            selection = self.FindWindowById(self.win[nvizType][attrb]['use']).GetSelection()
+            if self.win[nvizType][attrb]['required']:
+                selection += 1
+
+            if selection == 0: # unset
+                useMap = None
+                value = ''
+            elif selection == 1: # map
+                value = self.FindWindowById(self.win[nvizType][attrb]['map']).GetValue()
+                useMap = True
+            else: # constant
+                if attrb == 'color':
+                    value = self.FindWindowById(self.win[nvizType][attrb]['const']).GetColour()
+                    # tuple to string
+                    value = self._getColorString(value)
+                else:
+                    value = self._getPercent(
+                        self.FindWindowById(self.win[nvizType][attrb]['const']).GetValue(), toPercent = False)
+                    
+                useMap = False
+        else:
             useMap = None
-            value = ''
-        elif selection == 1: # map
-            value = self.FindWindowById(self.win[nvizType][attrb]['map']).GetValue()
-            useMap = True
-        else: # constant
-            if attrb == 'color':
-                value = self.FindWindowById(self.win[nvizType][attrb]['const']).GetColour()
-                # tuple to string
-                value = self._getColorString(value)
-            else:
-                value = self.FindWindowById(self.win[nvizType][attrb]['const']).GetValue()
-            useMap = False
-        
+            value = self.FindWindowById(self.win[nvizType][attrb]['const']).GetValue()
         if not self.pageChanging:
             name = self.FindWindowById(self.win[nvizType]['map']).GetValue()
             if nvizType == 'surface':
@@ -1790,21 +3143,27 @@
                     data[nvizType]['isosurface'][id][attrb] = { 'map' : useMap,
                                                                 'value' : str(value),
                                                                 'update' : None }
+                    if attrb == 'topo':
+                        list = self.FindWindowById(self.win['volume']['isosurfs'])
+                        sel = list.GetSelection()
+                        list.SetString(sel, "%s %s" % (_("Level"), str(value)))
+                        list.Check(sel)
             
             # update properties
             event = wxUpdateProperties(data = data)
             wx.PostEvent(self.mapWindow, event)
             
-            if self.mapDisplay.statusbarWin['render'].IsChecked():
+            if self.mapDisplay.IsAutoRendered():
                 self.mapWindow.Refresh(False)
         
     def OnSurfaceResolution(self, event):
         """!Draw resolution changed"""
         self.SetSurfaceResolution()
         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
-        
+    
+
     def SetSurfaceResolution(self):
         """!Set draw resolution"""
         coarse = self.FindWindowById(self.win['surface']['draw']['res-coarse']).GetValue()
@@ -1822,17 +3181,11 @@
     def SetSurfaceMode(self):
         """!Set draw mode"""
         mode = self.FindWindowById(self.win['surface']['draw']['mode']).GetSelection()
-        if mode == 0: # coarse
-            self.FindWindowById(self.win['surface']['draw']['res-coarse']).Enable(True)
-            self.FindWindowById(self.win['surface']['draw']['res-fine']).Enable(False)
-        elif mode == 1: # fine
-            self.FindWindowById(self.win['surface']['draw']['res-coarse']).Enable(False)
-            self.FindWindowById(self.win['surface']['draw']['res-fine']).Enable(True)
-        else: # both
-            self.FindWindowById(self.win['surface']['draw']['res-coarse']).Enable(True)
-            self.FindWindowById(self.win['surface']['draw']['res-fine']).Enable(True)
-        
         style = self.FindWindowById(self.win['surface']['draw']['style']).GetSelection()
+        if style == 0: # wire
+            self.FindWindowById(self.win['surface']['draw']['wire-color']).Enable(True)
+        elif style == 1: # surface
+            self.FindWindowById(self.win['surface']['draw']['wire-color']).Enable(False)
         
         shade = self.FindWindowById(self.win['surface']['draw']['shading']).GetSelection()
         
@@ -1853,7 +3206,7 @@
         event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
 
     def OnSurfaceModeAll(self, event):
@@ -1884,13 +3237,48 @@
             event = wxUpdateProperties(data = data)
             wx.PostEvent(self.mapWindow, event)
             
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
         
     def _getColorString(self, color):
-        """!Set wire color"""
+        """!Convert color tuple to R:G:B format
+
+        @param color tuple
+        
+        @return string R:G:B
+        """
         return str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
     
+    def _getColorFromString(self, color, delim = ':'):
+        """!Convert color string (R:G:B) to wx.Color
+
+        @param color string
+        @param delim delimiter
+
+        @return wx.Color instance
+        """
+        return wx.Color(*map(int, color.split(delim)))
+    
+    def _get3dRange(self, name):
+        """!Gelper func for getting range of 3d map"""
+        ret = gcmd.RunCommand('r3.info', read = True, flags = 'r', map = name)
+        if ret:
+            range = []
+            for value in ret.strip('\n').split('\n'):
+                range.append(float(value.split('=')[1]))
+            return range
+        
+        return -1e6, 1e6
+    
+    def _getPercent(self, value, toPercent = True):
+        """!Convert values 0 - 255 to percents and vice versa"""
+        value = int(value)
+        if toPercent:
+            value = int(value/255. * 100)
+        else:
+            value = int(value/100. * 255)
+        return value
+    
     def OnSurfaceWireColor(self, event):
         """!Set wire color"""
         data = self.GetLayerData('surface')
@@ -1902,7 +3290,7 @@
         event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
         
     def OnSurfaceAxis(self, event):
@@ -1912,19 +3300,25 @@
         
         axis = self.FindWindowById(self.win['surface']['position']['axis']).GetSelection()
         slider = self.FindWindowById(self.win['surface']['position']['slider'])
-        spin = self.FindWindowById(self.win['surface']['position']['spin'])
+        text = self.FindWindowById(self.win['surface']['position']['text'])
+        xydim = self._display.GetLongDim()
+        zdim = self._display.GetZRange()
+        zdim = zdim[1] - zdim[0]
         
         x, y, z = self._display.GetSurfacePosition(id)
         
         if axis == 0: # x
+            slider.SetRange(-3 * xydim, 3 * xydim)
             slider.SetValue(x)
-            spin.SetValue(x)
+            text.SetValue(x)
         elif axis == 1: # y
+            slider.SetRange(-3 * xydim, 3 * xydim)
             slider.SetValue(y)
-            spin.SetValue(y)
+            text.SetValue(y)
         else: # z
+            slider.SetRange(-3 * zdim, 3 * zdim)
             slider.SetValue(z)
-            spin.SetValue(z)
+            text.SetValue(z)
         
     def OnSurfacePosition(self, event):
         """!Surface position"""
@@ -1932,10 +3326,14 @@
         if not winName:
             return
         axis = self.FindWindowById(self.win['surface']['position']['axis']).GetSelection()
-        value = event.GetInt()
         
+        value = self.FindWindowById(event.GetId()).GetValue()
+        slider = self.FindWindowById(self.win['surface'][winName]['slider'])
+        self.AdjustSliderRange(slider = slider, value = value)
+        
         for win in self.win['surface']['position'].itervalues():
-            if win == self.win['surface']['position']['axis']:
+            if win in (self.win['surface']['position']['axis'],
+                       self.win['surface']['position']['reset']):
                 continue
             else:
                 self.FindWindowById(win).SetValue(value)
@@ -1951,19 +3349,30 @@
         else: # z
             z = value
         
-        data = self.GetLayerData('surface')
         data['surface']['position']['x'] = x
         data['surface']['position']['y'] = y
         data['surface']['position']['z'] = z
         data['surface']['position']['update'] = None
         # update properties
+        
         event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        self.mapWindow.render['quick'] = True
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
         #        self.UpdatePage('surface')
+        
+    def OnSurfacePositionChanged(self, event):
+        """!Surface position changed"""
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.Refresh(False)
 
+    def OnSurfacePositionText(self, event):
+        """!Surface position changed by textctrl"""
+        self.OnSurfacePosition(event)
+        self.OnSurfacePositionChanged(None)
+        
     def UpdateVectorShow(self, vecType, enabled):
         """!Enable/disable lines/points widgets
         
@@ -2005,9 +3414,9 @@
         data = self.GetLayerData('vector')['vector']
         
         if checked:
-            self.mapWindow.LoadVector(item, points = points)
+            self.mapWindow.LoadVector(item, points = points, append = False)
         else:
-            self.mapWindow.UnloadVector(item, points = points)
+            self.mapWindow.UnloadVector(item, points = points, remove = False)
         
         self.UpdateVectorShow(vecType, checked)
         
@@ -2024,7 +3433,7 @@
                 event = wxUpdateProperties(data = data)
                 wx.PostEvent(self.mapWindow, event)
         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
         
         event.Skip()
@@ -2059,15 +3468,23 @@
         mode = {}
         if self.FindWindowById(self.win['vector']['lines']['flat']).GetSelection() == 0:
             mode['type'] = 'surface'
-            mode['surface'] = self.FindWindowById(self.win['vector']['lines']['surface']).GetValue()
-            mode['update'] = None
+            mode['surface'] = {}
+            checklist = self.FindWindowById(self.win['vector']['lines']['surface'])
+            value = list()
+            checked = list()
+            for surface in range(checklist.GetCount()):
+                value.append(checklist.GetString(surface))
+                checked.append(checklist.IsChecked(surface))
+                    
+            mode['surface']['value'] = value
+            mode['surface']['show'] = checked
         else:
             mode['type'] = 'flat'
         
         for attrb in ('width', 'mode'):
             data['vector']['lines'][attrb]['update'] = None
         data['vector']['lines']['width']['value'] = width
-        data['vector']['lines']['mode']['value'] = mode
+        data['vector']['lines']['mode'] = mode
         
         color = self.FindWindowById(self.win['vector']['lines']['color']).GetColour()
         
@@ -2082,31 +3499,29 @@
         event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
                         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
         
     def OnVectorHeight(self, event):
-        value = event.GetInt()
         id = event.GetId()
-        if id == self.win['vector']['lines']['height']['spin'] or \
-                id == self.win['vector']['lines']['height']['slider']:
+        if id in self.win['vector']['lines']['height'].values():
             vtype = 'lines'
         else:
             vtype = 'points'
         
-        if type(event) == type(wx.ScrollEvent()):
-            # slider
-            win = self.FindWindowById(self.win['vector'][vtype]['height']['spin'])
-        else:
-            # spin
-            win = self.FindWindowById(self.win['vector'][vtype]['height']['slider'])
-        win.SetValue(value)
-
-        data = self.GetLayerData('vector')['vector'][vtype]
-        data['height'] = { 'value' : value,
-                           'update' : None }
+        value = self.FindWindowById(id).GetValue()
+        slider = self.FindWindowById(self.win['vector'][vtype]['height']['slider'])
+        self.AdjustSliderRange(slider = slider, value = value)
         
+        for win in self.win['vector'][vtype]['height'].itervalues():
+            self.FindWindowById(win).SetValue(value)
+        
+        data = self.GetLayerData('vector')
+        data['vector'][vtype]['height'] = { 'value' : value,
+                                            'update' : None }
+        
         # update properties
+        
         event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
@@ -2119,10 +3534,9 @@
     def OnVectorHeightFull(self, event):
         """!Vector height changed, render in full resolution"""
         self.OnVectorHeight(event)
-        self.OnVectorSurface(event)
+##        self.OnVectorSurface(event)
         id = event.GetId()
-        if id == self.win['vector']['lines']['height']['spin'] or \
-                id == self.win['vector']['lines']['height']['slider']:
+        if id in self.win['vector']['lines']['height'].values():
             vtype = 'lines'
         else:
             vtype = 'points'
@@ -2131,30 +3545,38 @@
         self.mapWindow.render['v' + vtype] = False
         self.mapWindow.Refresh(False)
 
-    def OnVectorHeightSpin(self, event):
+    def OnVectorHeightText(self, event):
         """!Vector height changed, render in full resolution"""
-        # TODO: use step value instead
         
         #        self.OnVectorHeight(event)
         self.OnVectorHeightFull(event)
-        
+    
     def OnVectorSurface(self, event):
-        """!Reference surface for vector map (lines/points)"""
+        """!Reference surface for vector map (lines/points)"""   
         id = event.GetId()
         if id == self.win['vector']['lines']['surface']:
             vtype = 'lines'
         else:
             vtype = 'points'
+        checkList = self.FindWindowById(self.win['vector'][vtype]['surface'])
+        checked = []
+        surfaces = []
+        for items in range(checkList.GetCount()):
+            checked.append(checkList.IsChecked(items))
+            surfaces.append(checkList.GetString(items))
+        
         data = self.GetLayerData('vector')
-        data['vector'][vtype]['mode']['surface'] = { 'value' : event.GetString(),
-                                                     'update' : None }
+        data['vector'][vtype]['mode']['surface'] = { 'value' : surfaces,
+                                                     'show'  : checked}
+        data['vector'][vtype]['mode']['update'] = None 
         
         # update properties
         event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
+            
         
     def OnVectorPoints(self, event):
         """!Set vector points mode, apply changes if auto-rendering is enabled"""
@@ -2182,17 +3604,18 @@
         event = wxUpdateProperties(data = data)
         wx.PostEvent(self.mapWindow, event)
         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
-
+        
+        
     def UpdateIsosurfButtons(self, list):
         """!Enable/disable buttons 'add', 'delete',
         'move up', 'move down'"""
         nitems = list.GetCount()
-        add = self.parent.FindWindowById(self.win['volume']['btnIsosurfAdd'])
-        delete = self.parent.FindWindowById(self.win['volume']['btnIsosurfDelete'])
-        moveDown = self.parent.FindWindowById(self.win['volume']['btnIsosurfMoveDown'])
-        moveUp = self.parent.FindWindowById(self.win['volume']['btnIsosurfMoveUp'])
+        add = self.parent.FindWindowById(self.win['volume']['btnAdd'])
+        delete = self.parent.FindWindowById(self.win['volume']['btnDelete'])
+        moveDown = self.parent.FindWindowById(self.win['volume']['btnMoveDown'])
+        moveUp = self.parent.FindWindowById(self.win['volume']['btnMoveUp'])
         if nitems >= wxnviz.MAX_ISOSURFS:
             # disable add button on max
             add.Enable(False)
@@ -2216,13 +3639,42 @@
             moveUp.Enable(False)
         else:
             moveUp.Enable(True)
+            
+    def OnVolumeMode(self, event):
+        """!Change mode isosurfaces/slices"""
+        mode = self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection()
+        data = self.GetLayerData('volume')['volume']
         
-    def OnVolumeIsosurfMode(self, event):
+        sizer = self.isoPanel.GetContainingSizer()
+        sizer = self.slicePanel.GetContainingSizer()
+        listBox = self.FindWindowByName('listStaticBox')
+        if mode == 0:
+            sizer.Show(self.isoPanel)
+            sizer.Hide(self.slicePanel)
+            listBox.SetLabel(" %s " % _("List of isosurfaces"))
+            data['draw']['mode']['value'] = 0
+            data['draw']['mode']['desc'] = 'isosurface'
+        else:
+            sizer.Hide(self.isoPanel)
+            sizer.Show(self.slicePanel)
+            listBox.SetLabel(" %s " % _("List of slices"))
+            data['draw']['mode']['value'] = 1
+            data['draw']['mode']['desc'] = 'slice'
+        
+        if event:
+            name = self.FindWindowById(self.win['volume']['map']).GetValue()
+            layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
+            self.UpdateVolumePage(layer, data, updateName = False)
+            
+        sizer.Layout()
+        listBox.GetParent().Fit()
+            
+    def OnVolumeDrawMode(self, event):
+        """!Set isosurface/slice draw mode"""
+        self.SetVolumeDrawMode(event.GetSelection())
+        
+    def SetVolumeDrawMode(self, selection):
         """!Set isosurface draw mode"""
-        self.SetIsosurfaceMode(event.GetSelection())
-    
-    def SetIsosurfaceMode(self, selection):
-        """!Set isosurface draw mode"""
         data = self.GetLayerData('volume')['volume']
         id = data['object']['id']
         
@@ -2231,55 +3683,113 @@
             mode |= wxnviz.DM_FLAT
         else:
             mode |= wxnviz.DM_GOURAUD
+            
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            self._display.SetIsosurfaceMode(id, mode)
+            data['draw']['shading']['isosurface']['desc'] = 'gouraud'
+            data['draw']['shading']['isosurface']['value'] = mode
+        else:
+            self._display.SetSliceMode(id, mode)
+            data['draw']['shading']['slice']['desc'] = 'flat'
+            data['draw']['shading']['slice']['value'] = mode
         
-        self._display.SetIsosurfaceMode(id, mode)
-        
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
         
-    def OnVolumeIsosurfResolution(self, event):
-        """!Set isosurface draw resolution"""
-        self.SetIsosurfaceResolution(event.GetInt())
+    def OnVolumeResolution(self, event):
+        """!Set isosurface/slice draw resolution"""
+        self.SetVolumeResolution(event.GetInt())
         
-    def SetIsosurfaceResolution(self, res):
+    def SetVolumeResolution(self, res):
         """!Set isosurface draw resolution"""
         data = self.GetLayerData('volume')['volume']
+        id = data['object']['id']
         
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            self._display.SetIsosurfaceRes(id, res)
+            data['draw']['resolution']['isosurface']['value'] = res
+        else:
+            self._display.SetSliceRes(id, res)
+            data['draw']['resolution']['slice']['value'] = res
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+    
+    def OnInOutMode(self, event):
+        """!Change isosurfaces mode inout"""
+        data = self.GetLayerData('volume')['volume']
         id = data['object']['id']
-        self._display.SetIsosurfaceRes(id, res)
+        isosurfId = self.FindWindowById(self.win['volume']['isosurfs']).GetSelection()
         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        ret = self._display.SetIsosurfaceInOut(id, isosurfId, event.GetInt())
+        if ret == 1:
+            data['isosurface'][isosurfId]['inout'] = event.GetInt()
+            
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
+    
         
     def OnVolumeIsosurfMap(self, event):
         """!Set surface attribute"""
+        if self.vetoGSelectEvt:
+            self.vetoGSelectEvt = False
+            return
         self.SetMapObjAttrb(nvizType = 'volume', winId = event.GetId())
         
-    def OnVolumeIsosurfCheck(self, event):
-        """!Isosurface checked (->load) or unchecked (->unload)"""
+    def OnVolumeCheck(self, event):
+        """!Isosurface/slice checked (->load) or unchecked (->unload)"""
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            mode = 'isosurf'
+        else:
+            mode = 'slice'
         index = event.GetSelection()
-        list = self.FindWindowById(self.win['volume']['isosurfs'])
+        list = self.FindWindowById(self.win['volume'][mode + 's'])
         
         data = self.GetLayerData('volume')['volume']
-        id = data['object']['id']
+        vid = data['object']['id']
         
-        isosurfId = event.GetSelection()
+        id = event.GetSelection()
         
-        if list.IsChecked(index):
-            self._display.SetIsosurfaceTransp(id, isosurfId, False, "0")
+        if mode == 'isosurf':
+            if list.IsChecked(index):
+                if 'transp' in data['isosurface'][id] and\
+                    data['isosurface'][id]['transp']['map'] is not None:
+                    if data['isosurface'][id]['transp']['map']:
+                        map = True
+                        value = data['isosurface'][id]['transp']['value']
+                    elif data['isosurface'][id]['transp']['map'] is not None:
+                        map = False
+                        value = data['isosurface'][id]['transp']['value']
+                    self._display.SetIsosurfaceTransp(vid, id, map, value)
+                else:
+                    self._display.SetIsosurfaceTransp(vid, id, False, "0")
+            else:
+                # disable -> make transparent
+                self._display.SetIsosurfaceTransp(vid, id, False, "255")
         else:
-            # disable -> make transparent
-            self._display.SetIsosurfaceTransp(id, isosurfId, False, "255")
-        
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+            if list.IsChecked(index):
+                value = data['slice'][id]['transp']['value']
+                self._display.SetSliceTransp(vid, id, value)
+            else:
+                # disable -> make transparent
+                self._display.SetSliceTransp(vid, id, 255)
+                
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
         
-    def OnVolumeIsosurfSelect(self, event):
-        """!Isosurface item selected"""
-        winUp = self.FindWindowById(self.win['volume']['btnIsosurfMoveUp'])
-        winDown = self.FindWindowById(self.win['volume']['btnIsosurfMoveDown'])
+    def OnVolumeSelect(self, event):
+        """!Isosurface/Slice item selected"""
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            mode = 'isosurf'
+        else:
+            mode = 'slice'
+            
+        winUp = self.FindWindowById(self.win['volume']['btnMoveUp'])
+        winDown = self.FindWindowById(self.win['volume']['btnMoveDown'])
         selection = event.GetSelection()
-        if selection == 0:
+        if selection == -1:
+            return
+        elif selection == 0:
             winUp.Enable(False)
             if not winDown.IsEnabled():
                 winDown.Enable()
@@ -2296,83 +3806,95 @@
         # update dialog
         name = self.FindWindowById(self.win['volume']['map']).GetValue()
         layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
-        data = self.GetLayerData('volume')['volume']['isosurface'][selection]
         
-        self.UpdateVolumeIsosurfPage(layer, data)
+        if mode == 'isosurf':
+            data = self.GetLayerData('volume')['volume']['isosurface'][selection]
+            self.UpdateVolumeIsosurfPage(data)
+        else:
+            data = self.GetLayerData('volume')['volume']['slice'][selection]
+            self.UpdateVolumeSlicePage(data)
         
-    def OnVolumeIsosurfAdd(self, event):
-        """!Add new isosurface to the list"""
-        list = self.FindWindowById(self.win['volume']['isosurfs'])
-        level = self.FindWindowById(self.win['volume']['topo']['const']).GetValue()
         
-        sel = list.GetSelection()
-        if sel < 0 or sel >= list.GetCount() - 1:
-            item = list.Append(item = "%s %s" % (_("Level"), str(level)))
+        
+    def OnVolumeAdd(self, event):
+        """!Add new isosurface/slice to the list"""
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            mode = 'isosurf'
         else:
-            list.Insert(item = "%s %s" % (_("Level"), str(level)),
-                        pos = sel+1) # append
-            item = sel + 1
+            mode = 'slice'
+        list = self.FindWindowById(self.win['volume'][mode + 's'])
         
-        list.Check(item)
-        list.SetSelection(item)
-        
         name = self.FindWindowById(self.win['volume']['map']).GetValue()
         layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
         data = self.GetLayerData('volume')['volume']
         id = data['object']['id']
         
-        # collect properties
-        isosurfData = {}
-        for attrb in ('topo', 'color', 'mask',
-                      'transp', 'shine', 'emit'):
-            if attrb == 'topo':
-                isosurfData[attrb] = {}
-                win = self.FindWindowById(self.win['volume'][attrb]['const'])
-                isosurfData[attrb]['value'] = win.GetValue()
+        sel = list.GetSelection()
+        if mode == 'isosurf':
+            isosurfData = self.mapWindow.nvizDefault.SetIsosurfaceDefaultProp()
+            if isosurfData['color']['map']:
+                isosurfData['color']['value'] = layer.name
+
+            level = isosurfData['topo']['value'] = round(self._get3dRange(name = layer.name)[0], 2)
+        
+            if sel < 0 or sel >= list.GetCount() - 1:
+                item = list.Append(item = "%s %s" % (_("Level"), str(level)))
             else:
-                uwin = self.FindWindowById(self.win['volume'][attrb]['use'])
-                sel = uwin.GetSelection()
-                if self.win['volume'][attrb]['required']:
-                    sel += 1
-                if sel == 0: # unset
-                    continue
-                
-                isosurfData[attrb] = {}
-                if sel == 1: # map
-                    isosurfData[attrb]['map'] = True
-                    vwin = self.FindWindowById(self.win['volume'][attrb]['map'])
-                    value = vwin.GetValue()
-                else: # const
-                    isosurfData[attrb]['map'] = False
-                    vwin = self.FindWindowById(self.win['volume'][attrb]['const'])
-                    if vwin.GetName() == "color":
-                        value = self._getColorString(vwin.GetValue())
-                    else:
-                        value = vwin.GetValue()
-                isosurfData[attrb]['value'] = value
+                list.Insert(item = "%s %s" % (_("Level"), str(level)),
+                            pos = sel+1) # append
+                item = sel + 1
+        else:
+            sliceData = self.mapWindow.nvizDefault.SetSliceDefaultProp()
+            axis = ("X", "Y", "Z")[sliceData['position']['axis']]
+            if sel < 0 or sel >= list.GetCount() - 1:
+                item = list.Append(item = "%s %s" % (_("Slice parallel to"), axis))
+            else:
+                list.Insert(item = "%s" % (_("Slice parallel to"), axis),
+                            pos = sel+1) # append
+                item = sel + 1
         
-        data['isosurface'].insert(item, isosurfData)
+        list.Check(item)
+        list.SetSelection(item)
         
-        # add isosurface        
-        self._display.AddIsosurface(id, level)
-        # use by default 3d raster map for color
-        self._display.SetIsosurfaceColor(id, item, True, str(layer.name))
+        if mode == 'isosurf':
+            data['isosurface'].insert(item, isosurfData)
+            # add isosurface        
+            self._display.AddIsosurface(id, float(level))
+        else:
+            data['slice'].insert(item, sliceData)
+            # add isosurface        
+            nslice = self._display.AddSlice(id)
+            self._display.SetSlicePosition(id, nslice -1, sliceData['position']['x1'], sliceData['position']['x2'],
+                                               sliceData['position']['y1'], sliceData['position']['y2'],
+                                               sliceData['position']['z1'], sliceData['position']['z2'],
+                                               sliceData['position']['axis'])
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
         
         # update buttons
         self.UpdateIsosurfButtons(list)
+        if mode == 'isosurf':
+            self.UpdateVolumeIsosurfPage(isosurfData)
+        else:
+            self.UpdateVolumeSlicePage(sliceData)
         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
         
         event.Skip()
         
-    def OnVolumeIsosurfDelete(self, event):
-        """!Remove isosurface from list"""
-        list = self.FindWindowById(self.win['volume']['isosurfs'])
+    def OnVolumeDelete(self, event):
+        """!Remove isosurface/slice from list"""
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            mode = 'isosurf'
+        else:
+            mode = 'slice'
+        list = self.FindWindowById(self.win['volume'][mode + 's'])
         
         # remove item from list
-        isosurfId = list.GetSelection()
-        list.Delete(isosurfId)
+        id = list.GetSelection()
+        list.Delete(id)
         # select last item
         if list.GetCount() > 0:
             list.SetSelection(list.GetCount()-1)
@@ -2381,24 +3903,41 @@
         layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
         data = self.GetLayerData('volume')['volume']
 
-        id = data['object']['id']
+        vid = data['object']['id']
         
         # delete isosurface
-        del data['isosurface'][isosurfId]
+        if mode == 'isosurf':
+            del data['isosurface'][id]
+            self._display.DeleteIsosurface(vid, id)
+        else:
+            del data['slice'][id]
+            self._display.DeleteSlice(vid, id)
         
-        self._display.DeleteIsosurface(id, isosurfId)
-        
         # update buttons
+        if list.GetCount() > 0:
+            if mode == 'isosurf':
+                self.UpdateVolumeIsosurfPage(data['isosurface'][list.GetSelection()])
+            else:
+                self.UpdateVolumeSlicePage(data['slice'][list.GetSelection()])
+        else:
+            if mode == 'isosurf':
+                self.UpdateVolumeIsosurfPage(data['attribute'])
+            else:
+                self.UpdateVolumeSlicePage(None)
         self.UpdateIsosurfButtons(list)
         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
         
         event.Skip()
         
-    def OnVolumeIsosurfMoveUp(self, event):
-        """!Move isosurface up in the list"""
-        list = self.FindWindowById(self.win['volume']['isosurfs'])
+    def OnVolumeMoveUp(self, event):
+        """!Move isosurface/slice up in the list"""
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            mode = 'isosurf'
+        else:
+            mode = 'slice'
+        list = self.FindWindowById(self.win['volume'][mode + 's'])
         sel = list.GetSelection()
         
         if sel < 1:
@@ -2416,21 +3955,30 @@
         list.Check(sel-1)
         list.SetSelection(sel-1)
         list.Delete(sel+1)
-        data['isosurface'].insert(sel-1, data['isosurface'][sel])
-        del data['isosurface'][sel+1]
-        self._display.MoveIsosurface(id, sel, True)
+        if mode == 'isosurf':
+            data['isosurface'].insert(sel-1, data['isosurface'][sel])
+            del data['isosurface'][sel+1]
+            self._display.MoveIsosurface(id, sel, True)
+        else:
+            data['slice'].insert(sel-1, data['slice'][sel])
+            del data['slice'][sel+1]
+            self._display.MoveSlice(id, sel, True)
         
         # update buttons
         self.UpdateIsosurfButtons(list)
         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
         
         event.Skip()
         
-    def OnVolumeIsosurfMoveDown(self, event):
-        """!Move isosurface dowm in the list"""
-        list = self.FindWindowById(self.win['volume']['isosurfs'])
+    def OnVolumeMoveDown(self, event):
+        """!Move isosurface/slice down in the list"""
+        if self.FindWindowById(self.win['volume']['draw']['mode']).GetSelection() == 0:
+            mode = 'isosurf'
+        else:
+            mode = 'slice'
+        list = self.FindWindowById(self.win['volume'][mode + 's'])
         sel = list.GetSelection()
         
         if sel >= list.GetCount() - 1:
@@ -2448,18 +3996,365 @@
         list.Check(sel+2)
         list.SetSelection(sel+2)
         list.Delete(sel)
-        data['isosurface'].insert(sel+2, data['isosurface'][sel])
-        del data['isosurface'][sel]
-        self._display.MoveIsosurface(id, sel, False)
+        if mode == 'isosurf':
+            data['isosurface'].insert(sel+2, data['isosurface'][sel])
+            del data['isosurface'][sel]
+            self._display.MoveIsosurface(id, sel, False)
+        else:
+            data['slice'].insert(sel+2, data['slice'][sel])
+            del data['slice'][sel]
+            self._display.MoveSlice(id, sel, False)
         
         # update buttons
         self.UpdateIsosurfButtons(list)
         
-        if self.mapDisplay.statusbarWin['render'].IsChecked():
+        if self.mapDisplay.IsAutoRendered():
             self.mapWindow.Refresh(False)
         
         event.Skip()
+    
+    def OnVolumePositionChanged(self, event):
+        """!Volume position changed"""
+        self.mapWindow.render['quick'] = False
+        self.mapWindow.Refresh(False)
         
+    def OnVolumePosition(self, event):
+        """!Volume position"""
+        winName = self.__GetWindowName(self.win['volume'], event.GetId())
+        if not winName:
+            return
+        axis = self.FindWindowById(self.win['volume']['position']['axis']).GetSelection()
+        
+        value = self.FindWindowById(event.GetId()).GetValue()
+        slider = self.FindWindowById(self.win['volume'][winName]['slider'])
+        self.AdjustSliderRange(slider = slider, value = value)
+        
+        for win in self.win['volume']['position'].itervalues():
+            if win in (self.win['volume']['position']['axis'],
+                       self.win['volume']['position']['reset']):
+                continue
+            else:
+                self.FindWindowById(win).SetValue(value)
+        
+        data = self.GetLayerData('volume')
+        id = data['volume']['object']['id']
+        x, y, z = self._display.GetVolumePosition(id)
+        
+        if axis == 0: # x
+            x = value
+        elif axis == 1: # y
+            y = value
+        else: # z
+            z = value
+        
+        data['volume']['position']['x'] = x
+        data['volume']['position']['y'] = y
+        data['volume']['position']['z'] = z
+        data['volume']['position']['update'] = None
+        # update properties
+        
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        self.mapWindow.render['quick'] = True
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def OnVolumeAxis(self, event):
+        """!Volume position, axis changed"""
+        data = self.GetLayerData('volume')
+        id = data['volume']['object']['id']
+        
+        axis = self.FindWindowById(self.win['volume']['position']['axis']).GetSelection()
+        slider = self.FindWindowById(self.win['volume']['position']['slider'])
+        text = self.FindWindowById(self.win['volume']['position']['text'])
+        xydim = self._display.GetLongDim()
+        zdim = self._display.GetZRange()
+        zdim = zdim[1] - zdim[0]
+        x, y, z = self._display.GetVolumePosition(id)
+        
+        if axis == 0: # x
+            slider.SetRange(-3 * xydim, 3 * xydim)
+            slider.SetValue(x)
+            text.SetValue(x)
+        elif axis == 1: # y
+            slider.SetRange(-3 * xydim, 3 * xydim)
+            slider.SetValue(y)
+            text.SetValue(y)
+        else: # z
+            slider.SetRange(-3 * zdim, 3 * zdim)
+            slider.SetValue(z)
+            text.SetValue(z)
+            
+    def OnVolumePositionText(self, event):
+        """!Volume position changed by textctrl"""
+        self.OnVolumePosition(event)
+        self.OnVolumePositionChanged(None)
+        
+    def OnResetVolumePosition(self, event):
+        """!Reset position of volume"""
+        for win in self.win['volume']['position'].itervalues():
+            if win == self.win['volume']['position']['axis']:
+                self.FindWindowById(win).SetSelection(0)
+            elif win == self.win['volume']['position']['reset']:
+                continue
+            else:
+                self.FindWindowById(win).SetValue(0)
+                
+        data = self.GetLayerData('volume')
+        data['volume']['position']['x'] = 0
+        data['volume']['position']['y'] = 0
+        data['volume']['position']['z'] = 0
+        data['volume']['position']['update'] = None
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def OnVolumeSliceAxes(self, event):
+        """!Slice axis changed"""
+        self.UpdateSliceLabels()
+        data = self.GetLayerData('volume')
+        list = self.FindWindowById(self.win['volume']['slices'])
+        sel = list.GetSelection()
+        if sel < 0:
+            return
+        axis = self.FindWindowById(self.win['volume']['slice']['axes']).GetSelection()
+        data['volume']['slice'][sel]['position']['axis'] = axis
+        data['volume']['slice'][sel]['position']['update'] = None
+        
+        axis = ("X", "Y", "Z")[axis]
+        list.SetString(sel, "%s %s" % (_("Slice parallel to"), axis))
+        list.Check(sel)
+        
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event) 
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+    
+    def OnSliceTransparency(self, event):
+        """!Slice transparency changed"""
+        data = self.GetLayerData('volume')
+        
+        list = self.FindWindowById(self.win['volume']['slices'])
+        sel = list.GetSelection()
+        if sel < 0:
+            return
+        
+        val = self.FindWindowById(self.win['volume']['slice']['transp']).GetValue()
+        data['volume']['slice'][sel]['transp']['value'] = self._getPercent(val, toPercent = False)
+        data['volume']['slice'][sel]['transp']['update'] = None
+        
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def OnSliceReset(self, event):
+        """!Slice position reset"""
+        data = self.GetLayerData('volume')
+        
+        list = self.FindWindowById(self.win['volume']['slices'])
+        sel = list.GetSelection()
+        if sel < 0:
+            return
+        
+        for coord, val in zip(('x1', 'x2', 'y1', 'y2', 'z1', 'z2'),(0, 1, 0, 1, 0, 1, 0)):
+            data['volume']['slice'][sel]['position'][coord] = val
+        data['volume']['slice'][sel]['position']['update'] = None
+        
+        self.UpdateVolumeSlicePage(data['volume']['slice'][sel])
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        
+    def OnSlicePositionChange(self, event):
+        """!Slice position is changing"""
+        data = self.GetLayerData('volume')
+        list = self.FindWindowById(self.win['volume']['slices'])
+        sel = list.GetSelection()
+        if sel < 0:
+            return
+        win = self.win['volume']['slice']
+        winId = event.GetId()
+        value = event.GetInt()/100.
+        
+        for coord in ('x1', 'x2', 'y1', 'y2', 'z1', 'z2'):
+            if win['slider_' + coord] == winId:
+                data['volume']['slice'][sel]['position'][coord] = value
+                data['volume']['slice'][sel]['position']['update'] = None
+                break
+        self.mapWindow.render['quick'] = True
+        # update properties
+        event = wxUpdateProperties(data = data)
+        wx.PostEvent(self.mapWindow, event) 
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+               
+    def OnSlicePositionChanged(self, event):
+        """!Slice position is changed"""
+        self.mapWindow.render['quick'] = False
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+                
+    def OnCPlaneSelection(self, event):
+        """!Cutting plane selected"""
+        plane = self.FindWindowById(self.win['cplane']['planes']).GetStringSelection()
+        try:
+            planeIndex = int(plane.split()[-1]) - 1
+            self.EnablePage("cplane", enabled = True)
+        except:
+            planeIndex = -1
+            self.EnablePage("cplane", enabled = False)
+        self.mapWindow.SelectCPlane(planeIndex)
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+        self.UpdateCPlanePage(planeIndex)
+        
+    def OnCPlaneChanging(self, event):
+        """!Cutting plane is changing"""
+        plane = self.FindWindowById(self.win['cplane']['planes']).GetStringSelection()
+        try:
+            planeIndex = int(plane.split()[-1]) - 1
+        except:#TODO disabled page
+            planeIndex = -1
+    
+        if event.GetId() in (self.win['cplane']['rotation']['rot'].values() +
+                            self.win['cplane']['rotation']['tilt'].values()):
+            action = 'rotation'
+        else:
+            action = 'position'
+        data = self.mapWindow.cplanes[planeIndex][action]
+        self.OnScroll(event, self.win['cplane'][action], data)
+        
+        self.mapWindow.render['quick'] = True
+        event = wxUpdateCPlane(update = (action,), current = planeIndex)
+        wx.PostEvent(self.mapWindow, event)
+        
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+
+    def OnCPlaneChangeDone(self, event):
+        """!Cutting plane change done"""
+        self.mapWindow.render['quick'] = False
+        if self.mapDisplay.IsAutoRendered():
+            self.mapWindow.Refresh(False)
+            
+    def OnCPlaneChangeText(self, event):
+        """!Cutting plane changed by textctrl"""
+        for axis in ('x', 'y', 'z'):
+            if event.GetId() == self.win['cplane']['position'][axis]['text']:
+                value = self.FindWindowById(event.GetId()).GetValue()
+                slider = self.FindWindowById(self.win['cplane']['position'][axis]['slider'])
+                self.AdjustSliderRange(slider = slider, value = value)
+        self.OnCPlaneChanging(event = event)
+        self.OnCPlaneChangeDone(None)   
+        
+    def OnCPlaneShading(self, event):
+        """!Cutting plane shading changed"""
+        shading = self.FindWindowById(self.win['cplane']['shading']).GetSelection()
+        plane = self.FindWindowById(self.win['cplane']['planes']).GetStringSelection()
+        try:
+            planeIndex = int(plane.split()[-1]) - 1
+        except:#TODO disabled page
+            planeIndex = -1
+            
+        self.mapWindow.cplanes[planeIndex]['shading'] = shading
+        
+        event = wxUpdateCPlane(update = ('shading',), current = planeIndex)
+        wx.PostEvent(self.mapWindow, event)
+        
+        self.OnCPlaneChangeDone(None)
+        
+    def OnCPlaneReset(self, event):
+        """!Reset current cutting plane"""
+        plane = self.FindWindowById(self.win['cplane']['planes']).GetStringSelection()
+        try:
+            planeIndex = int(plane.split()[-1]) - 1
+        except:#TODO disabled page
+            planeIndex = -1
+        self.mapWindow.cplanes[planeIndex] = copy.deepcopy(UserSettings.Get(group = 'nviz',
+                                                                            key = 'cplane'))
+        event = wxUpdateCPlane(update = ('position','rotation','shading'), current = planeIndex)
+        wx.PostEvent(self.mapWindow, event)
+        self.OnCPlaneChangeDone(None)
+        self.UpdateCPlanePage(planeIndex)
+    
+    def OnDecorationPlacement(self, event):
+        """!Place an arrow/scalebar by clicking on display"""
+        if event.GetId() == self.win['decoration']['arrow']['place']:
+            type = 'arrow'
+        elif event.GetId() == self.win['decoration']['scalebar']['place']:
+            type = 'scalebar'
+        else: return
+        
+        if event.GetInt():
+            self.mapDisplay.Raise()
+            self.mapWindow.mouse['use'] = type
+            self.mapWindow.SetCursor(self.mapWindow.cursors["cross"])
+        else:
+            self.mapWindow.mouse['use'] = 'default'
+            self.mapWindow.SetCursor(self.mapWindow.cursors["default"])
+    
+    def OnArrowDelete(self, event):
+        """!Delete arrow"""
+        self._display.DeleteArrow()
+        self.mapWindow.decoration['arrow']['show'] = False
+        self.mapWindow.Refresh(False)
+    
+    def OnScalebarDelete(self, event):
+        """!Delete scalebar"""
+        try:
+            id = self.mapWindow.decoration['scalebar'][-1]['id']
+        except IndexError:
+            return
+        self._display.DeleteScalebar(id = id)
+        del self.mapWindow.decoration['scalebar'][-1]
+        
+        self.mapWindow.Refresh(False)   
+         
+    def OnDecorationProp(self, event):
+        """!Set arrow/scalebar properties"""
+        if event.GetId() in self.win['decoration']['arrow'].values():
+            type = 'arrow'
+        elif event.GetId() in self.win['decoration']['scalebar'].values():
+            type = 'scalebar'
+        else: return
+        
+        color = self.FindWindowById(self.win['decoration'][type]['color']).GetValue()
+        size = self.FindWindowById(self.win['decoration'][type]['size']).GetValue()
+        if type == 'arrow':
+            self.mapWindow.decoration[type]['color'] = self._getColorString(color)
+            self.mapWindow.decoration[type]['size'] = size
+        elif type == 'scalebar'and self.mapWindow.decoration['scalebar']:
+            self.mapWindow.decoration[type][-1]['color'] = self._getColorString(color)
+            self.mapWindow.decoration[type][-1]['size'] = size
+        
+        if type == 'arrow' and self.mapWindow.decoration['arrow']['show']:
+            self._display.SetArrow(self.mapWindow.decoration['arrow']['position']['x'],
+                                   self.mapWindow.decoration['arrow']['position']['y'],
+                                   self.mapWindow.decoration['arrow']['size'],
+                                   self.mapWindow.decoration['arrow']['color'])
+            self._display.DrawArrow()
+        elif type == 'scalebar' and self.mapWindow.decoration['scalebar']:
+            self._display.SetScalebar(self.mapWindow.decoration['scalebar'][-1]['id'],
+                                      self.mapWindow.decoration['scalebar'][-1]['position']['x'],
+                                      self.mapWindow.decoration['scalebar'][-1]['position']['y'],
+                                      self.mapWindow.decoration['scalebar'][-1]['size'],
+                                      self.mapWindow.decoration['scalebar'][-1]['color'])
+            self._display.DrawScalebar()
+            self.mapWindow.Refresh(False)
+        
     def UpdatePage(self, pageId):
         """!Update dialog (selected page)"""
         self.pageChanging = True
@@ -2474,17 +4369,26 @@
             zmax = self.mapWindow.view['z-exag']['max']
             zval = self.mapWindow.view['z-exag']['value']
             
-            for control in ('spin', 'slider'):
-                self.FindWindowById(self.win['view']['height'][control]).SetRange(hmin,
-                                                                                  hmax)
+            for control in ('slider','text'):
+                self.FindWindowById(self.win['view']['height'][control]).SetRange(
+                                                                        hmin,hmax)
+                self.FindWindowById(self.win['view']['z-exag'][control]).SetRange(
+                                                                        zmin, zmax) 
                 self.FindWindowById(self.win['view']['height'][control]).SetValue(hval)                                      
-                self.FindWindowById(self.win['view']['z-exag'][control]).SetRange(zmin,
-                                                                                  zmax)
+                
                 self.FindWindowById(self.win['view']['z-exag'][control]).SetValue(zval)                                      
         
-            self.FindWindowById(self.win['view']['bgcolor']).SetColour(\
+            self.FindWindowById(self.win['view']['background']['color']).SetColour(\
                             self.mapWindow.view['background']['color'])
+                            
+            tval = self.mapWindow.view['twist']['value']
+            pval = self.mapWindow.view['persp']['value']
+            for control in ('slider','text'):
+                self.FindWindowById(self.win['view']['twist'][control]).SetValue(tval)                                      
+                
+                self.FindWindowById(self.win['view']['persp'][control]).SetValue(pval)
             
+            
         elif pageId in ('surface', 'vector', 'volume'):
             name = self.FindWindowById(self.win[pageId]['map']).GetValue()
             data = self.GetLayerData(pageId)
@@ -2497,96 +4401,172 @@
                     self.UpdateVectorPage(layer, data['vector'])
                 elif pageId == 'volume':
                     layer = self.mapWindow.GetLayerByName(name, mapType = '3d-raster')
-                    self.UpdateVectorPage(layer, data['vector'])
+                    self.UpdateVolumePage(layer, data['volume'])
         elif pageId == 'light':
             zval = self.mapWindow.light['position']['z']
             bval = self.mapWindow.light['bright']
             aval = self.mapWindow.light['ambient']
-            for control in ('spin', 'slider'):
+            for control in ('slider','text'):
                 self.FindWindowById(self.win['light']['z'][control]).SetValue(zval)
                 self.FindWindowById(self.win['light']['bright'][control]).SetValue(bval)
                 self.FindWindowById(self.win['light']['ambient'][control]).SetValue(aval)
             self.FindWindowById(self.win['light']['color']).SetColour(self.mapWindow.light['color'])
+            self.FindWindowById(self.win['light']['position']).PostDraw()
         elif pageId == 'fringe':
             win = self.FindWindowById(self.win['fringe']['map'])
             win.SetValue(self.FindWindowById(self.win['surface']['map']).GetValue())
-        
+        elif pageId == 'decoration':
+            win = self.FindWindowById(self.win['decoration']['arrow']['size'])
+            win.SetValue(self.mapWindow.decoration['arrow']['size'])
+            win = self.FindWindowById(self.win['decoration']['scalebar']['size'])
+            win.SetValue(self.mapWindow._getDecorationSize())
+        elif pageId == 'constant':
+            if self.mapWindow.constants:
+                surface = self.FindWindowById(self.win['constant']['surface'])
+                for item in self.mapWindow.constants:
+                    surface.Append(_("constant#") + str(item['constant']['object']['name']))
+                surface.SetSelection(0)
+                self.OnConstantSelection(None)
+                self.EnablePage('constant', True)
+        elif pageId == 'cplane':
+            count = self._display.GetCPlanesCount()
+            choices = [_("None"),]
+            for plane in range(count):
+                choices.append("%s %i" % (_("Plane"), plane+1))
+            self.FindWindowById(self.win['cplane']['planes']).SetItems(choices)
+            current = 0
+            for i, cplane in enumerate(self.mapWindow.cplanes):
+                if cplane['on']:
+                    current = i + 1
+            self.FindWindowById(self.win['cplane']['planes']).SetSelection(current)
+            
+            xyRange, zRange = self._display.GetXYRange(), self._display.GetZRange()
+            if xyRange > 0: # GTK warning
+                self.FindWindowById(self.win['cplane']['position']['x']['slider']).SetRange(
+                                                                    -xyRange/2., xyRange/2.)
+                self.FindWindowById(self.win['cplane']['position']['y']['slider']).SetRange(
+                                                                    -xyRange/2., xyRange/2.)
+            if zRange[0] - zRange[1] > 0:
+                self.FindWindowById(self.win['cplane']['position']['z']['slider']).SetRange(zRange[0], zRange[1])
+            self.FindWindowById(self.win['cplane']['position']['z']['slider']).SetValue(zRange[0])
+            self.FindWindowById(self.win['cplane']['position']['z']['text']).SetValue(zRange[0])
+            self.OnCPlaneSelection(None)
+            
+        elif pageId == 'animation':
+            self.UpdateAnimationPage()
+            
         self.Update()
         self.pageChanging = False
         
+    def UpdateAnimationPage(self):
+        """!Update animation page"""
+        # wrap help text according to tool window
+        help = self.FindWindowById(self.win['anim']['help'])
+        width = help.GetGrandParent().GetSizeTuple()[0]
+        help.Wrap(width - 15)
+        anim = self.mapWindow.GetAnimation()
+        if anim.Exists():
+            self.FindWindowById(self.win['anim']['play']).Enable()
+        else:
+            self.UpdateFrameIndex(index = 0)
+            
+        self.UpdateFrameCount()
+        
+        self.FindWindowById(self.win['anim']['play']).Disable()
+        self.FindWindowById(self.win['anim']['record']).Enable()
+        self.FindWindowById(self.win['anim']['pause']).Disable()
+        self.FindWindowById(self.win['anim']['stop']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['slider']).Disable()
+        self.FindWindowById(self.win['anim']['frameIndex']['text']).Disable()
+        
+    def UpdateCPlanePage(self, index):
+        """!Update widgets according to selected clip plane"""
+        if index == -1:   
+            return
+        data = self.mapWindow.cplanes[index]
+        for widget in ('text', 'slider'):
+            for axes in ('x', 'y', 'z'):
+                self.FindWindowById(self.win['cplane']['position'][axes][widget]).SetValue(data['position'][axes])
+            for each in ('tilt', 'rot'):
+                self.FindWindowById(self.win['cplane']['rotation'][each][widget]).SetValue(data['rotation'][each])
+        self.FindWindowById(self.win['cplane']['shading']).SetSelection(data['shading'])
+                
     def UpdateSurfacePage(self, layer, data, updateName = True):
         """!Update surface page"""
-        ret = gcmd.RunCommand('r.info',
-                              read = True,
-                              flags = 'm',
-                              map = layer.name)
-        if ret:
-            desc = ret.split('=')[1].rstrip('\n')
-        else:
-            desc = None
+        desc = grass.raster_info(layer.name)['title']
         if updateName:
             self.FindWindowById(self.win['surface']['map']).SetValue(layer.name)
         self.FindWindowById(self.win['surface']['desc']).SetLabel(desc)
         
         # attributes
-        for attr in ('topo', 'color'): # required
-            if layer and layer.type == 'raster':
-                self.FindWindowById(self.win['surface'][attr]['map']).SetValue(layer.name)
-            else:
-                self.FindWindowById(self.win['surface'][attr]['map']).SetValue('')
-            self.SetMapObjUseMap(nvizType = 'surface',
-                                 attrb = attr, map = True) # -> map
-        
+        if layer and layer.type == 'raster':
+            self.vetoGSelectEvt = True
+            self.FindWindowById(self.win['surface']['color']['map']).SetValue(layer.name)
+        else:
+            self.FindWindowById(self.win['surface']['color']['map']).SetValue('')
+
+        self.SetMapObjUseMap(nvizType = 'surface',
+                             attrb = 'color', map = True) # -> map
+                                
         if 'color' in data['attribute']:
             value = data['attribute']['color']['value']
+
             if data['attribute']['color']['map']:
                 self.FindWindowById(self.win['surface']['color']['map']).SetValue(value)
             else: # constant
                 color = map(int, value.split(':'))
                 self.FindWindowById(self.win['surface']['color']['const']).SetColour(color)
             self.SetMapObjUseMap(nvizType = 'surface',
-                                 attrb = attr, map = data['attribute']['color']['map'])
-        
+                                 attrb = 'color', map = data['attribute']['color']['map'])
+
         self.SetMapObjUseMap(nvizType = 'surface',
                              attrb = 'shine', map = data['attribute']['shine']['map'])
         value = data['attribute']['shine']['value']
         if data['attribute']['shine']['map']:
             self.FindWindowById(self.win['surface']['shine']['map']).SetValue(value)
         else:
-            self.FindWindowById(self.win['surface']['shine']['const']).SetValue(value)
-        
+            self.FindWindowById(self.win['surface']['shine']['const']).SetValue(self._getPercent(value))
+        if 'transp' in data['attribute']:  
+            value = data['attribute']['transp']['value']  
+            if data['attribute']['transp']['map']:
+                self.FindWindowById(self.win['surface']['color']['map']).SetValue(value)
+            else:
+                self.FindWindowById(self.win['surface']['transp']['const']).SetValue(self._getPercent(value))
+            self.SetMapObjUseMap(nvizType = 'surface', attrb = 'transp', map = data['attribute']['transp']['map'])
+        else:
+            self.SetMapObjUseMap(nvizType = 'surface', attrb = 'transp', map = None)
         #
         # draw
         #
-        for control, data in data['draw'].iteritems():
+        for control, drawData in data['draw'].iteritems():
             if control == 'all': # skip 'all' property
                 continue
             if control == 'resolution':
-                self.FindWindowById(self.win['surface']['draw']['res-coarse']).SetValue(data['coarse'])
-                self.FindWindowById(self.win['surface']['draw']['res-fine']).SetValue(data['fine'])
+                self.FindWindowById(self.win['surface']['draw']['res-coarse']).SetValue(drawData['coarse'])
+                self.FindWindowById(self.win['surface']['draw']['res-fine']).SetValue(drawData['fine'])
                 continue
             
             if control == 'mode':
-                if data['desc']['mode'] == 'coarse':
+                if drawData['desc']['mode'] == 'coarse':
                     self.FindWindowById(self.win['surface']['draw']['mode']).SetSelection(0)
-                elif data['desc']['mode'] == 'fine':
+                elif drawData['desc']['mode'] == 'fine':
                     self.FindWindowById(self.win['surface']['draw']['mode']).SetSelection(1)
                 else: # both
                     self.FindWindowById(self.win['surface']['draw']['mode']).SetSelection(2)
                 
-                if data['desc']['style'] == 'wire':
+                if drawData['desc']['style'] == 'wire':
                     self.FindWindowById(self.win['surface']['draw']['style']).SetSelection(0)
                 else: # surface
                     self.FindWindowById(self.win['surface']['draw']['style']).SetSelection(1)
                 
-                if data['desc']['shading'] == 'flat':
+                if drawData['desc']['shading'] == 'flat':
                     self.FindWindowById(self.win['surface']['draw']['shading']).SetSelection(0)
                 else: # gouraud
                     self.FindWindowById(self.win['surface']['draw']['shading']).SetSelection(1)
                 
                 continue
             
-            value = data['value']
+            value = drawData['value']
             win = self.FindWindowById(self.win['surface']['draw'][control])
             
             name = win.GetName()
@@ -2598,6 +4578,16 @@
                 win.SetColour(color)
             else:
                 win.SetValue(value)
+        #
+        # position
+        #
+        dim = self._display.GetLongDim()
+        self.FindWindowById(self.win['surface']['position']['slider']).SetRange(-2 * dim, 2 * dim)
+        if 'x' in data['position']:
+            xval = data['position']['x']
+            self.FindWindowById(self.win['surface']['position']['axis']).SetSelection(0)
+            for control in ('slider','text'):
+                    self.FindWindowById(self.win['surface']['position'][control]).SetValue(xval)
         # enable/disable res widget + set draw mode
         self.OnSurfaceMode(event = None)
 
@@ -2646,7 +4636,7 @@
         for v in ('lines', 'points'):
             self.FindWindowById(self.win['vector'][v]['surface']).Enable(enable)
             self.FindWindowById(self.win['vector'][v]['height']['slider']).Enable(enable)
-            self.FindWindowById(self.win['vector'][v]['height']['spin']).Enable(enable)
+            self.FindWindowById(self.win['vector'][v]['height']['text']).Enable(enable)
             
         #
         # lines
@@ -2677,18 +4667,20 @@
                     display.SetSelection(1)
                 else:
                     display.SetSelection(0)
-            
             if data[vtype]['mode']['type'] == 'surface':
                 rasters = self.mapWindow.GetLayerNames('raster')
-                surface = self.FindWindowById(self.win['vector'][vtype]['surface'])
-                surface.SetItems(rasters)
-                if len(rasters) > 0:
-                    try:
-                        surface.SetStringSelection(data[vtype]['mode']['surface'])
-                    except:
-                        pass
-        
-        for type in ('slider', 'spin'):
+                constants = self.mapWindow.GetLayerNames('constant')
+                surfaces = rasters + constants
+                surfaceWin = self.FindWindowById(self.win['vector'][vtype]['surface'])
+                surfaceWin.SetItems(surfaces)
+                for idx, surface in enumerate(surfaces):
+                    try:# TODO fix this mess
+                        selected = data[vtype]['mode']['surface']['show'][idx]
+                    except (TypeError, IndexError, KeyError):
+                        selected = False
+                    surfaceWin.Check(idx, selected)
+
+        for type in ('slider', 'text'):
             win = self.FindWindowById(self.win['vector']['lines']['height'][type])
             win.SetValue(data['lines']['height']['value'])
         
@@ -2719,8 +4711,9 @@
                 win.SetValue(color)
             else:
                 win.SetValue(data['points'][prop]['value'])
+            
         # height
-        for type in ('slider', 'spin'):
+        for type in ('slider', 'text'):
             win = self.FindWindowById(self.win['vector']['points']['height'][type])
             win.SetValue(data['points']['height']['value'])
         
@@ -2728,56 +4721,86 @@
         """!Update volume page"""
         if updateName:
             self.FindWindowById(self.win['volume']['map']).SetValue(layer.name)
-        list = self.FindWindowById(self.win['volume']['isosurfs'])
         
         # draw
         for control, idata in data['draw'].iteritems():
             if control == 'all': # skip 'all' property
                 continue
-            
+                
             win = self.FindWindowById(self.win['volume']['draw'][control])
-            
+            if control == 'mode':
+                value = data['draw']['mode']['value']
             if control == 'shading':
-                if data['draw']['shading']['desc'] == 'flat':
+                if data['draw']['shading'][data['draw']['mode']['desc']]['desc'] == 'flat':
                     value = 0
                 else:
                     value = 1
-            else:
-                value = idata['value']
+            if control == 'resolution':
+                value = idata[data['draw']['mode']['desc']]['value']
             
             if win.GetName() == "selection":
                 win.SetSelection(value)
             else:
                 win.SetValue(value)
+                
+        self.OnVolumeMode(None)
+        id = data['object']['id']
+        if data['draw']['mode']['desc'] == 'isosurface':
+            self._display.SetIsosurfaceMode(id, data['draw']['shading']['isosurface']['value'])
+            self._display.SetIsosurfaceRes(id, data['draw']['resolution']['isosurface']['value'])
+        else:
+            self._display.SetSliceMode(id, data['draw']['shading']['slice']['value'])
+            self._display.SetSliceRes(id, data['draw']['resolution']['slice']['value'])
+        box = self.FindWindowById(self.win['volume']['isosurfs'])
         
-        self.SetIsosurfaceMode(data['draw']['shading']['value'])
-        self.SetIsosurfaceResolution(data['draw']['resolution']['value'])
+        if data['draw']['mode']['desc'] == 'isosurface':
+            isosurfaces = []
+            for iso in data['isosurface']:
+                level = iso['topo']['value']
+                isosurfaces.append("%s %s" % (_("Level"), level))
+            box.Set(isosurfaces)
+            box.SetChecked(range(len(isosurfaces)))
+            if data['isosurface']:
+                box.SetSelection(0)
+                self.UpdateVolumeIsosurfPage(data['isosurface'][0])
+            else:
+                self.UpdateVolumeIsosurfPage(data['attribute'])
+        else:
+            slices = []
+            for slice in data['slice']:
+                axis = ("X", "Y", "Z")[slice['position']['axis']]
+                slices.append("%s %s" % (_("Slice parallel to"), axis))
+            box.Set(slices)
+            box.SetChecked(range(len(slices)))
+            if data['slice']:
+                box.SetSelection(0)
+                self.UpdateVolumeSlicePage(data['slice'][0])
+            else:
+                self.UpdateVolumeSlicePage(None)
+        #
+        # position
+        #
+        if 'x' in data['position']:
+            xval = data['position']['x']
+            self.FindWindowById(self.win['volume']['position']['axis']).SetSelection(0)
+            for control in ('slider','text'):
+                    self.FindWindowById(self.win['volume']['position'][control]).SetValue(xval)    
+        # set topo range
+        mapRange = self._get3dRange(name = layer.name)
+        desc = self.FindWindowById(self.win['volume']['desc'])
+        desc.SetLabel("%s %.2f - %.2f" % (_("range:"), mapRange[0], mapRange[1]))
         
-        self.UpdateVolumeIsosurfPage(layer, data['attribute'])
-        
-    def UpdateVolumeIsosurfPage(self, layer, data):
+    def UpdateVolumeIsosurfPage(self, data):
         """!Update dialog -- isosurface attributes"""
         #
         # isosurface attributes
         #
         for attrb in ('topo', 'color', 'mask',
-                     'transp', 'shine', 'emit'):
-            # check required first
-            if attrb == 'topo':
-                self.FindWindowById(self.win['volume'][attrb]['const']).SetValue(0)
-                continue
-            if attrb == 'color':
-                if layer and layer.type == '3d-raster':
-                    self.FindWindowById(self.win['volume'][attrb]['map']).SetValue(layer.name)
-                else:
-                    self.FindWindowById(self.win['volume'][attrb]['map']).SetValue('')
-                self.SetMapObjUseMap(nvizType = 'volume',
-                                     attrb = attrb, map = True) # -> map
-                continue
-            
+                     'transp', 'shine'):
             # skip empty attributes
             if attrb not in data:
-                continue
+                self.SetMapObjUseMap(nvizType = 'volume', attrb = attrb, map = None)
+                continue     
             
             value = data[attrb]['value']
             if attrb == 'color':
@@ -2788,14 +4811,78 @@
                     self.FindWindowById(self.win['volume'][attrb]['const']).SetColour(color)
             else:
                 if data[attrb]['map']:
+                    self.vetoGSelectEvt = True
                     win = self.FindWindowById(self.win['volume'][attrb]['map'])
+                    win.SetValue(value)
                 else:
-                    win = self.FindWindowById(self.win['volume'][attrb]['const'])
-                win.SetValue(value)
-            
+                    if value:
+                        win = self.FindWindowById(self.win['volume'][attrb]['const'])
+                        if attrb == 'topo':
+                            win.SetValue(float(value))
+                        else:
+                            win.SetValue(self._getPercent(value))
+                    
             self.SetMapObjUseMap(nvizType = 'volume',
                                  attrb = attrb, map = data[attrb]['map'])
+        # set inout
+        if 'inout' in data:
+            self.FindWindowById(self.win['volume']['inout']).SetValue(data['inout'])
+            
+    def UpdateVolumeSlicePage(self, data):
+        """!Update dialog -- slice attributes"""
+        if data:
+            for coord in ('x1', 'x2', 'y1', 'y2', 'z1', 'z2'):
+                win = self.FindWindowById(self.win['volume']['slice']['slider_' + coord])
+                win.Enable()
+                win.SetValue(data['position'][coord] * 100)
+            win = self.FindWindowById(self.win['volume']['slice']['axes'])
+            win.SetSelection(data['position']['axis'])
+            win.Enable()
+            
+            win = self.FindWindowById(self.win['volume']['slice']['transp'])
+            win.SetValue(self._getPercent(data['transp']['value']))
+            win.Enable()
+            self.FindWindowById(self.win['volume']['slice']['reset']).Enable()
+        else:
+            for coord in ('x1', 'x2', 'y1', 'y2', 'z1', 'z2'):
+                self.FindWindowById(self.win['volume']['slice']['slider_' + coord]).Disable()
+            self.FindWindowById(self.win['volume']['slice']['axes']).Disable()
+            self.FindWindowById(self.win['volume']['slice']['transp']).Disable()
+            self.FindWindowById(self.win['volume']['slice']['reset']).Disable()
         
+        self.UpdateSliceLabels()
+        
+    def UpdateSliceLabels(self):
+        """!Update text labels of slice controls according to axis"""
+        sel = self.FindWindowById(self.win['volume']['slice']['axes']).GetSelection()
+        if sel == 0:
+            self.FindWindowByName('label_edge_0').SetLabel(_("North edge:"))
+            self.FindWindowByName('label_edge_1').SetLabel(_("South edge:"))
+            self.FindWindowByName('label_edge_2').SetLabel(_("West edge:"))
+            self.FindWindowByName('label_edge_3').SetLabel(_("East edge:"))
+            
+            self.FindWindowByName('label_coord_0').SetLabel(_("Northing (Y):"))
+            self.FindWindowByName('label_coord_1').SetLabel(_("Height (Z):"))
+            self.FindWindowByName('label_coord_2').SetLabel(_("Easting (X):"))
+        elif sel == 1:
+            self.FindWindowByName('label_edge_0').SetLabel(_("West edge:"))
+            self.FindWindowByName('label_edge_1').SetLabel(_("East edge:"))
+            self.FindWindowByName('label_edge_2').SetLabel(_("North edge:"))
+            self.FindWindowByName('label_edge_3').SetLabel(_("South edge:"))
+            
+            self.FindWindowByName('label_coord_0').SetLabel(_("Easting (X):"))
+            self.FindWindowByName('label_coord_1').SetLabel(_("Height (Z):"))
+            self.FindWindowByName('label_coord_2').SetLabel(_("Northing (Y):"))
+        else:
+            self.FindWindowByName('label_edge_0').SetLabel(_("West edge:"))
+            self.FindWindowByName('label_edge_1').SetLabel(_("East edge:"))
+            self.FindWindowByName('label_edge_2').SetLabel(_("Bottom edge:"))
+            self.FindWindowByName('label_edge_3').SetLabel(_("Top edge:"))  
+            
+            self.FindWindowByName('label_coord_0').SetLabel(_("Easting (X):"))
+            self.FindWindowByName('label_coord_1').SetLabel(_("Northing (Y):"))
+            self.FindWindowByName('label_coord_2').SetLabel(_("Height (Z):")) 
+        
     def SetPage(self, name):
         """!Get named page"""
         if name == 'view':
@@ -2804,9 +4891,13 @@
             self.SetSelection(1)
         else:
             self.SetSelection(2)
+
         win = self.FindWindowById(self.page[name]['notebook'])
-        
-        win.SetSelection(self.page[name]['id'])
+        try:
+            win.Expand(win.GetFoldPanel(self.page[name]['id']))
+            self.UpdateScrolling((win.GetFoldPanel(self.page[name]['id']).GetGrandParent(),))
+        except AttributeError:
+            win.SetSelection(self.page[name]['id'])
 
 class PositionWindow(wx.Window):
     """!Abstract position control window, see subclasses
@@ -2860,10 +4951,11 @@
             ycoord = 0.0
         elif ycoord > 1.0:
             ycoord = 1.0
-
-        self.data['position']['x'] = xcoord        
-        self.data['position']['y'] = ycoord
         
+        x, y = self.TransformCoordinates(xcoord, ycoord)
+        self.data['position']['x'] = x        
+        self.data['position']['y'] = y
+        
         return xcoord, ycoord
     
     def OnMouse(self, event):
@@ -2881,7 +4973,8 @@
     def PostDraw(self):
         x, y = self.UpdatePos(self.data['position']['x'],
                               self.data['position']['y'])
-        self.Draw(pos = (x, y), scale = True)
+        
+        self.Draw(pos = (x,y), scale = True)
 
 class ViewPositionWindow(PositionWindow):
     """!View position control widget"""
@@ -2899,8 +4992,12 @@
         wx.PostEvent(self.mapWindow, event)
 
         return x, y
-
+    
+    def TransformCoordinates(self, x, y, toLight = True):
+        return x, y
+        
     def OnMouse(self, event):
+        self.mapWindow.iview['dir']['use'] = False # use focus instead of viewdir
         PositionWindow.OnMouse(self, event)
         if event.LeftIsDown():
             self.mapWindow.render['quick'] = self.quick
@@ -2916,7 +5013,7 @@
     def __init__(self, parent, mapwindow, id = wx.ID_ANY,
                  **kwargs):
         PositionWindow.__init__(self, parent, mapwindow, id, **kwargs)
-
+        
         self.data = self.mapWindow.light
         self.quick = False
         self.PostDraw()
@@ -2924,522 +5021,30 @@
     def UpdatePos(self, xcoord, ycoord):
         x, y = PositionWindow.UpdatePos(self, xcoord, ycoord)
         
-        event = wxUpdateLight()
+        event = wxUpdateLight(refresh = False)
         wx.PostEvent(self.mapWindow, event)
+        
+        return x, y
     
+    def TransformCoordinates(self, x, y, toLight = True):
+        if toLight:
+            x = 2 * x - 1
+            y = -2 * y + 1
+        else:
+            x = (x + 1)/2
+            y = (1 - y)/2
         return x, y
-
+    
+    def PostDraw(self):
+        event = wxUpdateLight(refresh = True)
+        wx.PostEvent(self.mapWindow, event)
+        x, y = self.data['position']['x'], self.data['position']['y']
+        x, y = self.TransformCoordinates(x, y, toLight = False)
+        
+        self.Draw(pos = (x,y), scale = True)
+        
     def OnMouse(self, event):
         PositionWindow.OnMouse(self, event)
         if event.LeftUp():
             self.mapWindow.render['quick'] = False
             self.mapWindow.Refresh(eraseBackground = False)
-
-class NvizPreferencesDialog(PreferencesBaseDialog):
-    """!Nviz preferences dialog"""
-    def __init__(self, parent, title = _("3D view settings"),
-                 settings = UserSettings):
-        PreferencesBaseDialog.__init__(self, parent = parent, title = title,
-                                       settings = settings)
-        self.toolWin = self.parent.GetLayerManager().nviz
-        self.win = dict()
-        
-        # create notebook pages
-        self._createViewPage(self.notebook)
-        self._createVectorPage(self.notebook)
-        
-        self.SetMinSize(self.GetBestSize())
-        self.SetSize(self.size)
-        
-    def _createViewPage(self, notebook):
-        """!Create notebook page for general settings"""
-        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
-        
-        notebook.AddPage(page = panel,
-                         text = " %s " % _("View"))
-        
-        pageSizer = wx.BoxSizer(wx.VERTICAL)
-        
-        self.win['general'] = {}
-        self.win['view'] = {}
-        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
-                            label = " %s " % (_("View")))
-        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
-        
-        # perspective
-        self.win['view']['persp'] = {}
-        pvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'persp')
-        ipvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'persp', internal = True)
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Perspective:")),
-                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(value)")),
-                      pos = (0, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-        
-        pval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                           initial = pvals['value'],
-                           min = ipvals['min'],
-                           max = ipvals['max'])
-        self.win['view']['persp']['value'] = pval.GetId()
-        gridSizer.Add(item = pval, pos = (0, 2),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(step)")),
-                      pos = (0, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-        
-        pstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                           initial = pvals['step'],
-                           min = ipvals['min'],
-                           max = ipvals['max']-1)
-        self.win['view']['persp']['step'] = pstep.GetId()
-        gridSizer.Add(item = pstep, pos = (0, 4),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        # position
-        self.win['view']['position'] = {}
-        posvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'position')
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Position:")),
-                      pos = (1, 0), flag = wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(x)")),
-                      pos = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-        
-        px = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                           initial = posvals['x'] * 100,
-                           min = 0,
-                           max = 100)
-        self.win['view']['position']['x'] = px.GetId()
-        gridSizer.Add(item = px, pos = (1, 2),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = "(y)"),
-                      pos = (1, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-        
-        py = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                           initial = posvals['y'] * 100,
-                           min = 0,
-                           max = 100)
-        self.win['view']['position']['y'] = py.GetId()
-        gridSizer.Add(item = py, pos = (1, 4),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        # height
-        self.win['view']['height'] = {}
-        hvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'height')
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Height:")),
-                      pos = (2, 0), flag = wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(step)")),
-                      pos = (2, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-        
-        hstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                           initial = hvals['step'],
-                           min = 1,
-                           max = 1e6)
-        self.win['view']['height']['step'] = hstep.GetId()
-        gridSizer.Add(item = hstep, pos = (2, 2),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        # twist
-        self.win['view']['twist'] = {}
-        tvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'twist')
-        itvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'twist', internal = True)
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Twist:")),
-                      pos = (3, 0), flag = wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(value)")),
-                      pos = (3, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-        
-        tval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                           initial = tvals['value'],
-                           min = itvals['min'],
-                           max = itvals['max'])
-        self.win['view']['twist']['value'] = tval.GetId()
-        gridSizer.Add(item = tval, pos = (3, 2),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(step)")),
-                      pos = (3, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-        
-        tstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                           initial = tvals['step'],
-                           min = itvals['min'],
-                           max = itvals['max']-1)
-        self.win['view']['twist']['step'] = tstep.GetId()
-        gridSizer.Add(item = tstep, pos = (3, 4),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        # z-exag
-        self.win['view']['z-exag'] = {}
-        zvals = UserSettings.Get(group = 'nviz', key = 'view', subkey = 'z-exag')
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Z-exag:")),
-                      pos = (4, 0), flag = wx.ALIGN_CENTER_VERTICAL)
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(value)")),
-                      pos = (4, 1), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-        
-        zval = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                           initial = zvals['value'],
-                           min = -1e6,
-                           max = 1e6)
-        self.win['view']['z-exag']['value'] = zval.GetId()
-        gridSizer.Add(item = zval, pos = (4, 2),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("(step)")),
-                      pos = (4, 3), flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)
-        
-        zstep = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                           initial = zvals['step'],
-                           min = -1e6,
-                           max = 1e6)
-        self.win['view']['z-exag']['step'] = zstep.GetId()
-        gridSizer.Add(item = zstep, pos = (4, 4),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        boxSizer.Add(item = gridSizer, proportion = 1,
-                  flag = wx.ALL | wx.EXPAND, border = 3)
-        pageSizer.Add(item = boxSizer, proportion = 0,
-                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border = 3)
-
-        box = wx.StaticBox(parent = panel, id = wx.ID_ANY,
-                           label = " %s " % (_("Image Appearance")))
-        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
-        gridSizer.AddGrowableCol(0)
-        
-        # background color
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Background color:")),
-                      pos = (0, 0), flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        color = csel.ColourSelect(panel, id = wx.ID_ANY,
-                                  colour = UserSettings.Get(group = 'nviz', key = 'settings',
-                                                            subkey = ['general', 'bgcolor']),
-                                  size = globalvar.DIALOG_COLOR_SIZE)
-        self.win['general']['bgcolor'] = color.GetId()
-        gridSizer.Add(item = color, pos = (0, 1))
-        
-        boxSizer.Add(item = gridSizer, proportion = 1,
-                  flag = wx.ALL | wx.EXPAND, border = 3)
-        pageSizer.Add(item = boxSizer, proportion = 0,
-                      flag = wx.EXPAND | wx.ALL,
-                      border = 3)
-        
-        panel.SetSizer(pageSizer)
-        
-        return panel
-    
-    def _createVectorPage(self, notebook):
-        """!Create notebook page for general settings"""
-        panel = wx.Panel(parent = notebook, id = wx.ID_ANY)
-        
-        notebook.AddPage(page = panel,
-                         text = " %s " % _("Vector"))
-        
-        pageSizer = wx.BoxSizer(wx.VERTICAL)
-        
-        # vector lines
-        self.win['vector'] = {}
-        self.win['vector']['lines'] = {}
-        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
-                            label = " %s " % (_("Vector lines")))
-        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 3)
-        
-        # show
-        row = 0
-        showLines = wx.CheckBox(parent = panel, id = wx.ID_ANY,
-                                label = _("Show lines"))
-        self.win['vector']['lines']['show'] = showLines.GetId()
-        showLines.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
-                                            subkey = ['lines', 'show']))
-        gridSizer.Add(item = showLines, pos = (row, 0))
-        
-        boxSizer.Add(item = gridSizer, proportion = 1,
-                  flag = wx.ALL | wx.EXPAND, border = 3)
-        pageSizer.Add(item = boxSizer, proportion = 0,
-                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border = 3)
-        
-        # vector points
-        self.win['vector']['points'] = {}
-        box = wx.StaticBox (parent = panel, id = wx.ID_ANY,
-                            label = " %s " % (_("Vector points")))
-        boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL)
-        gridSizer = wx.GridBagSizer(vgap = 3, hgap = 5)
-        
-        # show
-        row = 0
-        showPoints = wx.CheckBox(parent = panel, id = wx.ID_ANY,
-                                 label = _("Show points"))
-        showPoints.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
-                                             subkey = ['points', 'show']))
-        self.win['vector']['points']['show'] = showPoints.GetId()
-        gridSizer.Add(item = showPoints, pos = (row, 0), span = (1, 8))
-        
-        # icon size
-        row += 1 
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Size:")),
-                      pos = (row, 0), flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        isize = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                            initial = 100,
-                            min = 1,
-                            max = 1e6)
-        self.win['vector']['points']['size'] = isize.GetId()
-        isize.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
-                                        subkey = ['points', 'size']))
-        gridSizer.Add(item = isize, pos = (row, 1),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        # icon width
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Width:")),
-                      pos = (row, 2), flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        iwidth = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, size = (65, -1),
-                            initial = 2,
-                            min = 1,
-                            max = 1e6)
-        self.win['vector']['points']['width'] = isize.GetId()
-        iwidth.SetValue(UserSettings.Get(group = 'nviz', key = 'vector',
-                                         subkey = ['points', 'width']))
-        gridSizer.Add(item = iwidth, pos = (row, 3),
-                      flag = wx.ALIGN_CENTER_VERTICAL)
-        
-        # icon symbol
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Marker:")),
-                      pos = (row, 4), flag = wx.ALIGN_CENTER_VERTICAL)
-        isym = wx.Choice (parent = panel, id = wx.ID_ANY, size = (100, -1),
-                          choices = UserSettings.Get(group = 'nviz', key = 'vector',
-                                                   subkey = ['points', 'marker'], internal = True))
-        isym.SetName("selection")
-        self.win['vector']['points']['marker'] = isym.GetId()
-        isym.SetSelection(UserSettings.Get(group = 'nviz', key = 'vector',
-                                           subkey = ['points', 'marker']))
-        gridSizer.Add(item = isym, flag = wx.ALIGN_CENTER_VERTICAL,
-                      pos = (row, 5))
-        
-        # icon color
-        gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY,
-                                         label = _("Color:")),
-                      pos = (row, 6), flag = wx.ALIGN_CENTER_VERTICAL)
-        icolor = csel.ColourSelect(panel, id = wx.ID_ANY)
-        icolor.SetName("color")
-        self.win['vector']['points']['color'] = icolor.GetId()
-        icolor.SetColour(UserSettings.Get(group = 'nviz', key = 'vector',
-                                          subkey = ['points', 'color']))
-        gridSizer.Add(item = icolor, flag = wx.ALIGN_CENTER_VERTICAL,
-                      pos = (row, 7))
-        
-        boxSizer.Add(item = gridSizer, proportion = 1,
-                  flag = wx.ALL | wx.EXPAND, border = 3)
-        pageSizer.Add(item = boxSizer, proportion = 0,
-                      flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
-                      border = 3)
-        
-        panel.SetSizer(pageSizer)
-        
-        return panel
-    
-    def OnDefault(self, event):
-        """Restore default settings"""
-        settings = copy.deepcopy(UserSettings.GetDefaultSettings()['nviz'])
-        UserSettings.Set(group = 'nviz',
-                         value = settings)
-        
-        for subgroup, key in settings.iteritems(): # view, surface, vector...
-            if subgroup != 'view':
-                continue
-            for subkey, value in key.iteritems():
-                for subvalue in value.keys():
-                    win = self.FindWindowById(self.win[subgroup][subkey][subvalue])
-                    val = settings[subgroup][subkey][subvalue]
-                    if subkey == 'position':
-                        val = int(val * 100)
-                    
-                    win.SetValue(val)
-        
-        event.Skip()
-        
-    def OnApply(self, event):
-        """Apply Nviz settings for current session"""
-        settings = UserSettings.Get(group = 'nviz')
-        for subgroup, key in settings.iteritems(): # view, surface, vector...
-            for subkey, value in key.iteritems():
-                for subvalue in value.keys():
-                    try: # TODO
-                        win = self.FindWindowById(self.win[subgroup][subkey][subvalue])
-                    except:
-                        # print 'e', subgroup, subkey, subvalue
-                        continue
-                    
-                    if win.GetName() == "selection":
-                        value = win.GetSelection()
-                    elif win.GetName() == "color":
-                        value = tuple(win.GetColour())
-                    else:
-                        value = win.GetValue()
-                    if subkey == 'position':
-                        value = float(value) / 100
-                    
-                    settings[subgroup][subkey][subvalue] = value
-                    
-    def OnSave(self, event):
-        """!Apply changes, update map and save settings of selected
-        layer
-        """
-        # apply changes
-        self.OnApply(None)
-        
-        if self.GetSelection() == self.page['id']:
-            fileSettings = {}
-            UserSettings.ReadSettingsFile(settings = fileSettings)
-            fileSettings['nviz'] = UserSettings.Get(group = 'nviz')
-            file = UserSettings.SaveToFile(fileSettings)
-            self.parent.goutput.WriteLog(_('Nviz settings saved to file <%s>.') % file)
-        
-    def OnLoad(self, event):
-        """!Apply button pressed"""
-        self.LoadSettings()
-        
-        if event:
-            event.Skip()
-
-    def LoadSettings(self):
-        """!Load saved Nviz settings and apply to current session"""
-        UserSettings.ReadSettingsFile()
-        settings = copy.deepcopy(UserSettings.Get(group = 'nviz'))
-        
-        for subgroup, key in settings.iteritems(): # view, surface, vector...
-            for subkey, value in key.iteritems():
-                for subvalue in value.keys():
-                    if subvalue == 'step':
-                        continue
-                    else:
-                        insetting = value[subvalue]                                                    
-                    if subgroup == 'view':
-                        for viewkey, viewitem in self.mapWindow.view[subkey].iteritems(): 
-                            if viewkey == subvalue:
-                                self.mapWindow.view[subkey][viewkey] = insetting 
-                            else:
-                                continue
-                    else:
-                        for otherkey, otheritem in self.win[subgroup][subkey].iteritems():
-                            if type(otheritem) == data:
-                                for endkey, enditem in otheritem.iteritems():
-                                    if endkey == subvalue:
-                                        paramwin = self.FindWindowById(enditem)
-                                    else:
-                                        continue
-                            else:
-                                if otherkey == subvalue:
-                                    paramwin = self.FindWindowById(otheritem)
-                                else:
-                                    continue
-                            if type(insetting) in [tuple, list] and len(insetting) > 2:
-                                insetting = tuple(insetting)
-                                paramwin.SetColour(insetting)
-                            else:
-                                try:
-                                    paramwin.SetValue(insetting)
-                                except:
-                                    try:
-                                        paramwin.SetStringSelection(insetting)
-                                    except:
-                                        continue
-                                
-        self.toolWin.UpdateSettings()
-        self.FindWindowById(self.win['view']['position']).Draw()
-        self.FindWindowById(self.win['view']['position']).Refresh(False)
-        
-        self.mapWindow.render['quick'] = False
-        self.mapWindow.Refresh(False)
-        
-    def OnSave(self, event):
-        """!Save button pressed
-        
-        Save settings to configuration file
-        """
-        fileSettings = {}
-        UserSettings.ReadSettingsFile(settings = fileSettings)
-        
-        self.toolWin.UpdateSettings()
-        
-        nvsettings = UserSettings.Get(group = 'nviz')
-        for subgroup, key in nvsettings.iteritems(): # view, surface, vector...
-            for subkey, value in key.iteritems():
-                if subkey == 'height': continue
-                for subvalue in value.keys():
-                    if subvalue == 'step':
-                        #no way to change steps for sliders or spinctrls on non-MSW systems
-                        nvsettings[subgroup][subkey][subvalue] = 1 
-                    else:
-                        if subgroup == 'view':
-                            nvsettings[subgroup][subkey][subvalue] = self.mapWindow.view[subkey][subvalue]                            
-                        elif subvalue == 'map':
-                            if subkey == 'shine': 
-                                nvsettings[subgroup][subkey][subvalue] = False
-                            if subkey == 'color': 
-                                nvsettings[subgroup][subkey][subvalue] = True
-                        else:
-                            for otherkey, otheritem in self.win[subgroup][subkey].iteritems():
-                                if type(otheritem) == data:
-                                    for endkey, enditem in otheritem.iteritems():
-                                        if endkey == subvalue:
-                                            if self.FindWindowById(enditem).GetClassName() == 'wxChoice':
-                                                outsetting = self.FindWindowById(enditem).GetSelection()
-                                            else:
-                                                try:
-                                                    outsetting = self.FindWindowById(enditem).GetColour()
-                                                    outsetting = str(outsetting.Red())+':'+str(outsetting.Green())+':'+str(outsetting.Blue())
-                                                except:
-                                                    try:
-                                                        outsetting = self.FindWindowById(enditem).GetValue()
-                                                    except:
-                                                        try:
-                                                            outsetting = self.FindWindowById(enditem).GetString()
-                                                        except:
-                                                            outsetting = ''
-                                            if (type(outsetting) == list or type(outsetting) == tuple) and len(outsetting) > 2:
-                                                outsetting = str(outsetting[0])+':'+str(outsetting[1])+':'+str(outsetting[2])
-                                                
-                                            nvsettings[subgroup][subkey][subvalue][endkey] = outsetting
-                                else:
-                                    if otherkey == subvalue:
-                                        if self.FindWindowById(otheritem).GetClassName() == 'wxChoice':
-                                            outsetting = self.FindWindowById(otheritem).GetSelection()
-                                        else:
-                                            try:
-                                                outsetting = self.FindWindowById(otheritem).GetColour()
-                                                outsetting = str(outsetting.Red())+':'+str(outsetting.Green())+':'+str(outsetting.Blue())
-                                            except:
-                                                try:
-                                                    outsetting = self.FindWindowById(otheritem).GetValue()
-                                                except:
-                                                    try:
-                                                        outsetting = self.FindWindowById(enditem).GetString()
-                                                    except:
-                                                        outsetting = ''
-                                        if (type(outsetting) == list or type(outsetting) == tuple) and len(outsetting) > 2:
-                                            outsetting = str(outsetting[0])+':'+str(outsetting[1])+':'+str(outsetting[2])
-
-                                        nvsettings[subgroup][subkey][subvalue] = outsetting
-                               
-        UserSettings.Set(group = 'nviz', value = nvsettings)
-        file = UserSettings.SaveToFile()
-        self.parent.goutput.WriteLog(_('Nviz settings saved to file <%s>.') % file)

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/preferences.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/preferences.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/preferences.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -470,26 +470,34 @@
                 'view' : {
                     'persp' : {
                         'value' : 20,
-                        'step' : 5,
+                        'step' : 2,
                         },
                     'position' : {
                         'x' : 0.84,
                         'y' : 0.16,
                         },
-                    'height' : {
-                        'step' : 100,
-                        },
                     'twist' : {
                         'value' : 0,
-                        'step' : 5,
                         },
                     'z-exag' : {
-                        'step' : 1,
+                        'min' : 0,
+                        'max' : 10,
+                        'value': 1,
                         },
                     'background' : {
                         'color' : (255, 255, 255, 255), # white
                         },
                     },
+                'fly' : {
+                    'exag' : {
+                        'move' : 5,
+                        'turn' : 5,
+                        }
+                    },
+                'animation' : {
+                    'fps' : 24,
+                    'prefix' : _("animation")
+                    },
                 'surface' : {
                     'shine': {
                         'map' : False,
@@ -497,7 +505,7 @@
                         },
                     'color' : {
                         'map' : True,
-                        'value' : (0, 0, 0, 255), # constant: black
+                        'value' : (100, 100, 100, 255), # constant: grey
                         },
                     'draw' : {
                         'wire-color' : (136, 136, 136, 255),
@@ -513,6 +521,12 @@
                         'z' : 0,
                         },
                     },
+                'constant' : {
+                    'color' : (100, 100, 100, 255),
+                    'value' : 0.0,
+                    'transp' : 0,
+                    'resolution': 6
+                },
                 'vector' : {
                     'lines' : {
                         'show' : False,
@@ -533,7 +547,7 @@
                 'volume' : {
                     'color' : {
                         'map' : True,
-                        'value' : (0, 0, 0, 255), # constant: black
+                        'value' : (100, 100, 100, 255), # constant: grey
                         },
                     'draw' : {
                         'mode'       : 0, # isosurfaces
@@ -544,11 +558,44 @@
                         'map' : False,
                         'value' : 60,
                         },
+                    'topo': {
+                        'map' : None,
+                        'value' : 0.0
+                        },
+                    'transp': {
+                        'map' : None,
+                        'value': 0
+                        },
+                    'mask': {
+                        'map' : None,
+                        'value': ''
+                        },
+                    'slice_position': {
+                        'x1' : 0,
+                        'x2' : 1,
+                        'y1' : 0,
+                        'y2' : 1,
+                        'z1' : 0,
+                        'z2' : 1,
+                        'axis' : 0,
+                        }
                     },
+                'cplane' : {
+                    'shading': 4,
+                    'rotation':{
+                        'rot': 0, 
+                        'tilt': 0
+                        }, 
+                    'position':{
+                        'x' : 0,
+                        'y' : 0,
+                        'z' : 0
+                    }   
+                },
                 'light' : {
                     'position' : {
                         'x' : 0.68,
-                        'y' : 0.68,
+                        'y' : -0.68,
                         'z' : 80,
                         },
                     'bright'  : 80,
@@ -559,6 +606,12 @@
                     'elev'   : 55,
                     'color'  : (128, 128, 128, 255), # grey
                     },
+                'arrow': {
+                    'color': (0, 0, 0),
+                    },
+                'scalebar': {
+                    'color': (0, 0, 0),
+                    }
                 },
             'modeler' : {
                 'disabled': {
@@ -668,6 +721,25 @@
         self.internalSettings['nviz']['view']['persp']['max'] = 100
         self.internalSettings['nviz']['view']['height'] = {}
         self.internalSettings['nviz']['view']['height']['value'] = -1
+        self.internalSettings['nviz']['view']['z-exag'] = {}
+        self.internalSettings['nviz']['view']['z-exag']['original'] = 1
+        self.internalSettings['nviz']['view']['rotation'] = None
+        self.internalSettings['nviz']['view']['focus'] = {}
+        self.internalSettings['nviz']['view']['focus']['x'] = -1
+        self.internalSettings['nviz']['view']['focus']['y'] = -1
+        self.internalSettings['nviz']['view']['focus']['z'] = -1
+        self.internalSettings['nviz']['view']['dir'] = {}
+        self.internalSettings['nviz']['view']['dir']['x'] = -1
+        self.internalSettings['nviz']['view']['dir']['y'] = -1
+        self.internalSettings['nviz']['view']['dir']['z'] = -1
+        self.internalSettings['nviz']['view']['dir']['use'] = False
+        
+        for decor in ('arrow', 'scalebar'):
+            self.internalSettings['nviz'][decor] = {}
+            self.internalSettings['nviz'][decor]['position'] = {}
+            self.internalSettings['nviz'][decor]['position']['x'] = 0
+            self.internalSettings['nviz'][decor]['position']['y'] = 0
+            self.internalSettings['nviz'][decor]['size'] = 100
         self.internalSettings['nviz']['vector'] = {}
         self.internalSettings['nviz']['vector']['points'] = {}
         self.internalSettings['nviz']['vector']['points']['marker'] = ("x",
@@ -933,6 +1005,16 @@
         """!Get default user settings"""
         return self.defaultSettings
 
+    def Reset(self, key = None):
+        """!Reset to default settings
+
+        @key key in settings dict (None for all keys)
+        """
+        if not key:
+            self.userSettings = copy.deepcopy(self.defaultSettings)
+        else:
+            self.userSettings[key] = copy.deepcopy(self.defaultSettings[key])
+        
 globalSettings = Settings()
 
 class PreferencesBaseDialog(wx.Dialog):

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/render.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/render.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/render.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -698,8 +698,7 @@
         if windres:
             compRegion = self.GetRegion()
             region = copy.copy(self.region)
-            for key in ('nsres', 'ewres',
-                        'rows', 'cols', 'cells'):
+            for key in ('nsres', 'ewres', 'cells'):
                 region[key] = compRegion[key]
         else:
             # adjust region settings to match monitor
@@ -733,10 +732,14 @@
                         (region['nsres'])
                     continue
                 elif key == "cols":
+                    if windres:
+                        continue
                     grass_region += 'cols: %d; ' % \
                         region['cols']
                     continue
                 elif key == "rows":
+                    if windres:
+                        continue
                     grass_region += 'rows: %d; ' % \
                         region['rows']
                     continue
@@ -1241,7 +1244,7 @@
     def DeleteOverlay(self, overlay):
         """!Delete overlay
         
-        @param id overlay id
+        @param overlay overlay layer
         
         @return removed overlay on success or None
         """
@@ -1288,6 +1291,12 @@
         """!Reverse list of layers"""
         return self.layers.reverse()
 
+    def RenderOverlays(self, force):
+        """!Render overlays only (for nviz)"""
+        for layer in self.overlays:
+            if force or layer.force_render:
+                layer.Render()
+                
 if __name__ == "__main__":
     import gettext
     gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/toolbars.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/toolbars.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/toolbars.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -10,7 +10,7 @@
  - GCPDisplayToolbar
  - VDigitToolbar
  - ProfileToolbar
- - NvizToolbar
+ - LMNvizToolbar
  - ModelToolbar
  - HistogramToolbar
  - LMWorkspaceToolbar
@@ -27,7 +27,7 @@
 @author Michael Barton
 @author Jachym Cepicky
 @author Martin Landa <landa.martin gmail.com>
- at author Anna Kratochvilova <anna.kratochvilova fsv.cvut.cz>
+ at author Anna Kratochvilova <kratochanna gmail.com>
 """
 
 import os
@@ -75,9 +75,10 @@
         return None
     
     def CreateTool(self, label, bitmap, kind,
-                   shortHelp, longHelp, handler):
+                   shortHelp, longHelp, handler, pos = -1):
         """!Add tool to the toolbar
         
+        @param pos if -1 add tool, if > 0 insert at given pos
         @return id of tool
         """
         bmpDisabled = wx.NullBitmap
@@ -86,10 +87,14 @@
             tool = vars(self)[label] = wx.NewId()
             Debug.msg(3, "CreateTool(): tool=%d, label=%s bitmap=%s" % \
                           (tool, label, bitmap))
-            
-            toolWin = self.AddLabelTool(tool, label, bitmap,
-                                        bmpDisabled, kind,
-                                        shortHelp, longHelp)
+            if pos < 0:
+                toolWin = self.AddLabelTool(tool, label, bitmap,
+                                            bmpDisabled, kind,
+                                            shortHelp, longHelp)
+            else:
+                toolWin = self.InsertLabelTool(pos, tool, label, bitmap,
+                                            bmpDisabled, kind,
+                                            shortHelp, longHelp)
             self.Bind(wx.EVT_TOOL, handler, toolWin)
         else: # separator
             self.AddSeparator()
@@ -149,10 +154,10 @@
                         'desc' : self.defaultAction.get('desc', '') }
         
     def FixSize(self, width):
-	"""!Fix toolbar width on Windows
-        
-	@todo Determine why combobox causes problems here
-	"""
+        """!Fix toolbar width on Windows
+            
+        @todo Determine why combobox causes problems here
+        """
         if platform.system() == 'Windows':
             size = self.GetBestSize()
             self.SetSize((size[0] + width, size[1]))
@@ -176,16 +181,15 @@
         retData = list()
         for args in data:
             retData.append(self._defineTool(*args))
-        
         return retData
 
-    def _defineTool(self, name = None, icon = None, handler = None, item = wx.ITEM_NORMAL):
+    def _defineTool(self, name = None, icon = None, handler = None, item = wx.ITEM_NORMAL, pos = -1):
         """!Define tool
         """
         if name:
             return (name, icon.GetBitmap(),
                     item, icon.GetLabel(), icon.GetDesc(),
-                    handler)
+                    handler, pos)
         return ("", "", "", "", "", "") # separator
     
 class MapToolbar(AbstractToolbar):
@@ -308,7 +312,40 @@
                                       self.parent.PrintMenu),
                                      (None, ))
                                     )
-    
+    def InsertTool(self, data):
+        """!Insert tool to toolbar
+        
+        @param data toolbar data"""
+        data = self._getToolbarData(data)
+        for tool in data:
+            self.CreateTool(*tool)
+        self.Realize()
+        
+        self.parent._mgr.GetPane('mapToolbar').BestSize(self.GetBestSize())
+        self.parent._mgr.Update()
+        
+    def RemoveTool(self, tool):
+        """!Remove tool from toolbar
+        
+        @param tool tool id"""
+        self.DeleteTool(tool)
+        
+        self.parent._mgr.GetPane('mapToolbar').BestSize(self.GetBestSize())
+        self.parent._mgr.Update()
+        
+    def ChangeToolsDesc(self, mode2d):
+        """!Change description of zoom tools for 2D/3D view"""
+        if mode2d:
+            set = 'displayWindow'
+        else:
+            set = 'nviz'
+        for i, data in enumerate(self._data):
+            for tool, toolname in (('zoomin', 'zoomIn'),('zoomout', 'zoomOut')):
+                if data[0] == tool:
+                    tmp = list(data)
+                    tmp[4] = Icons[set][toolname].GetDesc()
+                    self._data[i] = tuple(tmp)
+                
     def OnSelectTool(self, event):
         """!Select / enable tool available in tools list
         """
@@ -317,11 +354,12 @@
         if tool == self.toolId['2d']:
             self.ExitToolbars()
             self.Enable2D(True)
+            self.ChangeToolsDesc(mode2d = True)            
         
         elif tool == self.toolId['3d'] and \
-                not self.parent.toolbars['nviz']:
+                not (self.parent.MapWindow3D and self.parent.IsPaneShown('3d')):
             self.ExitToolbars()
-            self.parent.AddToolbar("nviz")
+            self.parent.AddNviz()
             
         elif tool == self.toolId['vdigit'] and \
                 not self.parent.toolbars['vdigit']:
@@ -332,19 +370,13 @@
     def ExitToolbars(self):
         if self.parent.toolbars['vdigit']:
             self.parent.toolbars['vdigit'].OnExit()
-        if self.parent.toolbars['nviz']:       
-            self.parent.toolbars['nviz'].OnExit()
+        if self.parent.GetLayerManager().IsPaneShown('toolbarNviz'):
+            self.parent.RemoveNviz()
         
     def Enable2D(self, enabled):
         """!Enable/Disable 2D display mode specific tools"""
-        for tool in (self.pointer,
-                     self.pan,
-                     self.zoomin,
-                     self.zoomout,
-                     self.zoomback,
-                     self.zoommenu,
+        for tool in (self.zoommenu,
                      self.analyze,
-                     self.dec,
                      self.printmap):
             self.EnableTool(tool, enabled)
         
@@ -1227,12 +1259,11 @@
                                       self.parent.OnQuit),
                                      ))
     
-class NvizToolbar(AbstractToolbar):
+class LMNvizToolbar(AbstractToolbar):
     """!Nviz toolbar
     """
-    def __init__(self, parent, mapcontent):
-        self.mapcontent = mapcontent
-        self.lmgr = parent.GetLayerManager()
+    def __init__(self, parent):
+        self.lmgr = parent
         
         AbstractToolbar.__init__(self, parent)
         
@@ -1247,55 +1278,19 @@
     def _toolbarData(self):
         """!Toolbar data"""
         icons = Icons['nviz']
-        return self._getToolbarData((("view", icons["view"],
-                                      self.OnShowPage),
+        return self._getToolbarData((("nvizCmd", icons['nvizCmd'],
+                                      self.OnNvizCmd),
                                      (None, ),
-                                     ("surface", icons["surface"],
-                                      self.OnShowPage),
-                                     ("vector", icons["vector"],
-                                      self.OnShowPage),
-                                     ("volume", icons["volume"],
-                                      self.OnShowPage),
-                                     (None, ),
-                                     ("light", icons["light"],
-                                      self.OnShowPage),
-                                     ("fringe", icons["fringe"],
-                                      self.OnShowPage),
-                                     (None, ),
                                      ("settings", icons["settings"],
                                       self.OnSettings),
-                                     ("help", Icons['misc']["help"],
-                                      self.OnHelp),
-                                     (None, ),
-                                     ('quit', icons["quit"],
-                                      self.OnExit))
+                                     ("help", icons["help"],
+                                      self.OnHelp))
                                     )
-    
-    def OnShowPage(self, event):
-        """!Go to the selected page"""
-        if not self.lmgr or not hasattr(self.lmgr, "nviz"):
-            event.Skip()
-            return
         
-        self.lmgr.notebook.SetSelectionByName('nviz')
-        eId = event.GetId()
-        if eId == self.view:
-            self.lmgr.nviz.SetPage('view')
-        elif eId == self.surface:
-            self.lmgr.nviz.SetPage('surface')
-        elif eId == self.surface:
-            self.lmgr.nviz.SetPage('surface')
-        elif eId == self.vector:
-            self.lmgr.nviz.SetPage('vector')
-        elif eId == self.volume:
-            self.lmgr.nviz.SetPage('volume')
-        elif eId == self.light:
-            self.lmgr.nviz.SetPage('light')
-        elif eId == self.fringe:
-            self.lmgr.nviz.SetPage('fringe')
+    def OnNvizCmd(self, event):
+        """!Show m.nviz.image command"""
+        self.lmgr.GetLayerTree().GetMapDisplay().GetWindow().OnNvizCmd()
         
-        self.lmgr.Raise()
-
     def OnHelp(self, event):
         """!Show 3D view mode help"""
         if not self.lmgr:
@@ -1311,20 +1306,7 @@
         if not self.settingsDialog:
             self.settingsDialog = NvizPreferencesDialog(parent = self.parent)
         self.settingsDialog.Show()
-            
-    def OnExit (self, event = None):
-        """!Quit nviz tool (swith to 2D mode)"""
-        # set default mouse settings
-        self.parent.MapWindow.mouse['use'] = "pointer"
-        self.parent.MapWindow.mouse['box'] = "point"
-        self.parent.MapWindow.polycoords = []
         
-        # return to map layer page (gets rid of ugly exit bug)
-        self.lmgr.notebook.SetSelectionByName('layers')
-
-        # disable the toolbar
-        self.parent.RemoveToolbar("nviz")
-        
 class ModelToolbar(AbstractToolbar):
     """!Graphical modeler toolbar (see gmodeler.py)
     """

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/workspace.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/workspace.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/workspace.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -9,16 +9,19 @@
  - WriteWorkspaceFile
  - ProcessGrcFile
 
-(C) 2007-2010 by the GRASS Development Team
+(C) 2007-2011 by the GRASS Development Team
 This program is free software under the GNU General Public
 License (>=v2). Read the file COPYING that comes with GRASS
 for details.
 
 @author Martin Landa <landa.martin gmail.com>
+ at author Anna Kratochvilova <kratochanna gmail.com> (wxNviz / Google SoC 2011)
 """
 
 import os
 import sys
+import copy
+import types
 
 import wx
 
@@ -64,6 +67,10 @@
         # list of map layers
         #
         self.layers = []
+        #
+        # nviz state
+        #
+        self.nviz_state = {}
         
         self.displayIndex = -1 # first display has index '0'
         
@@ -150,10 +157,13 @@
                     "extent"         : extent,
                     "alignExtent"    : bool(int(display.get('alignExtent', "0"))),
                     "constrainRes"   : bool(int(display.get('constrainRes', "0"))),
-                    "projection"     : projection, } )
+                    "projection"     : projection,
+                    "viewMode"       : display.get('viewMode', '2d')} )
             
             # process all layers/groups in the display
             self.__processLayers(display)
+            # process nviz_state
+            self.__processNvizState(display)
 
     def __processLayers(self, node, inGroup = -1):
         """!Process layers/groups of selected display
@@ -331,12 +341,12 @@
             # position
             node_pos = node_surface.find('position')
             if node_pos is not None:
-                dc = self.nviz['surface']['position'] = {}
+                dc = nviz['surface']['position'] = {}
                 for coor in ['x', 'y', 'z']:
                     node = node_pos.find(coor)
                     if node is None:
                         continue
-                    value = int(self.__getNodeText(node, 'value'))
+                    value = int(self.__getNodeText(node_pos, coor))
                     dc[coor] = value
             
         elif 'vector' in nviz:
@@ -351,9 +361,17 @@
                 node_mode = node_vpoints.find('mode')
                 if node_mode is not None:
                     nviz['vector']['points']['mode'] = {}
-                    nviz['vector']['points']['mode']['type'] = str(node_mode.get('type', ''))
-                    nviz['vector']['points']['mode']['surface'] = \
-                        self.__processLayerNvizNode(node_mode, 'map', str)
+                    nviz['vector']['points']['mode']['type'] = str(node_mode.get('type', 'surface'))
+                    nviz['vector']['points']['mode']['surface'] = {}
+                    nviz['vector']['points']['mode']['surface']['value'] = []
+                    nviz['vector']['points']['mode']['surface']['show'] = []
+                    
+                    # map
+                    for node_map in node_mode.findall('map'):
+                        nviz['vector']['points']['mode']['surface']['value'].append(
+                            self.__processLayerNvizNode(node_map, 'name', str))
+                        nviz['vector']['points']['mode']['surface']['show'].append(bool(
+                            self.__processLayerNvizNode(node_map, 'checked', int)))
                 
                 # color
                 self.__processLayerNvizNode(node_vpoints, 'color', str,
@@ -370,7 +388,7 @@
                 # height
                 self.__processLayerNvizNode(node_vpoints, 'size', int,
                                             nviz['vector']['points'])
-            
+                
             # vlines
             node_vlines = node_nviz.find('vlines')
             if node_vlines is not None:
@@ -378,11 +396,16 @@
                 if node_mode is not None:
                     nviz['vector']['lines']['mode'] = {}
                     nviz['vector']['lines']['mode']['type'] = str(node_mode.get('type', ''))
-                    nviz['vector']['lines']['mode']['surface'] = ''
+                    nviz['vector']['lines']['mode']['surface'] = {}
+                    nviz['vector']['lines']['mode']['surface']['value'] = []
+                    nviz['vector']['lines']['mode']['surface']['show'] = []
                     
                     # map
-                    nviz['vector']['lines']['mode']['surface'] = \
-                        self.__processLayerNvizNode(node_mode, 'map', str)
+                    for node_map in node_mode.findall('map'):
+                        nviz['vector']['lines']['mode']['surface']['value'].append(
+                            self.__processLayerNvizNode(node_map, 'name', str))
+                        nviz['vector']['lines']['mode']['surface']['show'].append(bool(
+                            self.__processLayerNvizNode(node_map, 'checked', int)))
                 
                 # color
                 self.__processLayerNvizNode(node_vlines, 'color', str,
@@ -395,7 +418,7 @@
                 # height
                 self.__processLayerNvizNode(node_vlines, 'height', int,
                                             nviz['vector']['lines'])
-            
+                
         return nviz
     
     def __processLayerNvizNode(self, node, tag, cast, dc = None):
@@ -418,14 +441,124 @@
             else:
                 return value
     
+    def __processNvizState(self, node):
+        """!Process tag nviz_state"""
+        node_state = node.find('nviz_state')
+        if node_state is None:
+            return
+        self.nviz_state['display'] = self.displayIndex
+        #
+        # view
+        #
+        node_view = node_state.find('view')
+        view = {}
+        iview = {}
+        
+        node_position = node_view.find('v_position')
+        view['position'] = {}
+        view['position']['x'] = self.__processLayerNvizNode(node_position, 'x', float)
+        view['position']['y'] = self.__processLayerNvizNode(node_position, 'y', float)
+        node_persp = node_view.find('persp')
+        view['persp'] = {}
+        iview['persp'] = {}
+        view['persp']['value'] = self.__processLayerNvizNode(node_persp, 'value', int)
+        view['persp']['step'] = self.__processLayerNvizNode(node_persp, 'step', int)
+        iview['persp']['min'] = self.__processLayerNvizNode(node_persp, 'min', int)
+        iview['persp']['max'] = self.__processLayerNvizNode(node_persp, 'max', int)
+        node_height = node_view.find('v_height')
+        iview['height'] = {}
+        iview['height']['value'] = self.__processLayerNvizNode(node_height, 'value', int)
+        iview['height']['min'] = self.__processLayerNvizNode(node_height, 'min', int)
+        iview['height']['max'] = self.__processLayerNvizNode(node_height, 'max', int)
+        node_twist = node_view.find('twist')
+        view['twist'] = {}
+        iview['twist'] = {}
+        view['twist']['value'] = self.__processLayerNvizNode(node_twist, 'value', int)
+        iview['twist']['min'] = self.__processLayerNvizNode(node_twist, 'min', int)
+        iview['twist']['max'] = self.__processLayerNvizNode(node_twist, 'max', int)
+        node_zexag = node_view.find('z-exag')
+        view['z-exag'] = {}
+        iview['z-exag'] = {}
+        view['z-exag']['value'] = self.__processLayerNvizNode(node_zexag, 'value', int)
+        view['z-exag']['min'] = self.__processLayerNvizNode(node_zexag, 'min', int)
+        view['z-exag']['max'] = self.__processLayerNvizNode(node_zexag, 'max', int)
+        iview['z-exag']['original'] = self.__processLayerNvizNode(node_zexag, 'original', float)
+        node_focus = node_view.find('focus')
+        iview['focus'] = {}
+        iview['focus']['x'] = self.__processLayerNvizNode(node_focus, 'x', int)
+        iview['focus']['y'] = self.__processLayerNvizNode(node_focus, 'y', int)
+        iview['focus']['z'] = self.__processLayerNvizNode(node_focus, 'z', int)
+        node_dir = node_view.find('dir')
+        if node_dir:
+            iview['dir'] = {}
+            iview['dir']['x'] = self.__processLayerNvizNode(node_dir, 'x', int)
+            iview['dir']['y'] = self.__processLayerNvizNode(node_dir, 'y', int)
+            iview['dir']['z'] = self.__processLayerNvizNode(node_dir, 'z', int)
+            iview['dir']['use'] = True
+        else:
+            iview['dir'] = {}
+            iview['dir']['x'] = -1
+            iview['dir']['y'] = -1
+            iview['dir']['z'] = -1
+            iview['dir']['use'] = False
+        
+        view['background'] = {}
+        color = self.__processLayerNvizNode(node_view, 'background_color', str)
+        view['background']['color'] = tuple(map(int, color.split(':')))
+        
+        self.nviz_state['view'] = view
+        self.nviz_state['iview'] = iview
+        #
+        # light
+        #
+        node_light = node_state.find('light')
+        light = {}
+        
+        node_position = node_light.find('l_position')
+        light['position'] = {}
+        light['position']['x'] = self.__processLayerNvizNode(node_position, 'x', float)
+        light['position']['y'] = self.__processLayerNvizNode(node_position, 'y', float)
+        light['position']['z'] = self.__processLayerNvizNode(node_position, 'z', int)
+        
+        light['bright'] = self.__processLayerNvizNode(node_light, 'bright', int) 
+        light['ambient'] = self.__processLayerNvizNode(node_light, 'ambient', int)
+        color = self.__processLayerNvizNode(node_light, 'color', str)
+        light['color'] = tuple(map(int, color.split(':')))
+        
+        self.nviz_state['light'] = light
+        
+        node_constants = node_state.find('constant_planes')
+        constants = []
+        if node_constants:
+            for i, node_plane in enumerate(node_constants.findall('plane')):
+                plane = {}
+                plane['color'] = self.__processLayerNvizNode(node_plane, 'color', str)                
+                plane['resolution'] = self.__processLayerNvizNode(node_plane, 'fine_resolution', int)
+                plane['value'] = self.__processLayerNvizNode(node_plane, 'height', int)
+                plane['object'] = {}
+                constants.append({'constant': plane})
+        self.nviz_state['constants'] = constants    
+        
 class Nviz:
     def __init__(self):
         """Default 3D settings"""
-        pass
+        UserSettings.Reset('nviz')
+        UserSettings.ReadSettingsFile()
+        
+    def SetConstantDefaultProp(self):
+        """Set default constant data properties"""
+        data = dict()
+        for key, value in UserSettings.Get(group='nviz', key='constant').iteritems():
+            data[key] = value
+        color = str(data['color'][0]) + ':' + str(data['color'][1]) + ':' + str(data['color'][2])
+        data['color'] = color
+
+        return data
     
-    def SetSurfaceDefaultProp(self):
+    def SetSurfaceDefaultProp(self, data = None):
         """Set default surface data properties"""
-        data = dict()
+        if not data:
+            data = dict()
         for sec in ('attribute', 'draw', 'mask', 'position'):
             data[sec] = {}
         
@@ -434,7 +567,7 @@
         #
         for attrb in ('shine', ):
             data['attribute'][attrb] = {}
-            for key, value in UserSettings.Get(group='nviz', key='volume',
+            for key, value in UserSettings.Get(group='nviz', key='surface',
                                                subkey=attrb).iteritems():
                 data['attribute'][attrb][key] = value
             data['attribute'][attrb]['update'] = None
@@ -469,7 +602,11 @@
         data['draw']['mode'] = { 'value' : value,
                                  'desc' : desc, 
                                  'update': None }
-        
+        # position
+        for coord in ('x', 'y', 'z'):
+            data['position'][coord] = UserSettings.Get(group='nviz', key='surface', subkey=['position', coord])
+        data['position']['update'] = None
+            
         return data
     
     def SetVolumeDefaultProp(self):
@@ -484,14 +621,15 @@
         # draw
         #
         for control, value in UserSettings.Get(group='nviz', key='volume', subkey='draw').iteritems():
-            if control == 'mode':
-                continue
             if control == 'shading':
-                sel = UserSettings.Get(group='nviz', key='surface', subkey=['draw', 'shading'])
+                sel = UserSettings.Get(group='nviz', key='volume', subkey=['draw', 'shading'])
                 value, desc = self.GetDrawMode(shade=sel, string=False)
-
-                data['draw']['shading'] = { 'value' : value,
-                                            'desc' : desc['shading'] }
+                
+                data['draw']['shading'] = {}
+                data['draw']['shading']['isosurface'] = { 'value' : value,
+                                                          'desc' : desc['shading'] }
+                data['draw']['shading']['slice'] = { 'value' : value,
+                                                     'desc' : desc['shading'] }
             elif control == 'mode':
                 sel = UserSettings.Get(group='nviz', key='volume', subkey=['draw', 'mode'])
                 if sel == 0:
@@ -501,7 +639,9 @@
                 data['draw']['mode'] = { 'value' : sel,
                                          'desc' : desc, }
             else:
-                data['draw'][control] = { 'value' : value }
+                data['draw'][control] = {}
+                data['draw'][control]['isosurface'] = { 'value' : value }
+                data['draw'][control]['slice'] = { 'value' : value }
 
             if 'update' not in data['draw'][control]:
                 data['draw'][control]['update'] = None
@@ -517,9 +657,32 @@
         
         return data
     
-    def SetVectorDefaultProp(self):
+    def SetIsosurfaceDefaultProp(self):
+        """!Set default isosurface properties"""
+        data = dict()
+        for attr in ('shine', 'topo', 'transp', 'color'):
+            data[attr] = {}
+            for key, value in UserSettings.Get(group = 'nviz', key = 'volume',
+                                               subkey = attr).iteritems():
+                data[attr][key] = value
+            data[attr]['update'] = None
+        return data
+    
+    def SetSliceDefaultProp(self):
+        """!Set default slice properties"""
+        data = dict()
+        data['position'] = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'volume',
+                                               subkey = 'slice_position'))
+        data['position']['update'] = None
+        
+        data['transp'] = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'volume',
+                                               subkey = 'transp'))
+        return data
+    
+    def SetVectorDefaultProp(self, data = None):
         """Set default vector data properties"""
-        data = dict()
+        if not data:
+            data = dict()
         for sec in ('lines', 'points'):
             data[sec] = {}
         
@@ -544,21 +707,17 @@
         if UserSettings.Get(group='nviz', key='vector',
                             subkey=['lines', 'flat']):
             type = 'flat'
-            map  = None
+            
         else:
-            type = 'flat'
-            map = None
-
+            type = 'surface'
+            
         data['mode'] = {}
         data['mode']['type'] = type
         data['mode']['update'] = None
-        if map:
-            data['mode']['surface'] = map
-
+    
         # height
         data['height'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
                                                       subkey=['lines', 'height']) }
-
         if 'object' in data:
             for attrb in ('color', 'width', 'mode', 'height'):
                 data[attrb]['update'] = None
@@ -584,16 +743,15 @@
         data['color'] = { 'value' : color }
 
         # mode
-        data['mode'] = { 'type' : 'surface',
-                         'surface' : '', }
+        data['mode'] = { 'type' : 'surface'}
+##                         'surface' : '', }
         
         # height
         data['height'] = { 'value' : UserSettings.Get(group='nviz', key='vector',
                                                       subkey=['points', 'height']) }
-
+        
         if 'object' in data:
-            for attrb in ('size', 'width', 'marker',
-                          'color', 'surface', 'height'):
+            for attrb in ('size', 'width', 'marker', 'color', 'height'):
                 data[attrb]['update'] = None
         
     def GetDrawMode(self, mode=None, style=None, shade=None, string=False):
@@ -662,6 +820,28 @@
         
         return (value, desc)
     
+    def SetDecorDefaultProp(self, type):
+        """!Set default arrow properties
+        """
+        data = {}
+        
+        # arrow
+        if type == 'arrow':
+            data['arrow'] = UserSettings.Get(group = 'nviz', key = 'arrow')
+            data['arrow']['color'] = "%d:%d:%d" % (
+                UserSettings.Get(group = 'nviz', key = 'arrow', subkey = 'color')[:3])
+            data['arrow'].update(UserSettings.Get(group = 'nviz', key = 'arrow', internal = True))
+            data['arrow']['show'] = False
+        
+        # arrow
+        if type == 'scalebar':
+            data['scalebar'] = copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'scalebar'))
+            data['scalebar']['color'] = "%d:%d:%d" % (
+                UserSettings.Get(group = 'nviz', key = 'scalebar', subkey = 'color')[:3])
+            data['scalebar'].update(copy.deepcopy(UserSettings.Get(group = 'nviz', key = 'scalebar', internal = True)))
+            data['scalebar']['id'] = 0
+        return data
+    
 class WriteWorkspaceFile(object):
     """!Generic class for writing workspace file"""
     def __init__(self, lmgr, file):
@@ -695,13 +875,18 @@
             
             displayPos = mapTree.mapdisplay.GetPosition()
             displaySize = mapTree.mapdisplay.GetSize()
+            if mapTree.mapdisplay.toolbars['map'].combo.GetSelection() == 1:
+                viewmode = '3d'
+            else:
+                viewmode = '2d'
             
             file.write('%s<display render="%d" '
                        'mode="%d" showCompExtent="%d" '
                        'alignExtent="%d" '
                        'constrainRes="%d" '
                        'dim="%d,%d,%d,%d" '
-                       'extent="%f,%f,%f,%f">\n' % (' ' * self.indent,
+                       'extent="%f,%f,%f,%f" '
+                       'viewMode="%s" >\n' % (' ' * self.indent,
                                                     int(mapTree.mapdisplay.statusbarWin['render'].IsChecked()),
                                                     mapTree.mapdisplay.statusbarWin['toggle'].GetSelection(),
                                                     int(mapTree.mapdisplay.statusbarWin['region'].IsChecked()),
@@ -714,7 +899,8 @@
                                                     region['w'],
                                                     region['s'],
                                                     region['e'],
-                                                    region['n']
+                                                    region['n'],
+                                                    viewmode
                                                     ))
             # projection statusbar info
             if mapTree.mapdisplay.statusbarWin['projection'].IsChecked() and \
@@ -735,6 +921,12 @@
             # list of layers
             item = mapTree.GetFirstChild(mapTree.root)[0]
             self.__writeLayer(mapTree, item)
+            
+            if mapTree.mapdisplay.MapWindow3D is not None:
+                nvizDisp = mapTree.mapdisplay.MapWindow3D
+                self.__writeNvizState(view = nvizDisp.view, iview =  nvizDisp.iview, 
+                                      light = nvizDisp.light, constants = nvizDisp.constants)
+            
             file.write('%s</display>\n' % (' ' * self.indent))
         
         self.indent =- 4
@@ -843,7 +1035,6 @@
         """
         if 'object' not in data: # skip disabled
             return
-
         self.indent += 4
         self.file.write('%s<surface>\n' % (' ' * self.indent))
         self.indent += 4
@@ -940,8 +1131,14 @@
                                                           data[attrb][name]['type']))
                     if data[attrb][name]['type'] == 'surface':
                         self.indent += 4
-                        self.file.write('%s<map>%s</map>\n' % (' ' * self.indent,
-                                                               data[attrb][name]['surface']))
+                        for idx, surface in enumerate(data[attrb][name]['surface']['value']):
+                            checked = data[attrb][name]['surface']['show'][idx]
+                            self.file.write('%s<map>\n' % (' ' * self.indent))
+                            self.indent += 4
+                            self.file.write('%s<name>%s</name>\n' % (' ' * self.indent, surface))
+                            self.file.write('%s<checked>%s</checked>\n' % (' ' * self.indent, int(checked)))
+                            self.indent -= 4
+                            self.file.write('%s</map>\n' % (' ' * self.indent))
                         self.indent -= 4
                     self.file.write('%s</%s>\n' % ((' ' * self.indent, name)))
                 else:
@@ -955,6 +1152,132 @@
 
         self.indent -= 4
 
+    def __writeNvizState(self, view, iview, light, constants):
+        """"!Save Nviz properties (view, light) to workspace
+
+        @param view Nviz view properties
+        @param iview Nviz internal view properties
+        @param light Nviz light properties
+        """
+        self.indent += 4
+        self.file.write('%s<nviz_state>\n' % (' ' * self.indent))
+        #
+        # view
+        #
+        self.indent += 4
+        self.file.write('%s<view>\n' % (' ' * self.indent))
+        self.indent += 4
+        # position
+        self.file.write('%s<v_position>\n' % (' ' * self.indent))
+        self.indent += 4
+        self.file.write('%s<x>%.2f</x>\n' % (' ' * self.indent, view['position']['x']))
+        self.file.write('%s<y>%.2f</y>\n' % (' ' * self.indent, view['position']['y']))
+        self.indent -= 4
+        self.file.write('%s</v_position>\n' % (' ' * self.indent))
+        # perspective
+        self.file.write('%s<persp>\n' % (' ' * self.indent))
+        self.indent += 4
+        self.file.write('%s<value>%d</value>\n' % (' ' * self.indent, view['persp']['value']))
+        self.file.write('%s<step>%d</step>\n' % (' ' * self.indent, view['persp']['step']))
+        self.file.write('%s<min>%d</min>\n' % (' ' * self.indent, iview['persp']['min']))
+        self.file.write('%s<max>%d</max>\n' % (' ' * self.indent, iview['persp']['max']))
+        self.indent -= 4
+        self.file.write('%s</persp>\n' % (' ' * self.indent))
+        # height
+        self.file.write('%s<v_height>\n' % (' ' * self.indent))
+        self.indent += 4
+        self.file.write('%s<value>%d</value>\n' % (' ' * self.indent, iview['height']['value']))
+        self.file.write('%s<min>%d</min>\n' % (' ' * self.indent, iview['height']['min']))
+        self.file.write('%s<max>%d</max>\n' % (' ' * self.indent, iview['height']['max']))
+        self.indent -= 4
+        self.file.write('%s</v_height>\n' % (' ' * self.indent))
+        # twist
+        self.file.write('%s<twist>\n' % (' ' * self.indent))
+        self.indent += 4
+        self.file.write('%s<value>%d</value>\n' % (' ' * self.indent, view['twist']['value']))
+        self.file.write('%s<min>%d</min>\n' % (' ' * self.indent, iview['twist']['min']))
+        self.file.write('%s<max>%d</max>\n' % (' ' * self.indent, iview['twist']['max']))
+        self.indent -= 4
+        self.file.write('%s</twist>\n' % (' ' * self.indent))
+        # z-exag
+        self.file.write('%s<z-exag>\n' % (' ' * self.indent))
+        self.indent += 4
+        self.file.write('%s<value>%d</value>\n' % (' ' * self.indent, view['z-exag']['value']))
+        self.file.write('%s<min>%d</min>\n' % (' ' * self.indent, view['z-exag']['min']))
+        self.file.write('%s<max>%d</max>\n' % (' ' * self.indent, view['z-exag']['max']))
+        self.file.write('%s<original>%d</original>\n' % (' ' * self.indent, iview['z-exag']['original']))
+        self.indent -= 4
+        self.file.write('%s</z-exag>\n' % (' ' * self.indent))
+        # focus (look here)
+        self.file.write('%s<focus>\n' % (' ' * self.indent))
+        self.indent += 4
+        self.file.write('%s<x>%d</x>\n' % (' ' * self.indent, iview['focus']['x']))
+        self.file.write('%s<y>%d</y>\n' % (' ' * self.indent, iview['focus']['y']))
+        self.file.write('%s<z>%d</z>\n' % (' ' * self.indent, iview['focus']['z']))
+        self.indent -= 4
+        self.file.write('%s</focus>\n' % (' ' * self.indent))
+        # background
+        self.__writeTagWithValue('background_color', view['background']['color'][:3], format = 'd:%d:%d')
+        
+        self.indent -= 4
+        self.file.write('%s</view>\n' % (' ' * self.indent))
+        #
+        # light
+        #
+        self.file.write('%s<light>\n' % (' ' * self.indent))
+        self.indent += 4
+        # position
+        self.file.write('%s<l_position>\n' % (' ' * self.indent))
+        self.indent += 4
+        self.file.write('%s<x>%.2f</x>\n' % (' ' * self.indent, light['position']['x']))
+        self.file.write('%s<y>%.2f</y>\n' % (' ' * self.indent, light['position']['y']))
+        self.file.write('%s<z>%d</z>\n' % (' ' * self.indent, light['position']['z']))
+        self.indent -= 4
+        self.file.write('%s</l_position>\n' % (' ' * self.indent))
+        # bright
+        self.__writeTagWithValue('bright', light['bright'])
+        # ambient
+        self.__writeTagWithValue('ambient', light['ambient'])
+        # color
+        self.__writeTagWithValue('color', light['color'][:3], format = 'd:%d:%d')
+        
+        self.indent -= 4
+        self.file.write('%s</light>\n' % (' ' * self.indent))
+        #
+        # constant planes
+        #
+        if constants:
+            self.file.write('%s<constant_planes>\n' % (' ' * self.indent))
+            self.indent += 4
+            for idx, plane in enumerate(constants):
+                self.file.write('%s<plane>\n' % (' ' * self.indent))
+                self.indent += 4
+                self.__writeTagWithValue('height', constants[idx]['constant']['value'])
+                self.__writeTagWithValue('fine_resolution', constants[idx]['constant']['resolution'])
+                self.__writeTagWithValue('color', constants[idx]['constant']['color'], format = 's')
+                self.indent -= 4
+                self.file.write('%s</plane>\n' % (' ' * self.indent))
+            self.indent -= 4
+            self.file.write('%s</constant_planes>\n' % (' ' * self.indent))
+        self.indent -= 4
+        
+        self.file.write('%s</nviz_state>\n' % (' ' * self.indent))
+        self.indent -= 4
+    
+    def __writeTagWithValue(self, tag, data, format = 'd'):
+        """!Helper function for writing pair tag
+        
+        @param tag written tag
+        @param data written data
+        @param format conversion type
+        """
+        self.file.write('%s<%s>\n' % (' ' * self.indent, tag))
+        self.indent += 4
+        self.file.write('%s' % (' ' * self.indent))
+        self.file.write(('<value>%' + format + '</value>\n') % data)
+        self.indent -= 4
+        self.file.write('%s</%s>\n' % (' ' * self.indent, tag))
+        
 class ProcessGrcFile(object):
     def __init__(self, filename):
         """!Process GRC file"""

Modified: grass/branches/develbranch_6/gui/wxpython/gui_modules/wxnviz.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/gui_modules/wxnviz.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/gui_modules/wxnviz.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -3,12 +3,14 @@
 
 @brief wxGUI 3D view mode
 
-This module implements 3D visualization mode for map display.
+This module implements 3D visualization mode for map display (ctypes
+required).
 
 List of classes:
  - Nviz
+ - Texture
 
-(C) 2008-2010 by the GRASS Development Team
+(C) 2008-2011 by the GRASS Development Team
 
 This program is free software under the GNU General Public
 License (>=v2). Read the file COPYING that comes with GRASS
@@ -16,21 +18,29 @@
 
 @author Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
 @author Pythonized by Glynn Clements
+ at author Anna Kratochvilova <KratochAnna seznam.cz> (Google SoC 2011)
 """
 
+import wx
 import sys
+import locale
+import struct
 from threading import Thread
+from math import sqrt
+from numpy import matrix
 
 from ctypes import *
 try:
-    from grass.lib.gis   import *
-    from grass.lib.g3d   import *
-    from grass.lib.ogsf  import *
-    from grass.lib.nviz  import *
+    from grass.lib.gis      import *
+    from grass.lib.g3d import *
+    from grass.lib.vector   import *
+    from grass.lib.ogsf     import *
+    from grass.lib.nviz     import *
 except ImportError, e:
     sys.stderr.write(_("3D view mode: %s\n") % e)
-
+    
 from debug import Debug
+import grass.script as grass
 
 log      = None
 progress = None
@@ -65,20 +75,24 @@
         """!Initialize Nviz class instance
         
         @param log logging area
+        @param gprogress progressbar
         """
         global errfunc, perfunc, log, progress
         log = glog
         progress = gprogress
         
-        G_gisinit("")
+        G_gisinit("wxnviz")
+        # gislib is already initialized (where?)
         G_set_error_routine(errfunc)
         G_set_percent_routine(perfunc)
         
-        GS_libinit()
-        GVL_libinit()
+        self.Init()
         
         self.data_obj = nv_data()
         self.data = pointer(self.data_obj)
+        self.color_obj = Colors()
+        self.color = pointer(self.color_obj)
+        
         self.width = self.height = -1
         self.showLight = False
         
@@ -91,7 +105,16 @@
         del self.data
         del self.data_obj
         self.log = None
-        
+
+    def Init(self):
+        """!Initialize window"""
+        locale.setlocale(locale.LC_NUMERIC, 'C')
+        #G_unset_window()
+        #Rast_unset_window()
+        #Rast__init_window()
+        GS_libinit()
+        GVL_libinit()
+    
     def ResizeWindow(self, width, height):
         """!GL canvas resized
         
@@ -107,6 +130,10 @@
                   width, height)
         return Nviz_resize_window(width, height)
     
+    def GetLongDim(self):
+        """!Get longest dimension, used for initial size of north arrow"""
+        return Nviz_get_longdim(self.data)
+    
     def SetViewDefault(self):
         """!Set default view (based on loaded data)
         
@@ -141,7 +168,62 @@
         
         Debug.msg(3, "Nviz::SetView(): x=%f, y=%f, height=%f, persp=%f, twist=%f",
                   x, y, height, persp, twist)
+                
+    def GetViewpointPosition(self):
+        x = c_double()
+        y = c_double()
+        h = c_double()
+        Nviz_get_viewpoint_height(byref(h))
+        Nviz_get_viewpoint_position(byref(x), byref(y))
         
+        return (x.value, y.value, h.value)
+        
+    def LookHere(self, x, y):
+        """!Look here feature 
+        @param x,y screen coordinates
+        """
+        
+        Nviz_look_here(x, y)
+        Debug.msg(3, "Nviz::LookHere(): x=%f, y=%f", x, y)
+    
+    def LookAtCenter(self):
+        """!Center view at center of displayed surface"""
+        Nviz_set_focus_map(MAP_OBJ_UNDEFINED, -1)
+        Debug.msg(3, "Nviz::LookAtCenter()")
+    
+    def GetFocus(self):
+        """!Get focus"""
+        Debug.msg(3, "Nviz::GetFocus()")
+        if Nviz_has_focus(self.data):
+            x = c_float()
+            y = c_float()
+            z = c_float()
+            Nviz_get_focus(self.data, byref(x), byref(y), byref(z))
+            return x.value, y.value, z.value
+        else:
+            return -1, -1, -1
+        
+    def SetFocus(self, x, y, z):
+        """!Set focus"""
+        Debug.msg(3, "Nviz::SetFocus()")
+        Nviz_set_focus(self.data, x, y, z)
+        
+    def GetViewdir(self):
+        """!Get viewdir"""
+        Debug.msg(3, "Nviz::GetViewdir()")
+        dir = (c_float * 3)()
+        GS_get_viewdir(byref(dir))
+        
+        return dir[0], dir[1], dir[2]
+        
+    def SetViewdir(self, x, y, z):
+        """!Set viewdir"""
+        Debug.msg(3, "Nviz::SetViewdir(): x=%f, y=%f, z=%f" % (x, y, z))
+        dir = (c_float * 3)()
+        for i, coord in enumerate((x, y, z)):
+            dir[i] = coord
+        GS_set_viewdir(byref(dir))
+                
     def SetZExag(self, z_exag):
         """!Set z-exag value
         
@@ -189,7 +271,7 @@
         # set background color
         Nviz_set_bgcolor(self.data, Nviz_color_from_str("white"))
         
-        GS_clear(Nviz_get_bgcolor(self.data))
+        GS_clear(Nviz_get_bgcolor(self.data))        
         # initialize view, lights
         Nviz_init_view(self.data)
         
@@ -265,7 +347,19 @@
         Debug.msg(1, "Nviz::LoadRaster(): name=%s -> id=%d", name, id)
         
         return id
-
+    
+    def AddConstant(self, value, color):
+        """!Add new constant surface"""
+        id = Nviz_new_map_obj(MAP_OBJ_SURF, None, value, self.data)
+        
+        Nviz_set_attr(id, MAP_OBJ_SURF, ATT_COLOR, CONST_ATT,
+                        None, Nviz_color_from_str(color),
+                        self.data)
+        Nviz_set_focus_map(MAP_OBJ_UNDEFINED, -1)
+        
+        Debug.msg(1, "Nviz::AddConstant(): id=%d", id)
+        return id
+        
     def UnloadSurface(self, id):
         """!Unload surface
         
@@ -290,16 +384,17 @@
         @param name vector map name
         @param points if true load 2d points rather then 2d lines
         
-        @return object id
+        @return object id, id of base surface (or -1 if it is not loaded)
         @return -1 on failure
         """
+        baseId = -1
         if GS_num_surfs() == 0:     # load base surface if no loaded
-            Nviz_new_map_obj(MAP_OBJ_SURF, None, 0.0, self.data)
+            baseId = Nviz_new_map_obj(MAP_OBJ_SURF, None, 0.0, self.data)
             
             nsurf = c_int()
             surf_list = GS_get_surf_list(byref(nsurf))
             GS_set_att_const(surf_list[0], ATT_TRANSP, 255)
-        
+            
         mapset = G_find_vector2 (name, "")
         if mapset is None:
             G_warning(_("Vector map <%s> not found"),
@@ -316,7 +411,7 @@
         
         Debug.msg(1, "Nviz::LoadVector(): name=%s -> id=%d", name, id)
         
-        return id
+        return id, baseId
     
     def UnloadVector(self, id, points):
         """!Unload vector set
@@ -342,6 +437,19 @@
         
         return 1
 
+    def VectorSurfaceSelected(self, vid, sid):
+        """!Check if surface is selected (currently unused)
+        
+        @param vid vector id
+        @param sid surface id
+        
+        @return True if selected
+        @return False if not selected
+        """
+        selected = GV_surf_is_selected(vid, sid)
+        Debug.msg(1, "Nviz::VectorSurfaceSelected(): vid=%s, sid=%d -> selected=%d", vid, sid, selected)
+        return selected
+    
     def LoadVolume(self, name, color_name, color_value):
         """!Load 3d raster map (volume)
         
@@ -423,7 +531,7 @@
         
         @param id surface id
         @param map if true use map otherwise constant
-        @param value map name of value
+        @param value map name or value
         
         @return 1 on success
         @return -1 surface not found
@@ -444,7 +552,7 @@
         @return -1 surface not found
         @return -2 setting attributes failed
         """
-        return self.SetSurfaceAttr(id, ATT_MASK, true, value)
+        return self.SetSurfaceAttr(id, ATT_MASK, True, value)
     
     def SetSurfaceTransp(self, id, map, value):
         """!Set surface mask
@@ -475,7 +583,7 @@
         return self.SetSurfaceAttr(id, ATT_SHINE, map, value)
     
     def SetSurfaceEmit(self, id, map, value):
-        """!Set surface emission
+        """!Set surface emission (currently unused)
         
         @param id surface id
         @param map if true use map otherwise constant
@@ -546,7 +654,7 @@
         return self.UnsetSurfaceAttr(id, ATT_TRANSP)
     
     def UnsetSurfaceEmit(self, id):
-        """!Unset surface emission
+        """!Unset surface emission (currently unused)
         
         @param id surface id
         
@@ -784,6 +892,28 @@
         
         return 1
 
+    def UnsetVectorLineSurface(self, id, surf_id):
+        """!Unset reference surface of vector set (lines)
+        
+        @param id vector set id
+        @param surf_id surface id
+        
+        @return 1 on success
+        @return -1 vector set not found
+        @return -2 surface not found
+        @return -3 on failure
+        """
+        if not GV_vect_exists(id):
+            return -1
+        
+        if not GS_surf_exists(surf_id):
+            return -2
+        
+        if GV_unselect_surf(id, surf_id) < 0:
+            return -3
+        
+        return 1
+        
     def SetVectorPointMode(self, id, color_str, width, size, marker):
         """!Set mode of vector point overlay
         
@@ -803,9 +933,8 @@
         
         color = Nviz_color_from_str(color_str)
         
-        ### TODO
-        # if GP_set_style(id, color, width, size, marker) < 0:
-        #    return -2
+        if GP_set_sitemode(id, ST_ATT_NONE, color, width, size, marker) < 0:
+            return -2
         
         return 1
 
@@ -850,7 +979,64 @@
         
         return 1
 
-    def AddIsosurface(self, id, level):
+    def ReadVectorColors(self, name, mapset):
+        """!Read vector colors
+        
+        @param name vector map name
+        @mapset mapset name ("" for search path)
+        
+        @return -1 on error 
+        @return 0 if color table missing 
+        @return 1 on success (color table found) 
+        """
+        return Vect_read_colors(name, mapset, self.color)
+        
+    def CheckColorTable(self, id, type):
+        """!Check if color table exists.
+        
+        @param id vector set id
+        @param type vector set type (lines/points)
+        
+        @return 1 color table exists
+        @return 0 no color table found
+        @return -1 on error
+        @return -2 vector set not found
+        """
+        file = c_char_p()
+        
+        if type == 'points':
+            ret = GP_get_sitename(id, byref(file))
+        elif type == 'lines':
+            ret = GV_get_vectname(id, byref(file))
+            
+        if ret < 0:
+            return -2
+        
+        return self.ReadVectorColors(file, "")
+        
+    def UnsetVectorPointSurface(self, id, surf_id):
+        """!Unset reference surface of vector set (points)
+        
+        @param id vector set id
+        @param surf_id surface id
+        
+        @return 1 on success
+        @return -1 vector set not found
+        @return -2 surface not found
+        @return -3 on failure
+        """
+        if not GP_site_exists(id):
+            return -1
+        
+        if not GS_surf_exists(surf_id):
+            return -2
+        
+        if GP_unselect_surf(id, surf_id) < 0:
+            return -3
+        
+        return 1
+        
+    def AddIsosurface(self, id, level, isosurf_id = None):
         """!Add new isosurface
         
         @param id volume id
@@ -862,6 +1048,11 @@
         if not GVL_vol_exists(id):
             return -1
         
+        if isosurf_id is not None:
+            num = GVL_isosurf_num_isosurfs(id)
+            if num < 0 or isosurf_id != num:
+                return -1
+                
         if GVL_isosurf_add(id) < 0:
             return -1
         
@@ -870,6 +1061,27 @@
         
         return GVL_isosurf_set_att_const(id, nisosurfs - 1, ATT_TOPO, level)
     
+    def AddSlice(self, id, slice_id = None):
+        """!Add new slice
+        
+        @param id volume id
+        
+        @return -1 on failure
+        @return number of slices
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if slice_id is not None:
+            num = GVL_slice_num_slices(id)
+            if num < 0 or slice_id != num:
+                return -1
+                
+        if GVL_slice_add(id) < 0:
+            return -1
+        
+        return GVL_slice_num_slices(id)
+    
     def DeleteIsosurface(self, id, isosurf_id):
         """!Delete isosurface
         
@@ -894,6 +1106,30 @@
 
         return 1
     
+    def DeleteSlice(self, id, slice_id):
+        """!Delete slice
+        
+        @param id volume id
+        @param slice_id slice id
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 slice not found
+        @return -3 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if slice_id > GVL_slice_num_slices(id):
+            return -2
+        
+        ret = GVL_slice_del(id, slice_id)
+        
+        if ret < 0:
+            return -3
+
+        return 1
+    
     def MoveIsosurface(self, id, isosurf_id, up):
         """!Move isosurface up/down in the list
         
@@ -922,6 +1158,49 @@
 
         return 1
 
+    def MoveSlice(self, id, slice_id, up):
+        """!Move slice up/down in the list
+        
+        @param id volume id
+        @param slice_id slice id
+        @param up if true move up otherwise down
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 slice not found
+        @return -3 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if slice_id > GVL_slice_num_slices(id):
+            return -2
+        
+        if up:
+            ret = GVL_slice_move_up(id, slice_id)
+        else:
+            ret = GVL_slice_move_down(id, slice_id)
+        
+        if ret < 0:
+            return -3
+
+        return 1
+    
+    def SetIsosurfaceTopo(self, id, isosurf_id, map, value):
+        """!Set isosurface level
+        
+        @param id volume id
+        @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
+        @param map if true use map otherwise constant
+        @param value map name of value
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        return self.SetIsosurfaceAttr(id, isosurf_id, ATT_TOPO, map, value)
+    
     def SetIsosurfaceColor(self, id, isosurf_id, map, value):
         """!Set isosurface color
         
@@ -952,7 +1231,7 @@
         @return -2 isosurface not found
         @return -3 on failure
         """
-        return self.SetIsosurfaceAttr(id, isosurf_id, ATT_MASK, true, value)
+        return self.SetIsosurfaceAttr(id, isosurf_id, ATT_MASK, True, value)
     
     def SetIsosurfaceTransp(self, id, isosurf_id, map, value):
         """!Set isosurface transparency
@@ -985,7 +1264,7 @@
         return self.SetIsosurfaceAttr(id, isosurf_id, ATT_SHINE, map, value)
     
     def SetIsosurfaceEmit(self, id, isosurf_id, map, value):
-        """!Set isosurface emission
+        """!Set isosurface emission (currently unused)
         
         @param id volume id
         @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
@@ -1030,7 +1309,7 @@
             ret = GVL_isosurf_set_att_const(id, isosurf_id, attr, val)
         
         Debug.msg(3, "Nviz::SetIsosurfaceAttr(): id=%d, isosurf=%d, "
-                  "attr=%d, map=%d, value=%s",
+                  "attr=%d, map=%s, value=%s",
                   id, isosurf_id, attr, map, value)
         
         if ret < 0:
@@ -1065,7 +1344,7 @@
         return self.UnsetIsosurfaceAttr(id, isosurf_id, ATT_TRANSP)
     
     def UnsetIsosurfaceEmit(self, id, isosurf_id):
-        """!Unset isosurface emission
+        """!Unset isosurface emission (currently unused)
         
         @param id volume id
         @param isosurf_id isosurface id (0 - MAX_ISOSURFS)
@@ -1124,6 +1403,25 @@
         
         return 1
     
+    def SetSliceMode(self, id, mode):
+        """!Set draw mode for slices
+        
+        @param mode
+        
+        @return 1 on success
+        @return -1 volume set not found
+        @return -2 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        ret = GVL_slice_set_drawmode(id, mode)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
     def SetIsosurfaceRes(self, id, res):
         """!Set draw resolution for isosurfaces
         
@@ -1142,7 +1440,228 @@
             return -2
         
         return 1
-
+    
+    def SetSliceRes(self, id, res):
+        """!Set draw resolution for slices
+        
+        @param res resolution value
+        
+        @return 1 on success
+        @return -1 volume set not found
+        @return -2 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        ret = GVL_slice_set_drawres(id, res, res, res)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
+    def SetSlicePosition(self, id, slice_id, x1, x2, y1, y2, z1, z2, dir):
+        """!Set slice position
+        
+        @param id volume id
+        @param slice_id slice id
+        @param x1,x2,y1,y2,z1,z2 slice coordinates
+        @param dir axis
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 slice not found
+        @return -3 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if slice_id > GVL_slice_num_slices(id):
+            return -2
+        
+        ret = GVL_slice_set_pos(id, slice_id, x1, x2, y1, y2, z1, z2, dir)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
+    def SetSliceTransp(self, id, slice_id, value):
+        """!Set slice transparency
+        
+        @param id volume id
+        @param slice_id slice id
+        @param x1,x2,y1,y2,z1,z2 slice coordinates
+        @param value transparency value (0 - 255)
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 slice not found
+        @return -3 on failure
+        """
+        
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if slice_id > GVL_slice_num_slices(id):
+            return -2
+        
+        ret = GVL_slice_set_transp(id, slice_id, value)
+        
+        if ret < 0:
+            return -2
+        
+        return 1
+    
+    def SetIsosurfaceInOut(self, id, isosurf_id, inout):
+        """!Set inout mode
+        
+        @param inout mode true/false
+        
+        @return 1 on success
+        @return -1 volume set not found
+        @return -2 isosurface not found
+        @return -3 on failure
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        if isosurf_id > GVL_isosurf_num_isosurfs(id) - 1:
+            return -2
+        
+        ret = GVL_isosurf_set_flags(id, isosurf_id, inout)
+        
+        if ret < 0:
+            return -3
+        
+        return 1
+    
+    def GetVolumePosition(self, id):
+        """!Get volume position
+        
+        @param id volume id
+        
+        @return x,y,z
+        @return zero-length vector on error
+        """
+        if not GVL_vol_exists(id):
+            return []
+        
+        x, y, z = c_float(), c_float(), c_float()
+        GVL_get_trans(id, byref(x), byref(y), byref(z))
+        
+        Debug.msg(3, "Nviz::GetVolumePosition(): id=%d, x=%f, y=%f, z=%f",
+                  id, x.value, y.value, z.value)
+        
+        return [x.value, y.value, z.value]
+    
+    def SetVolumePosition(self, id, x, y, z):
+        """!Set volume position
+        
+        @param id volume id
+        @param x,y,z translation values
+        
+        @return 1 on success
+        @return -1 volume not found
+        @return -2 setting position failed
+        """
+        if not GVL_vol_exists(id):
+            return -1
+        
+        Debug.msg(3, "Nviz::SetVolumePosition(): id=%d, x=%f, y=%f, z=%f",
+                  id, x, y, z)
+        
+        GVL_set_trans(id, x, y, z)
+        
+        return 1
+    
+    def GetCPlaneCurrent(self):
+        return Nviz_get_current_cplane(self.data)
+    
+    def GetCPlanesCount(self):
+        """!Returns number of cutting planes"""
+        return Nviz_num_cplanes(self.data) 
+    
+    def GetCPlaneRotation(self):
+        """!Returns rotation parameters of current cutting plane"""
+        x, y, z = c_float(), c_float(), c_float()
+        
+        current = Nviz_get_current_cplane(self.data)
+        Nviz_get_cplane_rotation(self.data, current, byref(x), byref(y), byref(z))
+        
+        return x.value, y.value, z.value
+    
+    def GetCPlaneTranslation(self):
+        """!Returns translation parameters of current cutting plane"""
+        x, y, z = c_float(), c_float(), c_float()
+        
+        current = Nviz_get_current_cplane(self.data)
+        Nviz_get_cplane_translation(self.data, current, byref(x), byref(y), byref(z))
+        
+        return x.value, y.value, z.value
+    
+    def SetCPlaneRotation(self, x, y, z):
+        """!Set current clip plane rotation
+        
+        @param x,y,z rotation parameters
+        """
+        current = Nviz_get_current_cplane(self.data)
+        Nviz_set_cplane_rotation(self.data, current, x, y, z)
+        Nviz_draw_cplane(self.data, -1, -1)
+    
+    def SetCPlaneTranslation(self, x, y, z):
+        """!Set current clip plane translation
+        
+        @param x,y,z translation parameters
+        """
+        current = Nviz_get_current_cplane(self.data)
+        Nviz_set_cplane_translation(self.data, current, x, y, z)
+        Nviz_draw_cplane(self.data, -1, -1) 
+        Debug.msg(3, "Nviz::SetCPlaneTranslation(): id=%d, x=%f, y=%f, z=%f",
+                  current, x, y, z)
+                
+    def SetCPlaneInteractively(self, x, y):
+        current = Nviz_get_current_cplane(self.data)
+        ret = Nviz_set_cplane_here(self.data, current, x, y)
+        if ret:
+            Nviz_draw_cplane(self.data, -1, -1)
+            x, y, z = self.GetCPlaneTranslation()
+            return x, y, z
+        else:
+            return None, None, None
+        
+        
+    def SelectCPlane(self, index):
+        """!Select cutting plane
+        
+        @param index index of cutting plane
+        """
+        Nviz_on_cplane(self.data, index)
+    
+    def UnselectCPlane(self, index):
+        """!Unselect cutting plane
+        
+        @param index index of cutting plane
+        """
+        Nviz_off_cplane(self.data, index)
+        
+    def SetFenceColor(self, index):
+        """!Select current cutting plane
+        
+        @param index type of fence - from 0 (off) to 4
+        """    
+        Nviz_set_fence_color(self.data, index)
+            
+    def GetXYRange(self):
+        """!Get xy range"""
+        return Nviz_get_xyrange(self.data)
+    
+    def GetZRange(self):
+        """!Get z range"""
+        min, max = c_float(), c_float()
+        Nviz_get_zrange(self.data, byref(min), byref(max))
+        return min.value, max.value
+    
     def SaveToFile(self, filename, width = 20, height = 20, itype = 'ppm'):
         """!Save current GL screen to ppm/tif file
 
@@ -1167,8 +1686,12 @@
     def DrawLightingModel(self):
         """!Draw lighting model"""
         if self.showLight:
-            GS_draw_lighting_model()
+            Nviz_draw_model(self.data)
 
+    def DrawFringe(self):
+        """!Draw fringe"""
+        Nviz_draw_fringe(self.data)
+        
     def SetFringe(self, sid, color, elev, nw = False, ne = False, sw = False, se = False):
         """!Set fringe
 
@@ -1178,11 +1701,49 @@
         @param nw,ne,sw,se fringe edges (turn on/off)
         """
         scolor = str(color[0]) + ':' + str(color[1]) + ':' + str(color[2])
-
         Nviz_set_fringe(self.data,
                         sid, Nviz_color_from_str(scolor),
                         elev, int(nw), int(ne), int(sw), int(se))
+    
+    def DrawArrow(self):
+        """!Draw north arrow
+        """
+        return Nviz_draw_arrow(self.data)
         
+    def SetArrow(self, sx, sy, size, color):
+        """!Set north arrow from canvas coordinates
+        
+        @param sx,sy canvas coordinates
+        @param size arrow length
+        @param color arrow color
+        """
+        return Nviz_set_arrow(self.data, sx, sy, size, Nviz_color_from_str(color))       
+        
+    def DeleteArrow(self):
+        """!Delete north arrow
+        """
+        Nviz_delete_arrow(self.data)
+    
+    def SetScalebar(self, id, sx, sy, size, color):
+        """!Set scale bar from canvas coordinates
+        
+        @param sx,sy canvas coordinates
+        @param id scale bar id
+        @param size scale bar length
+        @param color scale bar color
+        """
+        return Nviz_set_scalebar(self.data, id, sx, sy, size, Nviz_color_from_str(color))
+    
+    def DrawScalebar(self):
+        """!Draw scale bar
+        """
+        return Nviz_draw_scalebar(self.data)
+    
+    def DeleteScalebar(self, id):
+        """!Delete scalebar
+        """
+        Nviz_delete_scalebar(self.data, id)
+        
     def GetPointOnSurface(self, sx, sy):
         """!Get point on surface
 
@@ -1192,7 +1753,7 @@
         x   = c_float()
         y   = c_float()
         z   = c_float()
-        Debug.msg(5, "GLWindow.GetPointOnSurface(): sx=%d sy=%d" % (sx, sy))
+        Debug.msg(5, "Nviz::GetPointOnSurface(): sx=%d sy=%d" % (sx, sy))
         num = GS_get_selected_point_on_surface(sx, sy, byref(sid), byref(x), byref(y), byref(z))
         if num == 0:
             return (None, None, None, None)
@@ -1228,3 +1789,246 @@
                                   byref(d), int(useExag))
         
         return d.value
+
+    def GetRotationParameters(self, dx, dy):
+        """!Get rotation parameters (angle, x, y, z axes)
+        
+        @param dx,dy difference from previous mouse drag event
+        """
+        modelview = (c_double * 16)()
+        Nviz_get_modelview(byref(modelview))
+        
+        angle = sqrt(dx*dx+dy*dy)/float(self.width+1)*180.0
+        m = []
+        row = []
+        for i, item in enumerate(modelview):
+            row.append(item)
+            if (i+1) % 4 == 0:
+                m.append(row)
+                row = []
+        inv = matrix(m).I
+        ax, ay, az = dy, dx, 0.
+        x = inv[0,0]*ax + inv[1,0]*ay + inv[2,0]*az
+        y = inv[0,1]*ax + inv[1,1]*ay + inv[2,1]*az
+        z = inv[0,2]*ax + inv[1,2]*ay + inv[2,2]*az
+        
+        return angle, x, y, z 
+       
+    def Rotate(self, angle, x, y, z):
+        """!Set rotation parameters
+        Rotate scene (difference from current state).
+
+        @param angle angle
+        @param x,y,z axis coordinate
+        """
+        Nviz_set_rotation(angle, x, y, z)
+        
+    def UnsetRotation(self):
+        """!Stop rotating the scene"""
+        Nviz_unset_rotation()
+        
+    def ResetRotation(self):
+        """!Reset scene rotation"""
+        Nviz_init_rotation()
+        
+    def GetRotationMatrix(self):
+        """!Get rotation matrix"""
+        matrix = (c_double * 16)()
+        GS_get_rotation_matrix(byref(matrix))
+        returnMatrix = []
+        for item in matrix:
+            returnMatrix.append(item)
+        return returnMatrix
+        
+    def SetRotationMatrix(self, matrix):
+        """!Set rotation matrix"""
+        mtrx = (c_double * 16)()
+        for i in range(len(matrix)):
+            mtrx[i] = matrix[i]
+        GS_set_rotation_matrix(byref(mtrx))
+    
+    def Start2D(self):
+        Nviz_set_2D(self.width, self.height)
+        
+    def FlyThrough(self, flyInfo, mode, exagInfo):
+        """!Fly through the scene
+        
+        @param flyInfo fly parameters
+        @param mode 0 or 1 for different fly behaviour
+        @param exagInfo parameters changing fly speed
+        """
+        fly = (c_float * 3)()
+        for i, item in enumerate(flyInfo):
+            fly[i] = item
+        exag = (c_int * 2)()
+        exag[0] = int(exagInfo['move'])
+        exag[1] = int(exagInfo['turn'])
+        Nviz_flythrough(self.data, fly, exag, mode)
+        
+class Texture(object):
+    """!Class representing OpenGL texture"""
+    def __init__(self, filepath, overlayId, coords):
+        """!Load image to texture
+
+        @param filepath path to image file
+        @param overlayId id of overlay (1 for legend, 101 and more for text)
+        @param coords image coordinates
+        """
+        self.path = filepath
+        self.image = wx.Image(filepath, wx.BITMAP_TYPE_ANY)
+        self.width = self.image.GetWidth()
+        self.height = self.image.GetHeight()
+        self.id = overlayId
+        self.coords = list(coords)
+        self.bounds = wx.Rect()
+        self.active = True
+        
+        # alpha needs to be initialized
+        if not self.image.HasAlpha():
+            self.image.InitAlpha()
+    
+        # resize image to match 2^n
+        self.Resize()
+        
+        # check max texture size
+        maxSize = c_int()
+        Nviz_get_max_texture(byref(maxSize))
+        self.maxSize = maxSize.value
+        if self.maxSize < self.width or self.maxSize < self.height:
+            # TODO: split up image 
+            self.textureId = None
+        else:
+            self.textureId = self.Load()
+            
+    def __del__(self):
+        """!Delete texture"""
+        if self.textureId:
+            Nviz_del_texture(self.textureId)
+        grass.try_remove(self.path)
+            
+    def Resize(self):    
+        """!Resize image to match 2^n"""
+        n = m = 1
+        while self.width > pow(2,n):
+            n += 1
+        while self.height > pow(2,m):
+            m += 1
+        self.image.Resize(size = (pow(2,n), pow(2,m)), pos = (0, 0))
+        self.width = self.image.GetWidth()
+        self.height = self.image.GetHeight()
+        
+    def Load(self):
+        """!Load image to texture"""  
+        if self.image.HasAlpha():
+            bytesPerPixel = 4
+        else:
+            bytesPerPixel = 3
+        bytes = bytesPerPixel * self.width * self.height
+        rev_val = self.height - 1
+        im = (c_ubyte * bytes)()
+        bytes3 = 3 * self.width * self.height
+        bytes1 = self.width * self.height
+        imageData = struct.unpack(str(bytes3) + 'B', self.image.GetData())
+        if self.image.HasAlpha():
+            alphaData = struct.unpack(str(bytes1) + 'B', self.image.GetAlphaData())
+        
+        # this takes too much time
+        wx.BeginBusyCursor()
+        for i in range(self.height):
+            for j in range(self.width):
+                im[(j + i * self.width) * bytesPerPixel + 0] = imageData[( j + (rev_val - i) * self.width) * 3 + 0]
+                im[(j + i * self.width) * bytesPerPixel + 1] = imageData[( j + (rev_val - i) * self.width) * 3 + 1]
+                im[(j + i * self.width) * bytesPerPixel + 2] = imageData[( j + (rev_val - i) * self.width) * 3 + 2]
+                if self.image.HasAlpha():
+                    im[(j + i * self.width) * bytesPerPixel + 3] = alphaData[( j + (rev_val - i) * self.width)]
+        wx.EndBusyCursor()
+        
+        id = Nviz_load_image(im, self.width, self.height, self.image.HasAlpha())
+        
+        return id
+        
+    def Draw(self):
+        """!Draw texture as an image"""
+        Nviz_draw_image(self.coords[0], self.coords[1], self.width, self.height, self.textureId)
+    
+        
+    def SetBounds(self, rect):
+        """!Set Bounding Rectangle"""
+        self.bounds = rect
+        
+    def HitTest(self, x, y, radius):
+        copy = wx.Rect(*self.bounds)
+        copy.Inflate(radius, radius)
+        return copy.ContainsXY(x, y)
+    
+    def MoveTexture(self, dx, dy):
+        """!Move texture on the screen"""
+        self.coords[0] += dx
+        self.coords[1] += dy
+        self.bounds.OffsetXY(dx, dy)
+    
+    def SetCoords(self, coords):
+        """!Set coordinates"""
+        dx = coords[0] - self.coords[0]
+        dy = coords[1] - self.coords[1]
+        self.MoveTexture(dx, dy)
+        
+    def GetId(self):
+        """!Returns image id."""
+        return self.id
+    
+    def SetActive(self, active = True):
+        self.active = active
+        
+    def IsActive(self):
+        return self.active
+        
+class ImageTexture(Texture):
+    """!Class representing OpenGL texture as an overlay image"""
+    def __init__(self, filepath, overlayId, coords, cmd):
+        """!Load image to texture
+
+        @param filepath path to image file
+        @param overlayId id of overlay (1 for legend)
+        @param coords image coordinates
+        @param cmd d.legend command      
+        """
+        Texture.__init__(self, filepath = filepath, overlayId = overlayId, coords = coords)
+        
+        self.cmd = cmd
+        
+    def GetCmd(self):
+        """!Returns overlay command."""
+        return self.cmd
+        
+    def Corresponds(self, item):
+        return sorted(self.GetCmd()) == sorted(item.GetCmd())
+        
+class TextTexture(Texture):
+    """!Class representing OpenGL texture as a text label"""
+    def __init__(self, filepath, overlayId, coords, textDict):
+        """!Load image to texture
+
+        @param filepath path to image file
+        @param overlayId id of overlay (101 and more for text)
+        @param coords text coordinates
+        @param textDict text properties      
+        """
+        Texture.__init__(self, filepath = filepath, overlayId = overlayId, coords = coords)
+        
+        self.textDict = textDict
+        
+    def GetTextDict(self):
+        """!Returns text properties."""
+        return self.textDict
+        
+        
+    def Corresponds(self, item):
+        t = self.GetTextDict()
+        for prop in t.keys():
+            if prop in ('coords','bbox'): continue
+            if t[prop] != item[prop]:
+                return False
+                
+        return True
+    

Modified: grass/branches/develbranch_6/gui/wxpython/icons/icon.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/icons/icon.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/icons/icon.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -16,7 +16,7 @@
 for details.
 
 @author Martin Landa <landa.martin gmail.com>
- at author Anna Kratochvilova <anna.kratochvilova fsv.cvut.cz>
+ at author Anna Kratochvilova <kratochanna gmail.com>
 """
 
 import os
@@ -364,30 +364,27 @@
                                desc = _('Settings dialog for georectification tool')),
         },
     'nviz' : {
-        'view'    : MetaIcon(img = iconSet.get('3d-view', wx.ART_ERROR),
-                             label = _('Switch to view control page'),
-                             desc = _('Change view settings')),
-        'surface' : MetaIcon(img = iconSet.get('3d-raster', wx.ART_ERROR),
-                             label = _('Switch to surface (raster) control page'),
-                             desc = _('Change surface (loaded raster maps) settings')),
-        'vector'  : MetaIcon(img = iconSet.get('3d-vector', wx.ART_ERROR),
-                             label = _('Switch to vector (2D/3D) control page'),
-                             desc = _('Change 2D/3D vector settings')),
-        'volume'  : MetaIcon(img = iconSet.get('3d-volume', wx.ART_ERROR),
-                             label = _('Switch to volume (3D raster) control page'),
-                             desc = _('Change volume (loaded 3D raster maps) settings')),
-        'light'   : MetaIcon(img = iconSet.get('3d-light', wx.ART_ERROR),
-                             label = _('Switch to lighting control page'),
-                             desc = _('Change lighting settings')),
-        'fringe'  : MetaIcon(img = iconSet.get('3d-fringe', wx.ART_ERROR),
-                             label = _('Switch to fringe control page'),
-                             desc = _('Switch on/off fringes')),
-        'settings': MetaIcon(img = iconSet.get('settings', wx.ART_ERROR),
-                             label = _('3D view mode tools'),
-                             desc = _('Show/hide 3D view mode settings dialog')),
-        'quit'    : MetaIcon(img = iconSet.get('quit', wx.ART_ERROR),
-                             label = _('Quit 3D view mode'),
-                             desc = _('Switch back to 2D view mode')),
+        'rotate':   MetaIcon(img = iconSet.get('3d-rotate', wx.ART_ERROR),
+                             label = _('Rotate 3D scene'),
+                             desc = _('Drag with mouse to rotate 3D scene')), 
+        'flyThrough':   MetaIcon(img = iconSet.get('', wx.ART_MISSING_IMAGE),
+                             label = _('Fly-through mode'),
+                             desc = _('Drag with mouse, hold Ctrl down for different mode'
+                                      ' or Shift to accelerate')),
+        'zoomIn':   MetaIcon(img = iconSet.get('zoom-in', wx.ART_ERROR),
+                            label = _('Zoom in'),
+                            desc = _('Click mouse to zoom')),
+        'zoomOut':  MetaIcon(img = iconSet.get('zoom-out', wx.ART_ERROR),
+                            label = _('Zoom out'),
+                            desc = _('Click mouse to unzoom')),
+        'nvizCmd': MetaIcon(img = iconSet.get('script-save', wx.ART_ERROR),
+                            label = _('Generate command for m.nviz.image'),
+                            desc = _('Generate command for m.nviz.image based on current state')),
+        'settings': MetaIcon(img = iconSet.get('3d-settings', wx.ART_ERROR),
+                             label = _('3D view mode settings'),
+                             desc = _('Show 3D view mode settings dialog')),
+        'help'    : MetaIcon(img = iconSet.get('3d-help', wx.ART_ERROR),
+                             label = _('Show 3D view mode manual')),
         },
     'modeler' : {
         'new'        : MetaIcon(img = iconSet.get('create', wx.ART_ERROR),

Modified: grass/branches/develbranch_6/gui/wxpython/wxgui.py
===================================================================
--- grass/branches/develbranch_6/gui/wxpython/wxgui.py	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/gui/wxpython/wxgui.py	2011-11-25 15:10:47 UTC (rev 49357)
@@ -77,7 +77,8 @@
 from gui_modules import nviz_tools
 from gui_modules.debug    import Debug
 from gui_modules.ghelp    import MenuTreeWindow, AboutWindow, InstallExtensionWindow
-from gui_modules.toolbars import LMWorkspaceToolbar, LMDataToolbar, LMToolsToolbar, LMMiscToolbar, LMVectorToolbar
+from gui_modules.toolbars import LMWorkspaceToolbar, LMDataToolbar, LMToolsToolbar,\
+                                 LMMiscToolbar, LMVectorToolbar, LMNvizToolbar
 from gui_modules.gpyshell import PyShellWindow
 from icons.icon           import Icons
 
@@ -113,6 +114,7 @@
         self.workspaceChanged = False     # track changes in workspace
         self.georectifying = None         # reference to GCP class or None
         self.gcpmanagement = None         # reference to GCP class or None
+        
         # list of open dialogs
         self.dialogs        = dict()
         self.dialogs['preferences'] = None
@@ -126,7 +128,8 @@
                            'data'      : LMDataToolbar(parent = self),
                            'tools'     : LMToolsToolbar(parent = self),
                            'misc'      : LMMiscToolbar(parent = self),
-                           'vector'    : LMVectorToolbar(parent = self) }
+                           'vector'    : LMVectorToolbar(parent = self),
+                           'nviz'      : LMNvizToolbar(parent = self)}
         
         self._toolbarsData = { 'workspace' : ("toolbarWorkspace",     # name
                                               _("Workspace Toolbar"), # caption
@@ -143,13 +146,16 @@
                                'vector'    : ("toolbarVector",
                                               _("Vector Toolbar"),
                                               2),
+                               'nviz'      : ("toolbarNviz",
+                                              _("3D view Toolbar"),
+                                              2),                                            
                                }
         if sys.platform == 'win32':
             self._toolbarsList = ('workspace', 'data',
-                                  'vector', 'tools', 'misc')
+                                  'vector', 'tools', 'misc', 'nviz')
         else:
             self._toolbarsList = ('data', 'workspace',
-                                  'misc', 'tools', 'vector')
+                                  'nviz', 'misc', 'tools', 'vector')
         for toolbar in self._toolbarsList:
             name, caption, row = self._toolbarsData[toolbar]
             self._auimgr.AddPane(self.toolbars[toolbar],
@@ -161,6 +167,7 @@
                                  CloseButton(False).Layer(2).
                                  BestSize((self.toolbars[toolbar].GetBestSize())))
         
+        self._auimgr.GetPane('toolbarNviz').Hide()
         # bindings
         self.Bind(wx.EVT_CLOSE,    self.OnCloseWindow)
         self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
@@ -231,7 +238,13 @@
     def _setCopyingOfSelectedText(self):
         copy = UserSettings.Get(group = 'manager', key = 'copySelectedTextToClipboard', subkey = 'enabled')
         self.goutput.SetCopyingOfSelectedText(copy)
-        
+    
+    def IsPaneShown(self, name):
+        """!Check if pane (toolbar, ...) of given name is currently shown"""
+        if self._auimgr.GetPane(name).IsOk():
+            return self._auimgr.GetPane(name).IsShown()
+        return False
+    
     def _createNoteBook(self):
         """!Creates notebook widgets"""
         self.notebook = menuform.GNotebook(parent = self, style = globalvar.FNPageDStyle)
@@ -270,20 +283,36 @@
         
         return self.notebook
             
-    def AddNviz(self):
+    def AddNvizTools(self):
         """!Add nviz notebook page"""
+        Debug.msg(5, "GMFrame.AddNvizTools()")
+        # show toolbar
+        self._auimgr.GetPane('toolbarNviz').Show()
+        # reorder other toolbars
+        for pos, toolbar in enumerate(('toolbarVector', 'toolbarTools', 'toolbarMisc','toolbarNviz')):
+            self._auimgr.GetPane(toolbar).Row(2).Position(pos)
+        self._auimgr.Update()
+        
+        # create nviz tools tab
         self.nviz = nviz_tools.NvizToolWindow(parent = self,
-                                              display = self.curr_page.maptree.GetMapDisplay()) 
-        self.notebook.AddPage(page = self.nviz, text = _("3D view"), name = 'nviz')
+                                              display = self.curr_page.maptree.GetMapDisplay())
+        idx = self.notebook.GetPageIndexByName('layers')
+        self.notebook.InsertPage(indx = idx + 1, page = self.nviz, text = _("3D view"), name = 'nviz')
         self.notebook.SetSelectionByName('nviz')
         
-    def RemoveNviz(self):
+        
+    def RemoveNvizTools(self):
         """!Remove nviz notebook page"""
-        # print self.notebook.GetPage(1)
+        # if more mapwindow3D were possible, check here if nb page should be removed
+        self.notebook.SetSelectionByName('layers')
         self.notebook.RemovePage(self.notebook.GetPageIndexByName('nviz'))
         del self.nviz
-        self.notebook.SetSelectionByName('layers')
-        
+        # hide toolbar
+        self._auimgr.GetPane('toolbarNviz').Hide()
+        for pos, toolbar in enumerate(('toolbarVector', 'toolbarTools', 'toolbarMisc')):
+            self._auimgr.GetPane(toolbar).Row(2).Position(pos)
+        self._auimgr.Update()
+    
     def WorkspaceChanged(self):
         """!Update window title"""
         if not self.workspaceChanged:
@@ -745,7 +774,7 @@
             return
 
         Debug.msg(4, "GMFrame.OnWorkspaceOpen(): filename=%s" % filename)
-
+        
         # delete current layer tree content
         self.OnWorkspaceClose()
         
@@ -869,9 +898,23 @@
             # reverse list of map layers
             maptree.Map.ReverseListOfLayers()
             
-        for mdisp in mapdisplay:
+        for idx, mdisp in enumerate(mapdisplay):
             mdisp.MapWindow2D.UpdateMap()
+            #nviz
+            if gxwXml.displays[idx]['viewMode'] == '3d':
+                mdisp.AddNviz()
+                self.nviz.UpdateState(view = gxwXml.nviz_state['view'],
+                                              iview = gxwXml.nviz_state['iview'],
+                                              light = gxwXml.nviz_state['light'])
+                mdisp.MapWindow3D.constants = gxwXml.nviz_state['constants']
+                for idx, constant in enumerate(mdisp.MapWindow3D.constants):
+                    mdisp.MapWindow3D.AddConstant(constant, idx + 1)
+                for page in ('view', 'light', 'fringe', 'constant', 'cplane'):
+                    self.nviz.UpdatePage(page)
+                self.nviz.UpdateSettings()
+                mapdisp.toolbars['map'].combo.SetSelection(1)
 
+
         return True
     
     def OnWorkspaceLoadGrcFile(self, event):
@@ -1277,6 +1320,23 @@
         # show ATM window
         dbmanager.Show()
         
+    def OnNewDisplayWMS(self, event = None):
+        """!Create new layer tree and map display instance"""
+        self.NewDisplayWMS()
+
+    def NewDisplayWMS(self, show = True):
+        Debug.msg(1, "GMFrame.NewDisplay(): idx=%d" % self.disp_idx)
+        try:
+            from gui_modules.wms.wmsmenu import DisplayWMSMenu
+        except:
+            gcmd.GError(parent = self.parent,
+                        message = _("Experimental WMS support for wxGUI not available. "
+                                    "You can install it by '%s'") % \
+                            "g.extension -s extension=wx.wms")
+            return
+        
+        DisplayWMSMenu()
+    
     def OnNewDisplay(self, event = None):
         """!Create new layer tree and map display instance"""
         self.NewDisplay()

Modified: grass/branches/develbranch_6/include/gstypes.h
===================================================================
--- grass/branches/develbranch_6/include/gstypes.h	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/include/gstypes.h	2011-11-25 15:10:47 UTC (rev 49357)
@@ -326,12 +326,21 @@
     float shine;		/* 0. to 128. */
 };
 
+struct georot 
+{ 
+    int do_rot;                 /* do rotation */ 
+    double rot_angle;           /* rotation angle */ 
+    double rot_axes[3];         /* rotation axis */ 
+    GLdouble rotMatrix[16];     /* rotation matrix */ 
+};
+    
 typedef struct
 {
     int coord_sys;		/* latlon, equal area, etc */
     int view_proj;		/* perspective, ortho */
     int infocus;		/* fixed center of view - true or false */
     float from_to[2][4];
+    struct georot rotate;
     int twist, fov, incl, look;	/* 10ths of degrees */
     float real_to[4], vert_exag;	/* a global Z exag */
     float scale;

Modified: grass/branches/develbranch_6/include/nviz.h
===================================================================
--- grass/branches/develbranch_6/include/nviz.h	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/include/nviz.h	2011-11-25 15:10:47 UTC (rev 49357)
@@ -84,6 +84,21 @@
     int           where[4];
 };
 
+struct arrow_data
+{
+    unsigned long color;
+    float	  size;
+    float	  where[3];
+};
+
+struct scalebar_data
+{
+    int           id;
+    unsigned long color;
+    float	  size;
+    float	  where[3];
+};
+
 typedef struct
 {
     /* ranges */
@@ -101,7 +116,15 @@
     /* fringe */
     int num_fringes;
     struct fringe_data **fringe;
+
+    /* north arrow */
+    int draw_arrow;
+    struct arrow_data *arrow;
     
+    /* scalebar */
+    int num_scalebars;
+    struct scalebar_data **scalebar;
+    
     /* background color */
     int bgcolor;
 
@@ -129,16 +152,34 @@
 int Nviz_resize_window(int, int);
 int Nviz_update_ranges(nv_data *);
 int Nviz_set_viewpoint_position(double, double);
+void Nviz_get_viewpoint_position(double *, double *);
 int Nviz_set_viewpoint_height(double);
+void Nviz_get_viewpoint_height(double *);
 int Nviz_set_viewpoint_persp(int);
 int Nviz_set_viewpoint_twist(int);
 int Nviz_change_exag(nv_data *, double);
+int Nviz_look_here(double, double);
+void Nviz_get_modelview(double *);
+void Nviz_set_rotation(double, double, double, double);
+void Nviz_unset_rotation(void);
+void Nviz_init_rotation(void);
+void Nviz_flythrough(nv_data *, float *, int *, int);
 
 /* cplanes_obj.c */
 int Nviz_new_cplane(nv_data *, int);
+int Nviz_on_cplane(nv_data *, int);
 int Nviz_off_cplane(nv_data *, int);
 int Nviz_draw_cplane(nv_data *, int, int);
+int Nviz_num_cplanes(nv_data *);
+int Nviz_get_current_cplane(nv_data *);
+int Nviz_set_cplane_rotation(nv_data *, int, float, float, float);
+int Nviz_get_cplane_rotation(nv_data *, int, float *, float *, float *);
+int Nviz_set_cplane_translation(nv_data *, int, float, float, float);
+int Nviz_get_cplane_translation(nv_data *, int, float *, float *, float *);
+int Nviz_set_fence_color(nv_data *, int);
+int Nviz_set_cplane_here(nv_data *, int, float, float);
 
+
 /* draw.c */
 int Nviz_draw_all_surf(nv_data *);
 int Nviz_draw_all_vect();
@@ -146,6 +187,11 @@
 int Nviz_draw_all_vol();
 int Nviz_draw_all(nv_data *);
 int Nviz_draw_quick(nv_data *, int);
+int Nviz_load_image(GLubyte *, int, int, int);
+void Nviz_draw_image(int, int, int, int, int);
+void Nviz_set_2D(int, int);
+void Nviz_del_texture(int);
+void Nviz_get_max_texture(int *);
 
 /* exag.c */
 int Nviz_get_exag_height(double *, double *, double *);
@@ -158,6 +204,7 @@
 int Nviz_set_light_ambient(nv_data *, int, double);
 int Nviz_init_light(nv_data *, int);
 int Nviz_new_light(nv_data *);
+void Nviz_draw_model(nv_data *);
 
 /* map_obj.c */
 int Nviz_new_map_obj(int, const char *, double, nv_data *);
@@ -177,11 +224,25 @@
 				    double, int, int, int, int);
 struct fringe_data *Nviz_set_fringe(nv_data *, int, unsigned long,
 				    double, int, int, int, int);
+void Nviz_draw_fringe(nv_data *data);
+int Nviz_draw_arrow(nv_data *);
+int Nviz_set_arrow(nv_data *, int, int, float, unsigned int);
+void Nviz_delete_arrow(nv_data *);
+struct scalebar_data * Nviz_new_scalebar(nv_data *, int, float *, float, unsigned int);
+struct scalebar_data * Nviz_set_scalebar(nv_data *, int , int, int, float, unsigned int);
+void Nviz_draw_scalebar(nv_data *);
+void Nviz_delete_scalebar(nv_data *, int);
 
 /* position.c */
 void Nviz_init_view(nv_data *);
 int Nviz_set_focus_state(int);
 int Nviz_set_focus_map(int, int);
+int Nviz_has_focus(nv_data *);
+int Nviz_set_focus(nv_data *, float, float, float);
+int Nviz_get_focus(nv_data *, float *, float *, float *);
+float Nviz_get_xyrange(nv_data *);
+int Nviz_get_zrange(nv_data *, float *, float *);
+float Nviz_get_longdim(nv_data *);
 
 /* render.c */
 struct render_window *Nviz_new_render_window();

Modified: grass/branches/develbranch_6/include/ogsf_proto.h
===================================================================
--- grass/branches/develbranch_6/include/ogsf_proto.h	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/include/ogsf_proto.h	2011-11-25 15:10:47 UTC (rev 49357)
@@ -86,6 +86,8 @@
 int GS_surf_exists(int);
 int GS_new_surface(void);
 int GS_new_light(void);
+void GS_set_light_reset(int);
+int GS_get_light_reset(void);
 void GS_setlight_position(int, float, float, float, int);
 void GS_setlight_color(int, float, float, float);
 void GS_setlight_ambient(int, float, float, float);
@@ -166,6 +168,11 @@
 void GS_get_viewdir(float *);
 void GS_set_viewdir(float *);
 void GS_set_fov(int);
+void GS_set_rotation(double, double, double, double);
+void GS_get_rotation_matrix(double *);
+void GS_set_rotation_matrix(double *);
+void GS_init_rotation(void); 
+void GS_unset_rotation(void); 
 int GS_get_fov(void);
 int GS_get_twist(void);
 void GS_set_twist(int);
@@ -583,6 +590,7 @@
 void gsd_real2model(Point3);
 void gsd_model2real(Point3);
 void gsd_model2surf(geosurf *, Point3);
+void gsd_surf2model(Point3);
 void gsd_surf2real(geosurf *, Point3);
 void gsd_real2surf(geosurf *, Point3);
 

Modified: grass/branches/develbranch_6/lib/nviz/change_view.c
===================================================================
--- grass/branches/develbranch_6/lib/nviz/change_view.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/lib/nviz/change_view.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -12,6 +12,8 @@
    \author Updated/modified by Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
  */
 
+#include <math.h>
+
 #include <grass/glocale.h>
 #include <grass/nviz.h>
 
@@ -125,6 +127,25 @@
     return 1;
 }
 
+void Nviz_get_viewpoint_position(double *x_pos, double *y_pos)
+{
+    float from[3];
+    double xpos, ypos;
+
+    GS_get_from(from);
+    xpos = (from[X] + RANGE_OFFSET) / RANGE;
+    ypos = (from[Y] + RANGE_OFFSET) / RANGE;
+    *x_pos = xpos;
+    *x_pos = (*x_pos < 0) ? 0 : (*x_pos > 1.0) ? 1.0 : *x_pos;
+    *y_pos = 1.0 - ypos;
+    *y_pos = (*y_pos < 0) ? 0 : (*y_pos > 1.0) ? 1.0 : *y_pos;
+
+    if (xpos < 0.0 || xpos > 1.0 || ypos < 0.0 || ypos > 1.0) {
+	G_debug(3, "Invalid view position coordinates, using %f,%f",
+		  *x_pos, 1.0 - *y_pos);
+    }
+}
+
 /*!
    \brief Change viewpoint height
 
@@ -157,6 +178,16 @@
     return 1;
 }
 
+void Nviz_get_viewpoint_height(double *height)
+{
+    float from[3];
+
+    G_debug(1, "Nviz_get_viewpoint_height():");
+
+    GS_get_from_real(from);
+
+    *height = from[Z];
+}
 /*!
    \brief Change viewpoint perspective (field of view)
 
@@ -221,3 +252,127 @@
 
     return 1;
 }
+/*!
+  \brief Change focused point
+  
+  \param sx,sy screen coordinates
+  
+  \return 1
+*/
+int Nviz_look_here(double sx, double sy)
+{
+     G_debug(1, "Nviz_look_here(): screen coordinates = %f %f", sx, sy); 
+     GS_look_here(sx, sy);
+     return 1;
+}
+
+/*!
+  \brief Get current modelview matrix
+*/
+void Nviz_get_modelview(double *modelMatrix)
+{
+    glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
+}
+
+/*!
+  \brief Set rotation parameters
+
+  Rotate scene by given parameters related to mouse drag event
+  (difference from current state).
+  Coordinates determine the second point of rotation axis,
+  the first point is (0, 0, 0).
+
+  \param angle angle
+  \param x,y,z axis coordinate
+*/
+void Nviz_set_rotation(double angle, double x, double y, double z)
+{
+    G_debug(3, "Nviz_set_rotation(): angle = %f, x = %f, y = %f, z = %f", angle, x, y, z); 
+    GS_set_rotation(angle, x, y, z);
+}
+
+/*!
+  \brief Stop scene rotation
+*/
+void Nviz_unset_rotation(void)
+{
+    GS_unset_rotation();
+}
+
+/*!
+  \brief Stop scene rotation
+*/
+void Nviz_init_rotation(void)
+{
+    GS_init_rotation();
+}
+
+/*!
+  \brief Fly through the scene
+
+  Computes parameters needed for moving scene.
+  Changes viewpoint and viewdir.
+  Based on visualization/nviz/src/togl_flythrough.c and simplified.
+
+  \param fly_info values computed from mouse movement
+  \param scale rate of movement
+  \param lateral type of movement
+  
+*/
+void Nviz_flythrough(nv_data *data, float *fly_info, int *scale, int lateral)
+{
+    float dir[3], from[4], cur_from[4], cur_dir[4];
+    float speed, h, p, sh, ch, sp, cp;
+    float diff_x, diff_y, diff_z;
+    float quasi_zero;
+
+    quasi_zero = 0.0001;
+
+    GS_get_from(cur_from);
+    GS_get_viewdir(cur_dir);
+
+    p = asin(cur_dir[Z]);
+    h = atan2(- cur_dir[X], - cur_dir[Y]);
+
+    speed = scale[0] * fly_info[0];
+
+    h += scale[1] * fly_info[1]; /* change heading */
+    if (!lateral)   /* in case of "lateral" doesn't change pitch */
+        p -= scale[1] * fly_info[2];
+
+    h = fmod(h + M_PI, 2 * M_PI) - M_PI;
+
+    sh = sin(h);
+    ch = cos(h);
+    sp = sin(p);
+    cp = cos(p);
+
+    dir[X] = -sh * cp;
+    dir[Y] = -ch * cp;
+    dir[Z] = sp;
+
+    if (lateral) {
+        from[X] = cur_from[X] + speed * dir[Y];
+        from[Y] = cur_from[Y] - speed * dir[X];
+        from[Z] = cur_from[Z] + scale[0] * fly_info[2];
+    }
+    else {
+        from[X] = cur_from[X] + speed * dir[X];
+        from[Y] = cur_from[Y] + speed * dir[Y];
+        /* not sure how this should behave (change Z coord or not ?)*/
+        from[Z] = cur_from[Z]; /* + speed * dir[Z]*/
+    }
+
+    diff_x = fabs(cur_dir[X] - dir[X]);
+    diff_y = fabs(cur_dir[Y] - dir[Y]);
+    diff_z = fabs(cur_dir[Z] - dir[Z]);
+
+    if (    /* something has changed */
+        (diff_x > quasi_zero) || (diff_y > quasi_zero) ||
+        (diff_z > quasi_zero) || (cur_from[X] != from[X]) ||
+        (cur_from[Y] != from[Y]) || (cur_from[Z] != from[Z])
+    ) {
+    GS_moveto(from);
+    GS_set_viewdir(dir);	/* calls gsd_set_view */
+    }
+}

Modified: grass/branches/develbranch_6/lib/nviz/cplanes_obj.c
===================================================================
--- grass/branches/develbranch_6/lib/nviz/cplanes_obj.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/lib/nviz/cplanes_obj.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -15,6 +15,7 @@
 #include <grass/nviz.h>
 
 static void cp_draw(nv_data *, int, int, int);
+static geoview Gv;
 
 /*!
    \brief Creates a clip plane object
@@ -36,6 +37,21 @@
 }
 
 /*!
+   \brief Turn on (make current) the given clip plane.
+
+   \param data nviz data
+   \param cplane id
+ */
+int Nviz_on_cplane(nv_data * data, int id)
+{
+    data->cur_cplane = id;
+    data->cp_on[id] = 1;
+    GS_set_cplane(id);
+
+    return 1;
+}
+
+/*!
    \brief Turn off (make inactive) the given clip plane
 
    \param data nviz data
@@ -118,3 +134,153 @@
 
     return;
 }
+/*!
+   \brief Return the number of clip planes objects currently allocated.
+   
+   \param data nviz data
+ */
+int Nviz_num_cplanes(nv_data * data)
+{
+	return data->num_cplanes;
+}
+
+/*!
+   \brief Get the current active cutplane.
+   
+   \param data nviz data
+ */
+int Nviz_get_current_cplane(nv_data * data)
+{
+	return data->cur_cplane;
+}
+
+/*!
+   \brief Set the rotation for the current clip plane.
+   
+   \param data nviz data
+   \param id id of current clip plane
+   \param dx,dy,dz rotation parameters
+   
+   \return 1 
+
+ */
+int Nviz_set_cplane_rotation(nv_data * data, int id, float dx, float dy, float dz)
+{
+    data->cp_rot[id][X] = dx;
+    data->cp_rot[id][Y] = dy;
+    data->cp_rot[id][Z] = dz;
+    GS_set_cplane_rot(id, data->cp_rot[id][X], data->cp_rot[id][Y],
+		      data->cp_rot[id][Z]);
+
+    cp_draw(data, data->cur_cplane, -1, -1);
+    
+    return 1;
+}
+/*!
+   \brief Get the rotation values for the current clip plane.
+   
+   \param data nviz data
+   \param id id of current clip plane
+   \param dx,dy,dz rotation parameters
+   
+   \return 1
+   
+ */
+int Nviz_get_cplane_rotation(nv_data * data, int id, float *dx, float *dy, float *dz)
+{
+    *dx = data->cp_rot[id][X];
+    *dy = data->cp_rot[id][Y];
+    *dz = data->cp_rot[id][Z];
+
+    return 1;
+}
+
+/*!
+   \brief Set the translation for the current clip plane.
+
+   \param data nviz data
+   \param id id of current clip plane
+   \param dx,dy,dz values for setting translation
+   
+   \return 1
+ */
+int Nviz_set_cplane_translation(nv_data * data, int id, float dx, float dy, float dz)
+{
+    data->cp_trans[id][X] = dx;
+    data->cp_trans[id][Y] = dy;
+    data->cp_trans[id][Z] = dz;
+    GS_set_cplane_trans(id, data->cp_trans[id][X], data->cp_trans[id][Y],
+			data->cp_trans[id][Z]);
+
+    cp_draw(data, data->cur_cplane, -1, -1);
+    
+    return 1;
+}
+/*!
+   \brief Get the translation values for the current clip plane.
+   
+   \param data nviz data
+   \param id id of current clip plane
+   \param dx,dy,dz translation parameters
+ */
+int Nviz_get_cplane_translation(nv_data * data, int id, float *dx, float *dy, float *dz)
+{
+    *dx = data->cp_trans[id][X];
+    *dy = data->cp_trans[id][Y];
+    *dz = data->cp_trans[id][Z];
+
+    return 1;
+}
+/*!
+   \brief Set appropriate fence color
+   
+   \param type type of fence (FC_ABOVE, FC_BELOW, FC_BLEND, FC_GREY, FC_OFF)
+ */
+int Nviz_set_fence_color(nv_data * data, int type)
+{
+	GS_set_fencecolor(type);
+
+    return 1;
+
+}
+int Nviz_set_cplane_here(nv_data *data, int cplane, float sx, float sy)
+{
+    float x, y, z, len, los[2][3];
+    float dx, dy, dz;
+    float n, s, w, e;
+    Point3 realto, dir;
+    int id;
+    geosurf *gs;
+
+    if (GS_get_selected_point_on_surface(sx, sy, &id, &x, &y, &z)) {
+	gs = gs_get_surf(id);
+	if (gs) {
+	    realto[X] = x - gs->ox + gs->x_trans;
+	    realto[Y] = y - gs->oy + gs->y_trans;
+	    realto[Z] = z + gs->z_trans;
+	}
+	else
+	    return 0;
+    }
+    else {
+	if (gsd_get_los(los, (short)sx, (short)sy)) {
+	    len = GS_distance(Gv.from_to[FROM], Gv.real_to);
+	    GS_v3dir(los[FROM], los[TO], dir);
+	    GS_v3mult(dir, len);
+	    realto[X] = Gv.from_to[FROM][X] + dir[X];
+	    realto[Y] = Gv.from_to[FROM][Y] + dir[Y];
+	    realto[Z] = Gv.from_to[FROM][Z] + dir[Z];
+	}
+	else
+	    return 0;
+    }  
+    Nviz_get_cplane_translation(data, cplane, &dx, &dy, &dz);
+
+    GS_get_region(&n, &s, &w, &e);
+    dx = realto[X] - (e - w) / 2.;
+    dy = realto[Y] - (n - s) / 2.;
+
+    Nviz_set_cplane_translation(data, cplane, dx, dy, dz);
+
+    return 1;
+}

Modified: grass/branches/develbranch_6/lib/nviz/draw.c
===================================================================
--- grass/branches/develbranch_6/lib/nviz/draw.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/lib/nviz/draw.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -6,15 +6,20 @@
    Based on visualization/nviz/src/draw.c and
    visualization/nviz/src/togl_flythrough.c
    
-   (C) 2008, 2010 by the GRASS Development Team
+   (C) 2008, 2010-2011 by the GRASS Development Team
    This program is free software under the GNU General Public License
    (>=v2). Read the file COPYING that comes with GRASS for details.
 
    \author Updated/modified by Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
+   \author Textures by Anna Kratochvilova 
  */
 
 #include <grass/nviz.h>
 
+#ifndef GL_CLAMP_TO_EDGE
+#define GL_CLAMP_TO_EDGE 0x812F
+#endif 
+
 static int sort_surfs_max(int *, int *, int *, int);
 
 /*!
@@ -232,11 +237,23 @@
 
     if (draw_vol)
 	Nviz_draw_all_vol(data);
-
+	
     for(i = 0; i < data->num_fringes; i++) {
 	struct fringe_data * f = data->fringe[i];
 	GS_draw_fringe(f->id, f->color, f->elev, f->where);
     }
+
+    /* North Arrow */
+    if (data->draw_arrow) {
+	gsd_north_arrow(data->arrow->where, data->arrow->size,
+			(GLuint)NULL, data->arrow->color, data->arrow->color);
+    }
+
+    /* scale bar */
+    for (i = 0; i < data->num_scalebars; i++) {
+	struct scalebar_data *s = data->scalebar[i];
+	gsd_scalebar_v2(s->where, s->size, 0, s->color, s->color);
+    }
     
     GS_done_draw();
     GS_set_draw(GSD_BACK);
@@ -287,3 +304,119 @@
     
     return 1;
 }
+
+/*!
+  \brief Load image into texture
+
+  \param image_data image data 
+  \param width, height image screen size 
+  \param alpha has alpha channel 
+*/
+int Nviz_load_image(GLubyte *image_data, int width, int height, int alpha)
+{
+    unsigned int texture_id;
+    int  in_format;
+    GLenum format;
+
+    if (alpha)
+    {
+	in_format = 4;
+	format = GL_RGBA;
+    }
+    else
+    {
+	in_format = 3;
+	format = GL_RGB;
+    }
+    glGenTextures( 1, &texture_id);
+    glBindTexture(GL_TEXTURE_2D, texture_id);
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+    glTexImage2D(GL_TEXTURE_2D, 0, in_format, width, height, 0,format,
+		 GL_UNSIGNED_BYTE, image_data);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 
+
+    return texture_id;
+}
+
+/*!
+  \brief Set ortho view for drawing images
+
+  \param width, height image screen size 
+*/
+void Nviz_set_2D(int width, int height)
+{
+    glEnable(GL_BLEND); // images are transparent
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    glOrtho(0, width, 0, height, -1, 1);
+    
+    // set coordinate system from upper left corner
+    glScalef(1, -1, 1);
+    glTranslatef(0, -height, 0);
+
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+}
+
+/*!
+  \brief Draw image as texture
+
+  \param x, y image coordinates 
+  \param width, height image size 
+  \param texture_id texture id 
+*/
+void Nviz_draw_image(int x, int y, int width, int height, int texture_id)
+{
+    glBindTexture(GL_TEXTURE_2D, texture_id);
+    GS_set_draw(GSD_FRONT);
+
+    glEnable(GL_TEXTURE_2D);
+
+    glBegin(GL_QUADS);
+
+    glTexCoord2d(0.0,1.0);
+    glVertex2d(x, y);
+    glTexCoord2d(0.0,0.0);
+    glVertex2d(x, y + height);
+    glTexCoord2d(1.0,0.0);
+    glVertex2d(x + width, y + height);
+    glTexCoord2d(1.0,1.0);
+    glVertex2d(x + width, y);
+
+    glEnd();
+
+    GS_done_draw();
+    glDisable(GL_TEXTURE_2D);
+}
+
+/*!
+  \brief Delete texture
+
+  \param texture_id texture id
+*/
+void Nviz_del_texture(int texture_id)
+{
+    GLuint t[1];
+
+    t[0] = texture_id;
+    glDeleteTextures(1, t);
+}
+
+/*!
+  \brief Get maximum texture size
+
+*/
+void Nviz_get_max_texture(int *size)
+{
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, size);
+}

Modified: grass/branches/develbranch_6/lib/nviz/lights.c
===================================================================
--- grass/branches/develbranch_6/lib/nviz/lights.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/lib/nviz/lights.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -39,16 +39,16 @@
 		x, y, xpos, 1.0 - ypos);
     }
     */
-    num--;
-    data->light[num].id = num + 1;
+
+    data->light[num].id = num;
     data->light[num].x = x;
     data->light[num].y = y;
     data->light[num].z = z;
     data->light[num].w = w;
 
     G_debug(1, "Nviz_set_light_position(): num = %d x = %f y = %f z = %f w = %f",
-	    num + 1, x, y, z, w);
-    GS_setlight_position(num + 1, x, y, z, w);
+	    num, x, y, z, w);
+    GS_setlight_position(num, x, y, z, w);
 
     return 1;
 }
@@ -64,7 +64,6 @@
 {
     double r, g, b;
 
-    num--;
     data->light[num].brt = value;
 
     r = data->light[num].r * data->light[num].brt;
@@ -72,8 +71,8 @@
     b = data->light[num].b * data->light[num].brt;
 
     G_debug(1, "Nviz_set_light_bright(): num = %d value = %f r = %f g = %f b = %f",
-	    num + 1, value, r, g, b);
-    GS_setlight_color(num + 1, r, g, b);
+	    num, value, r, g, b);
+    GS_setlight_color(num, r, g, b);
 
     return 1;
 }
@@ -90,7 +89,6 @@
 {
     double r, g, b;
 
-    num--;
     data->light[num].r = red / 255.;
     data->light[num].g = green / 255.;
     data->light[num].b = blue / 255.;
@@ -100,8 +98,8 @@
     b = data->light[num].b * data->light[num].brt;
 
     G_debug(1, "Nviz_set_light_color(): num = %d r = %d/%f g = %d/%f b = %d/%f",
-	    num + 1, red, r, green, g, blue, b);
-    GS_setlight_color(num + 1, r, g, b);
+	    num, red, r, green, g, blue, b);
+    GS_setlight_color(num, r, g, b);
 
     return 1;
 }
@@ -115,14 +113,13 @@
  */
 int Nviz_set_light_ambient(nv_data * data, int num, double value)
 {
-    num--;
     data->light[num].ar = value;
     data->light[num].ag = value;
     data->light[num].ab = value;
 
     G_debug(1, "Nviz_set_light_ambient(): num = %d value = %f",
-	    num + 1, value);
-    GS_setlight_ambient(num + 1, value, value, value);
+	    num, value);
+    GS_setlight_ambient(num, value, value, value);
     
     return 1;
 }
@@ -135,8 +132,8 @@
  */
 int Nviz_init_light(nv_data * data, int num)
 {
-    num--;
-    if (num >= MAX_LIGHTS) {
+	G_debug(1, "Nviz_init_light(): num = %d", num);
+    if (num > MAX_LIGHTS) {
 	return 0;
     }
 
@@ -180,3 +177,16 @@
     return 1;
 }
 
+/*!
+  \brief Draw lighting model
+
+  \param data nviz data
+*/
+void Nviz_draw_model(nv_data * data)
+{
+    GS_set_draw(GSD_FRONT);
+    GS_ready_draw();
+    GS_draw_lighting_model();
+    GS_done_draw();
+    GS_set_draw(GSD_BACK);
+}

Modified: grass/branches/develbranch_6/lib/nviz/nviz.c
===================================================================
--- grass/branches/develbranch_6/lib/nviz/nviz.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/lib/nviz/nviz.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -37,14 +37,24 @@
     }
 
     /* lights */
-    for (i = 0; i < MAX_LIGHTS; i++) {
+    GS_set_light_reset(1);
+    
+    for (i = 0; i < MAX_LIGHTS - 1; i++) {
 	Nviz_new_light(data);
     }
 
     /* fringe */
     data->num_fringes = 0;
     data->fringe = NULL;
-    
+
+    /* north arrow */
+    data->draw_arrow = 0;
+    data->arrow = NULL;
+
+    /* scale bar*/
+    data->num_scalebars = 0;
+    data->scalebar = NULL;
+
     return;
 }
 
@@ -61,6 +71,19 @@
     }
     data->num_fringes = 0;
     data->fringe = NULL;
+    
+    if (data->arrow) {
+	G_free(data->arrow);
+	data->arrow = NULL;
+	data->draw_arrow = 0;
+    }
+
+    for (i = 0; data->num_scalebars; i++) {
+	G_free(data->scalebar[i]);
+	data->scalebar[i] = NULL;
+    }
+    data->num_scalebars = 0;
+    data->scalebar = NULL;
 }
 
 /*!
@@ -134,6 +157,7 @@
 	if (num < 1)
 	    return NULL;
 	id = surf[0];
+	G_free(surf);
     }
      
 
@@ -177,6 +201,7 @@
 	if (num < 1)
 	    return NULL;
 	id = surf[0];
+	G_free(surf);
     }
     
     for (i = 0; i < data->num_fringes; i++) {
@@ -199,3 +224,211 @@
     
     return f;
 }
+/*! Draw fringe
+
+   \param data nviz data
+ */
+void Nviz_draw_fringe(nv_data *data)
+{
+    int i;
+
+    for (i = 0; i < data->num_fringes; i++) {
+	struct fringe_data *f = data->fringe[i];
+
+	GS_draw_fringe(f->id, f->color, f->elev, f->where);
+    }
+}
+/*!
+   \brief Sets the North Arrow position and return world coords
+
+   \param data nviz data
+   \param sx,sy screen coordinates
+   \param size arrow length
+   \param color arrow/text color
+ */
+int Nviz_set_arrow(nv_data *data,
+		   int sx, int sy, float size,
+		   unsigned int color)
+{
+    int id, pt[2];
+    int *surf_list, num_surfs;
+    float coords[3];
+    struct arrow_data *arw;
+
+    if (GS_num_surfs() > 0) {
+	surf_list = GS_get_surf_list(&num_surfs);
+	id = surf_list[0];
+	G_free(surf_list);
+
+	pt[0] = sx;
+	pt[1] = sy;
+
+	GS_set_Narrow(pt, id, coords);
+
+	if (data->arrow) {
+	    data->arrow->color = color;
+	    data->arrow->size  = size;
+	    data->arrow->where[0]  = coords[0];
+	    data->arrow->where[1]  = coords[1];
+	    data->arrow->where[2]  = coords[2];
+	}    
+	else {
+	    arw = (struct arrow_data *) G_malloc(sizeof(struct arrow_data));
+	    arw->color = color;
+	    arw->size  = size;
+	    arw->where[0]  = coords[0];
+	    arw->where[1]  = coords[1];
+	    arw->where[2]  = coords[2];
+
+	    data->arrow = arw;
+	}
+	return 1;
+    }
+    return 0;
+}
+
+
+/*!
+   \brief Draws the North Arrow
+
+   \param data nviz data
+ */
+int Nviz_draw_arrow(nv_data *data)
+{
+
+    struct arrow_data *arw = data->arrow;
+    GLuint FontBase = 0; /* don't know how to get fontbase*/
+
+    data->draw_arrow = 1;
+    gsd_north_arrow(arw->where, arw->size, FontBase, arw->color, arw->color);
+
+    return 1;
+}
+
+/*!
+   \brief Deletes the North Arrow
+
+   \param data nviz data
+ */
+void Nviz_delete_arrow(nv_data *data)
+{
+    data->draw_arrow = 0;
+
+    return;
+}
+
+/*! Add new scalebar
+
+  \param data nviz data
+  \param bar_id scale bar id
+  \param coords real(?) coordinates
+  \param size scale bar length
+  \param color scalebar/text color
+
+  \return pointer to allocated scalebar_data structure
+  \return NULL on error
+*/
+
+struct scalebar_data *Nviz_new_scalebar(nv_data *data,
+		      int bar_id, float *coords, float size,
+		      unsigned int color)
+{
+    struct scalebar_data *s;
+     
+
+    s = (struct scalebar_data *) G_malloc(sizeof(struct scalebar_data));
+    s->id = bar_id;
+    s->color = color;
+    s->size = size;
+    s->where[0] = coords[0];
+    s->where[1] = coords[1];
+    s->where[2] = coords[2];
+
+    data->scalebar = (struct scalebar_data **) G_realloc(data->scalebar,
+		      data->num_scalebars + 1 * sizeof(struct scalebar_data *));
+    data->scalebar[data->num_scalebars++] = s;
+
+    return s;
+
+}
+/*!
+   \brief Sets the scale bar position and return world coords
+
+   \param data nviz data
+   \param bar_id scale bar id
+   \param sx,sy screen coordinates
+   \param size scale bar length
+   \param color scalebar/text color
+
+   \return pointer to allocated scalebar_data structure
+   \return NULL when there's no surface
+ */
+struct scalebar_data *Nviz_set_scalebar(nv_data *data, int bar_id,
+		      int sx, int sy, float size,
+		      unsigned int color)
+{
+    int i, id, pt[2];
+    int *surf_list, num_surfs;
+    float coords[3];
+    struct scalebar_data *s;
+
+    if (GS_num_surfs() > 0) {
+	surf_list = GS_get_surf_list(&num_surfs);
+	id = surf_list[0];
+	G_free(surf_list);
+
+	pt[0] = sx;
+	pt[1] = sy;
+
+	GS_set_Narrow(pt, id, coords); /* the same like arrow */
+
+	for (i = 0; i < data->num_scalebars; i++) {
+	    s = data->scalebar[i];
+	    if (s->id == bar_id) {
+		s->color = color;
+		s->size = size;
+		s->where[0] = coords[0];
+		s->where[1] = coords[1];
+		s->where[2] = coords[2];
+
+		return s;
+	    }
+	}
+	
+	s = Nviz_new_scalebar(data, bar_id, coords, size, color);
+
+	return s;
+    }
+    return NULL;
+}
+/*!
+   \brief Draws the Scale bar
+
+   \param data nviz data
+ */
+void Nviz_draw_scalebar(nv_data *data)
+{
+    int i;
+
+    GLuint FontBase = 0; /* don't know how to get fontbase*/
+
+    for (i = 0; i < data->num_scalebars; i++) {
+	struct scalebar_data *s = data->scalebar[i];
+
+	gsd_scalebar_v2(s->where, s->size, FontBase, s->color, s->color);
+    }
+}
+
+/*!
+   \brief Deletes scale bar
+
+   \param data nviz data
+ */
+void Nviz_delete_scalebar(nv_data *data, int bar_id)
+{
+    if (bar_id < data->num_scalebars) {
+	G_free(data->scalebar[bar_id]);
+	data->scalebar[bar_id] = NULL;
+	data->num_scalebars--;
+    }
+}

Modified: grass/branches/develbranch_6/lib/nviz/position.c
===================================================================
--- grass/branches/develbranch_6/lib/nviz/position.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/lib/nviz/position.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -1,10 +1,10 @@
 /*!
    \file lib/nviz/position.c
-   
+
    \brief Nviz library -- Position, focus settings
-   
+
    Based on visualization/nviz/src/position.c
-   
+
    (C) 2008, 2010 by the GRASS Development Team
    This program is free software under the GNU General Public License
    (>=v2). Read the file COPYING that comes with GRASS for details.
@@ -20,21 +20,21 @@
 
    Set position to center of view
  */
-void Nviz_init_view(nv_data *data)
+void Nviz_init_view(nv_data * data)
 {
     GS_init_view();
     Nviz_set_focus_state(1);	/* center of view */
-    
+
     /* set default lights (1 & 2) */
     Nviz_set_light_position(data, 1, 0.68, -0.68, 0.80, 0.0);
-    Nviz_set_light_bright(data,   1, 0.8);
-    Nviz_set_light_color(data,    1, 255, 255, 255);
-    Nviz_set_light_ambient(data,  1, 0.2);
+    Nviz_set_light_bright(data, 1, 0.8);
+    Nviz_set_light_color(data, 1, 255, 255, 255);
+    Nviz_set_light_ambient(data, 1, 0.2);
     Nviz_set_light_position(data, 2, 0.0, 0.0, 1.0, 0.0);
-    Nviz_set_light_bright(data,   2, 0.5);
-    Nviz_set_light_color(data,    2, 255, 255, 255);
-    Nviz_set_light_ambient(data,  2, 0.3);
-    
+    Nviz_set_light_bright(data, 2, 0.5);
+    Nviz_set_light_color(data, 2, 255, 255, 255);
+    Nviz_set_light_ambient(data, 2, 0.3);
+
     return;
 }
 
@@ -109,3 +109,97 @@
 
     return id;
 }
+
+/*!
+   \brief Get focus
+
+   \param data nviz data
+   \param x,y,z focus coordinates
+ */
+int Nviz_get_focus(nv_data * data, float *x, float *y, float *z)
+{
+    float realto[3];
+
+    /* Get current center */
+    GS_get_focus(realto);
+    *x = realto[0];
+    *y = realto[1];
+    *z = realto[2];
+    // old nviz code is more complicated and it doesn't work properly,
+    // no idea why
+
+    return 1;
+
+}
+
+/*!
+   \brief Set focus
+
+   \param data nviz data
+   \param x, y, z focus coordinates
+ */
+int Nviz_set_focus(nv_data * data, float x, float y, float z)
+{
+    float realto[3];
+
+    realto[0] = x;
+    realto[1] = y;
+    realto[2] = z;
+    GS_set_focus(realto);
+    // old nviz code is more complicated and it doesn't work properly,
+    // no idea why
+
+    return 1;
+
+}
+
+/*!
+   \brief Test focus
+
+   \param data nviz data
+ */
+int Nviz_has_focus(nv_data * data)
+{
+    float realto[3];
+
+    if (GS_get_focus(realto))
+	return 1;
+    else
+	return 0;
+}
+
+/*!
+   \brief Get xy range
+
+   \param data nviz data
+ */
+float Nviz_get_xyrange(nv_data * data)
+{
+    return data->xyrange;
+}
+
+/*!
+   \brief Get z range
+
+   \param data nviz data
+   \param min,max z range
+ */
+int Nviz_get_zrange(nv_data * data, float *min, float *max)
+{
+    GS_get_zrange_nz(min, max);
+    return 1;
+}
+
+/*!
+   \brief Get largest dimension
+
+   \param data nviz data
+ */
+float Nviz_get_longdim(nv_data * data)
+{
+    float dim;
+
+    GS_get_longdim(&dim);
+
+    return dim;
+}

Modified: grass/branches/develbranch_6/lib/ogsf/GS2.c
===================================================================
--- grass/branches/develbranch_6/lib/ogsf/GS2.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/lib/ogsf/GS2.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -78,6 +78,7 @@
 static struct Cell_head wind;
 static int Buffermode;
 static int Numlights = 0;
+static int Resetlight = 1;
 static int Modelshowing = 0;
 
 void void_func(void)
@@ -113,9 +114,13 @@
 
     Gv.scale = GS_UNIT_SIZE / Longdim;
 
+    G_debug(1, "GS_libinit(): n=%f s=%f w=%f e=%f scale=%f first=%d",
+	    Region[0], Region[1], Region[2], Region[3], Gv.scale, first);
+    
     Cxl_func = void_func;
     Swap_func = void_func;
 
+    
     if (first) {
 	gs_init();
     }
@@ -244,7 +249,16 @@
 
     return (-1);
 }
-
+void GS_set_light_reset(int i)
+{
+    Resetlight = i;
+    if (i)
+	Numlights = 0;
+}
+int GS_get_light_reset(void)
+{
+    return Resetlight;
+}
 /*!
    \brief Add new model light
 
@@ -253,12 +267,12 @@
  */
 int GS_new_light(void)
 {
-    static int first = 1;
     int i;
 
-    if (first) {
-	first = 0;
+    if (GS_get_light_reset()) {
 
+	GS_set_light_reset(0);
+
 	for (i = 0; i < MAX_LIGHTS; i++) {
 	    Gv.lights[i].position[X] = Gv.lights[i].position[Y] = 0.0;
 	    Gv.lights[i].position[Z] = 1.0;
@@ -277,10 +291,10 @@
 	gsd_deflight(Numlights + 1, &(Gv.lights[Numlights]));
 	gsd_switchlight(Numlights + 1, 1);
 
-	return (++Numlights);
+	return ++Numlights;
     }
 
-    return (-1);
+    return -1;
 }
 
 /*!
@@ -1548,32 +1562,33 @@
  */
 int GS_delete_surface(int id)
 {
-    int i, j, found = 0;
-
-    G_debug(3, "GS_delete_surface");
-
+    int i, j, found;
+    
+    found = FALSE;
+    
+    G_debug(1, "GS_delete_surface(): id=%d", id);
+    
     if (GS_surf_exists(id)) {
 	gs_delete_surf(id);
-
 	for (i = 0; i < Next_surf && !found; i++) {
 	    if (Surf_ID[i] == id) {
-		found = 1;
+		found = TRUE;
 
 		for (j = i; j < Next_surf; j++) {
 		    Surf_ID[j] = Surf_ID[j + 1];
 		}
 	    }
 	}
-
+	
 	gv_update_drapesurfs();
 
 	if (found) {
 	    --Next_surf;
-	    return (1);
+	    return 1;
 	}
     }
 
-    return (-1);
+    return -1;
 }
 
 
@@ -2864,6 +2879,72 @@
 }
 
 /*!
+   \brief Set rotation params
+ */
+void GS_set_rotation(double angle, double x, double y, double z)
+{
+    Gv.rotate.rot_angle = angle;
+    Gv.rotate.rot_axes[0] = x;
+    Gv.rotate.rot_axes[1] = y;
+    Gv.rotate.rot_axes[2] = z;
+    Gv.rotate.do_rot = 1;
+
+    return;
+}
+
+/*!
+   \brief Stop scene rotation
+ */
+void GS_unset_rotation(void)
+{
+    Gv.rotate.do_rot = 0;
+}
+
+/*!
+   \brief Reset scene rotation
+ */
+void GS_init_rotation(void)
+{
+    int i;
+
+    for (i = 0; i < 16; i++) {
+	if (i == 0 || i == 5 || i == 10 || i == 15)
+	    Gv.rotate.rotMatrix[i] = 1.0;
+	else
+	    Gv.rotate.rotMatrix[i] = 0.0;
+    }
+    Gv.rotate.rot_angle = 0.0;
+    Gv.rotate.rot_axes[0] = 0.0;
+    Gv.rotate.rot_axes[1] = 0.0;
+    Gv.rotate.rot_axes[2] = 0.0;
+    Gv.rotate.do_rot = 0;
+    
+}
+/*!
+ * \brief Get rotation matrix
+ */ 
+void GS_get_rotation_matrix(double *matrix)
+{
+    int i;
+
+    for (i = 0; i < 16; i++) {
+	matrix[i] = Gv.rotate.rotMatrix[i];
+    }
+}
+
+/*!
+ * \brief Set rotation matrix
+ */ 
+void GS_set_rotation_matrix(double *matrix)
+{
+    int i;
+
+    for (i = 0; i < 16; i++) {
+	Gv.rotate.rotMatrix[i] = matrix[i];
+    }
+}
+
+/*!
    \brief Unset focus
  */
 void GS_set_nofocus(void)
@@ -3262,6 +3343,7 @@
  */
 void GS_init_view(void)
 {
+    int i;
     static int first = 1;
 
     G_debug(3, "GS_init_view");
@@ -3295,6 +3377,9 @@
 	/* replace these with something meaningful */
 	Gv.fov = 450;
 	Gv.twist = 0;
+
+	GS_init_rotation();
+
 	Gv.from_to[FROM][X] = Gv.from_to[FROM][Y] =
 	    Gv.from_to[FROM][Z] = GS_UNIT_SIZE / 2.;
 

Modified: grass/branches/develbranch_6/lib/ogsf/Gp3.c
===================================================================
--- grass/branches/develbranch_6/lib/ogsf/Gp3.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/lib/ogsf/Gp3.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -125,6 +125,8 @@
     }
 
     G_get_set_window(&wind);
+    Vect_set_constraint_region(&map, wind.north, wind.south, wind.east,
+			       wind.west, PORT_DOUBLE_MAX, -PORT_DOUBLE_MAX);
 
     /* get ndim */
     ndim = 2;

Modified: grass/branches/develbranch_6/lib/ogsf/gs.c
===================================================================
--- grass/branches/develbranch_6/lib/ogsf/gs.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/lib/ogsf/gs.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -572,7 +572,7 @@
 	    /* for ea att of all other surfs */
 	    for (gs = Surf_top; gs; gs = gs->next) {
 		for (j = 0; j < MAX_ATTS; j++) {
-		    if (old_datah == gs->att[j].hdata) {
+		    if ((old_datah == gs->att[j].hdata) && (fs != gs)) {
 			same = 1;
 		    }
 		}

Modified: grass/branches/develbranch_6/lib/ogsf/gsd_objs.c
===================================================================
--- grass/branches/develbranch_6/lib/ogsf/gsd_objs.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/lib/ogsf/gsd_objs.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -1234,6 +1234,74 @@
 }
 
 /*!
+   \brief Draw Scalebar (as lines)
+
+   Adapted from gsd_scalebar A.Kratochvilova 2011
+
+   \param pos2 scalebar position
+   \param fontbase font-base (unused)
+   \param bar_clr barscale color
+   \param text_clr text color (unused)
+
+   \return 1
+ */
+int gsd_scalebar_v2(float *pos, float len, GLuint fontbase,
+		 unsigned long bar_clr, unsigned long text_clr)
+{
+    float base[6][3];
+    float Ntop[] = { 0.0, 0.0, 1.0 };
+
+    base[0][Z] = base[1][Z] = base[2][Z] = pos[Z];
+    base[3][Z] = base[4][Z] = base[5][Z] = pos[Z];
+
+    /* simple scalebar: |------| */
+    base[0][X] = base[2][X] = base[3][X] = pos[X] - len / 2.;
+    base[1][X] = base[4][X] = base[5][X] = pos[X] + len / 2.;
+    base[0][Y] = base[1][Y] = pos[Y];
+    base[2][Y] = base[4][Y] = pos[Y] - len / 12;
+    base[3][Y] = base[5][Y] = pos[Y] + len / 12;
+
+    /* make sure we are drawing in front buffer */
+    GS_set_draw(GSD_FRONT);
+
+    gsd_pushmatrix();
+    gsd_do_scale(1);		/* get map scale factor */
+
+    glNormal3fv(Ntop);
+
+    gsd_color_func(bar_clr);
+
+    gsd_linewidth(3); /* could be optional */
+
+    /* ------- */
+    gsd_bgnline();
+    gsd_vert_func(base[0]);
+    gsd_vert_func(base[1]);
+    gsd_endline();
+
+    /* |------- */
+    gsd_bgnline();
+    gsd_vert_func(base[2]);
+    gsd_vert_func(base[3]);
+    gsd_endline();
+
+    /* |-------| */
+    gsd_bgnline();
+    gsd_vert_func(base[4]);
+    gsd_vert_func(base[5]);
+    gsd_endline();
+
+    /* TODO -- draw units */
+
+    GS_done_draw();
+
+    gsd_popmatrix();
+    gsd_flush();
+
+    return 1;
+}
+
+/*!
    \brief Primitives only called after transforms
 
    Center is actually center at base of 8 sided cone

Modified: grass/branches/develbranch_6/lib/ogsf/gsd_views.c
===================================================================
--- grass/branches/develbranch_6/lib/ogsf/gsd_views.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/lib/ogsf/gsd_views.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -88,7 +88,7 @@
     return (1);
 }
 
-
+#if 0
 /*!
    \brief Set view
 
@@ -134,7 +134,79 @@
 
     return;
 }
+#endif
+/*!
+   \brief Set view
 
+   Establishes viewing & projection matrices
+
+   \param gv view (geoview)
+   \param dp display (geodisplay)
+ */
+void gsd_set_view(geoview * gv, geodisplay * gd)
+{
+    double up[3];
+    float pos[3];
+    int i;
+    GLdouble modelMatrix[16];
+    GLint mm;
+
+    /* will expand when need to check for in focus, ortho, etc. */
+
+    gsd_check_focus(gv);
+    gsd_get_zup(gv, up);
+
+    gd->aspect = GS_get_aspect();
+
+    glGetIntegerv(GL_MATRIX_MODE, &mm);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    gluPerspective((double).1 * (gv->fov), (double)gd->aspect,
+		   (double)gd->nearclip, (double)gd->farclip);
+
+    glMatrixMode(mm);
+    
+    glLoadIdentity();
+
+    /* update twist parm */
+    glRotatef((float)(gv->twist / 10.), 0.0, 0.0, 1.0);
+
+    /* OGLXXX lookat: replace UPx with vector */
+    gluLookAt((double)gv->from_to[FROM][X], (double)gv->from_to[FROM][Y],
+	      (double)gv->from_to[FROM][Z], (double)gv->from_to[TO][X],
+	      (double)gv->from_to[TO][Y], (double)gv->from_to[TO][Z],
+	      (double)up[X], (double)up[Y], (double)up[Z]);
+	      
+    /* rotate to get rotation matrix and then save it*/
+    if (gv->rotate.do_rot) {
+
+	glPushMatrix();
+	glLoadMatrixd(gv->rotate.rotMatrix);
+
+	glRotated(gv->rotate.rot_angle, gv->rotate.rot_axes[0], 
+		  gv->rotate.rot_axes[1], gv->rotate.rot_axes[2]);
+	glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
+
+	for (i = 0; i < 16; i++) {
+	    gv->rotate.rotMatrix[i] = modelMatrix[i];
+	}
+
+	glPopMatrix();
+    }
+    
+    gs_get_datacenter(pos);
+    gsd_surf2model(pos);
+    /* translate rotation center to view center, rotate and translate back */
+    glTranslatef(pos[0], pos[1], pos[2]);
+    glMultMatrixd(gv->rotate.rotMatrix);
+    glTranslatef(-pos[0], -pos[1], -pos[2]);
+
+    /* have to redefine clipping planes when view changes */
+
+    gsd_update_cplanes();
+
+    return;
+}
 /*!
    \brief Check focus
 
@@ -361,7 +433,28 @@
 
     return;
 }
+/*!
+   \brief Convert surface to model coordinates
 
+   \param point 3d point (Point3)
+ */
+void gsd_surf2model(Point3 point)
+{
+    float min, max, sx, sy, sz;
+
+    /* need to undo z scaling & translate */
+    GS_get_scale(&sx, &sy, &sz, 1);
+    GS_get_zrange(&min, &max, 0);
+
+    point[Z] = (sz ? (point[Z] - min) * sz : 0.0);
+
+    /* need to unscale x & y */
+    point[X] = (sx ? point[X] * sx : 0.0);
+    point[Y] = (sy ? point[Y] * sy : 0.0);
+
+
+    return;
+}
 /*!
    \brief Convert surface to real coordinates
 

Modified: grass/branches/develbranch_6/misc/m.nviz.image/args.c
===================================================================
--- grass/branches/develbranch_6/misc/m.nviz.image/args.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/misc/m.nviz.image/args.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -31,6 +31,8 @@
 static void args_volume(struct GParams *);
 static void args_lighting(struct GParams *);
 static void args_fringe(struct GParams *);
+static void args_cplane(struct GParams *);
+static void args_arrow(struct GParams *);
 
 /*!
   \brief Parse command
@@ -73,7 +75,13 @@
 
     /*** fringe ***/
     args_fringe(params);
-    
+
+    /*** cutting plane ***/ 
+    args_cplane(params);
+
+    /*** north arrow ***/ 
+    args_arrow(params);
+
     /*** output image ***/
     /* output */
     params->output = G_define_standard_option(G_OPT_F_OUTPUT);
@@ -377,7 +385,7 @@
     params->vpoint_size->multiple = YES;
     params->vpoint_size->description = _("Icon size");
     params->vpoint_size->guisection = _("Vector points");
-    params->vpoint_size->options = "1-1000";
+    params->vpoint_size->options = "1-10000";
     params->vpoint_size->answer = "100";
 
     /* point width */
@@ -485,6 +493,16 @@
     params->exag->multiple = NO;
     params->exag->description = _("Vertical exaggeration");
 
+    /* focus */
+    params->focus = G_define_option();
+    params->focus->key = "focus";
+    params->focus->key_desc = "x,y,z";
+    params->focus->type = TYPE_DOUBLE;
+    params->focus->required = NO;
+    params->focus->multiple = NO;
+    params->focus->description = _("Focus to point on surface (from SW corner in map units)");
+    params->focus->guisection = _("Viewpoint");
+
     return;
 }
 
@@ -551,9 +569,152 @@
     params->isosurf_level->description = _("Isosurface level");
     params->isosurf_level->guisection = _("Volumes");
 
+    /* isosurface color map */
+    params->isosurf_color_map = G_define_standard_option(G_OPT_R3_MAPS);
+    params->isosurf_color_map->key = "isosurf_color_map";
+    params->isosurf_color_map->required = NO;
+    params->isosurf_color_map->multiple = YES;
+    params->isosurf_color_map->description = _("Name of volume for isosurface color");
+    params->isosurf_color_map->guisection = _("Volumes");
+
+    /* isosurface color value */
+    params->isosurf_color_const = G_define_standard_option(G_OPT_C_FG);
+    params->isosurf_color_const->key = "isosurf_color_value";
+    params->isosurf_color_const->required = NO;
+    params->isosurf_color_const->multiple = YES;
+    params->isosurf_color_const->label = _("Isosurface color");
+    params->isosurf_color_const->guisection = _("Volumes");
+    params->isosurf_color_const->answer = NULL;
+
+    /* isosurface transparency */
+    params->isosurf_transp_map = G_define_standard_option(G_OPT_R3_MAP);
+    params->isosurf_transp_map->multiple = YES;
+    params->isosurf_transp_map->required = NO;
+    params->isosurf_transp_map->description =
+	_("Name of 3D raster map(s) for isosurface transparency");
+    params->isosurf_transp_map->guisection = _("Volumes");
+    params->isosurf_transp_map->key = "isosurf_transparency_map";
+
+    params->isosurf_transp_const = G_define_option();
+    params->isosurf_transp_const->key = "isosurf_transparency_value";
+    params->isosurf_transp_const->key_desc = "value";
+    params->isosurf_transp_const->type = TYPE_INTEGER;
+    params->isosurf_transp_const->required = NO;
+    params->isosurf_transp_const->multiple = YES;
+    params->isosurf_transp_const->description = _("Transparency value(s)for isosurfaces");
+    params->isosurf_transp_const->guisection = _("Volumes");
+    params->isosurf_transp_const->options = "0-255";
+    
+    /* isosurface shininess */
+    params->isosurf_shine_map = G_define_standard_option(G_OPT_R3_MAP);
+    params->isosurf_shine_map->multiple = YES;
+    params->isosurf_shine_map->required = NO;
+    params->isosurf_shine_map->description = _("Name of 3D raster map(s) for shininess");
+    params->isosurf_shine_map->guisection = _("Volumes");
+    params->isosurf_shine_map->key = "isosurf_shininess_map";
+
+    params->isosurf_shine_const = G_define_option();
+    params->isosurf_shine_const->key = "isosurf_shininess_value";
+    params->isosurf_shine_const->key_desc = "value";
+    params->isosurf_shine_const->type = TYPE_INTEGER;
+    params->isosurf_shine_const->required = NO;
+    params->isosurf_shine_const->multiple = YES;
+    params->isosurf_shine_const->description = _("Shininess value(s) for isosurfaces");
+    params->isosurf_shine_const->guisection = _("Volumes");
+    params->isosurf_shine_const->options = "0-255";
+
+    /* slices */
+    /* slice axis */
+    params->slice = G_define_option();
+    params->slice->key = "slice";
+    params->slice->key_desc = "volume:axis";
+    params->slice->type = TYPE_STRING;
+    params->slice->required = NO;
+    params->slice->multiple = YES;
+    params->slice->description = _("Volume slice parallel to given axis (x, y, z)");
+    params->slice->guisection = _("Volumes");
+
+    /* slice position */
+    params->slice_pos = G_define_option();
+    params->slice_pos->key = "slice_position";
+    params->slice_pos->key_desc = "x1,x2,y1,y2,z1,z2";
+    params->slice_pos->type = TYPE_DOUBLE;
+    params->slice_pos->required = NO;
+    params->slice_pos->multiple = YES;
+    params->slice_pos->description = _("Volume slice position");
+    params->slice_pos->guisection = _("Volumes");
+    params->slice_pos->answer = "0,1,0,1,0,1";
+    
+    /* slice transparency */
+    params->slice_transp = G_define_option();
+    params->slice_transp->key = "slice_transparency";
+    params->slice_transp->key_desc = "value";
+    params->slice_transp->type = TYPE_INTEGER;
+    params->slice_transp->required = NO;
+    params->slice_transp->multiple = YES;
+    params->slice_transp->description = _("Volume slice transparency");
+    params->slice_transp->guisection = _("Volumes");
+    params->slice_transp->answer = "0";
+    params->slice_transp->options = "0-255";
+
     return;
 }
 
+void args_cplane(struct GParams *params)
+{
+    params->cplane = G_define_option();
+    params->cplane->key = "cplane";
+    params->cplane->key_desc = "value";
+    params->cplane->type = TYPE_INTEGER;
+    params->cplane->required = NO;
+    params->cplane->multiple = YES;
+    params->cplane->description = _("Cutting plane index (0-5)");
+    params->cplane->guisection = _("Cutting planes");
+    
+    params->cplane_pos = G_define_option();
+    params->cplane_pos->key = "cplane_position";
+    params->cplane_pos->key_desc = "x,y,z";
+    params->cplane_pos->type = TYPE_DOUBLE;
+    params->cplane_pos->required = NO;
+    params->cplane_pos->multiple = YES;
+    params->cplane_pos->description = _("Cutting plane x,y,z coordinates");
+    params->cplane_pos->guisection = _("Cutting planes");
+    params->cplane_pos->answer = "0,0,0";
+    
+    params->cplane_rot = G_define_option();
+    params->cplane_rot->key = "cplane_rotation";
+    params->cplane_rot->key_desc = "value";
+    params->cplane_rot->type = TYPE_DOUBLE;
+    params->cplane_rot->multiple = YES;
+    params->cplane_rot->required = NO;
+    params->cplane_rot->guisection = _("Cutting planes");
+    params->cplane_rot->description = _("Cutting plane rotation along the vertical axis");
+    params->cplane_rot->answer = "0";
+    params->cplane_rot->options="0-360";
+    
+    params->cplane_tilt = G_define_option();
+    params->cplane_tilt->key = "cplane_tilt";
+    params->cplane_tilt->key_desc = "value";
+    params->cplane_tilt->type = TYPE_DOUBLE;
+    params->cplane_tilt->multiple = YES;
+    params->cplane_tilt->required = NO;
+    params->cplane_tilt->guisection = _("Cutting planes");
+    params->cplane_tilt->description = _("Cutting plane tilt");
+    params->cplane_tilt->answer = "0";
+    params->cplane_tilt->options="0-360";
+    
+    params->cplane_shading = G_define_option();
+    params->cplane_shading->key = "cplane_shading";
+    params->cplane_shading->key_desc = "string";
+    params->cplane_shading->type = TYPE_STRING;
+    params->cplane_shading->multiple = NO;
+    params->cplane_shading->required = NO;
+    params->cplane_shading->guisection = _("Cutting planes");
+    params->cplane_shading->description = _("Cutting plane color (between two surfaces)");
+    params->cplane_shading->answer = "clear";
+    params->cplane_shading->options= "clear,top,bottom,blend,shaded";
+}
+
 void args_lighting(struct GParams *params)
 {
     params->light_pos = G_define_option();
@@ -624,6 +785,36 @@
     params->fringe_elev->answer = "55";
 }
 
+void args_arrow(struct GParams *params)
+{
+    params->north_arrow = G_define_option();
+    params->north_arrow->key = "arrow_position";
+    params->north_arrow->key_desc = "x,y";
+    params->north_arrow->type = TYPE_INTEGER;
+    params->north_arrow->required = NO;
+    params->north_arrow->multiple = NO;
+    params->north_arrow->description = _("Place north arrow at given position \
+	(in screen coordinates from bottom left corner)");
+    params->north_arrow->guisection = _("Decoration");
+    
+    params->north_arrow_size = G_define_option();
+    params->north_arrow_size->key = "arrow_size";
+    params->north_arrow_size->key_desc = "value";
+    params->north_arrow_size->type = TYPE_DOUBLE;
+    params->north_arrow_size->required = NO;
+    params->north_arrow_size->multiple = NO;
+    params->north_arrow_size->description = _("North arrow size (in map units)");
+    params->north_arrow_size->guisection = _("Decoration");
+    
+    params->north_arrow_color = G_define_standard_option(G_OPT_C_FG);
+    params->north_arrow_color->key = "arrow_color";
+    params->north_arrow_color->required = NO;
+    params->north_arrow_color->multiple = NO;
+    params->north_arrow_color->label = _("North arrow color");
+    params->north_arrow_color->guisection = _("Decoration");
+    params->north_arrow_color->answer = "black";
+}
+
 /*!
    \brief Get number of answers of given option
 
@@ -642,6 +833,8 @@
 	    i++;
 	}
     }
+    
+    G_debug(3, "opt_get_num_answers(): opt=%s num=%d", opt->key, i);
 
     return i;
 }
@@ -654,10 +847,12 @@
 void check_parameters(const struct GParams *params)
 {
     int nelev_map, nelev_const, nelevs;
-    int nmaps, nconsts;
+    int nmaps, nconsts, ncoords, ncplanes;
 
     int nvects;
 
+    int nvolumes, nisosurf, nslices;
+
     /* topography */
     nelev_map = opt_get_num_answers(params->elev_map);
     nelev_const = opt_get_num_answers(params->elev_const);
@@ -742,6 +937,25 @@
 			  params->elev_map->key, params->elev_const->key,
 			  nelevs, params->wire_color->key, nconsts);
     }
+    
+    /* 
+     * Cutting planes
+     */
+    ncplanes = opt_get_num_answers(params->cplane);
+    ncoords = opt_get_num_answers(params->cplane_pos);
+	if (ncplanes > 0 && ncplanes * 3 != ncoords)
+	    G_fatal_error(_("Inconsistent number of attributes (<%s> %d: <%s> %d x 3)"),
+			  params->cplane->key, ncplanes, params->cplane_pos->key, ncoords/3);
+              
+    nconsts = opt_get_num_answers(params->cplane_rot);
+    if (ncplanes > 0 && ncplanes != nconsts)
+        G_fatal_error(_("Inconsistent number of attributes (<%s> %d: <%s> %d)"),
+          params->cplane->key, ncplanes, params->cplane_rot->key, nconsts);
+          
+    nconsts = opt_get_num_answers(params->cplane_tilt);
+    if (ncplanes > 0 && ncplanes != nconsts)
+        G_fatal_error(_("Inconsistent number of attributes (<%s> %d: <%s> %d)"),
+          params->cplane->key, ncplanes, params->cplane_tilt->key, nconsts);
 
     /*
      * vector
@@ -776,6 +990,50 @@
 		      params->vlines->key, nvects, params->vline_height->key,
 		      nconsts);
 
+    /* position */
+    nconsts = opt_get_num_answers(params->vline_pos);
+    if (nvects > 0 && nconsts != 3 * nvects)
+	G_fatal_error(_("Inconsistent number of attributes (<%s> %d: <%s> %d)"),
+		      params->vlines->key, nvects, params->vline_pos->key,
+		      nconsts);
+
+    /*
+     * volumes
+     */
+    nvolumes = opt_get_num_answers(params->volume);
+    nisosurf = opt_get_num_answers(params->isosurf_level);
+    nslices = opt_get_num_answers(params->slice);
+
+    /* isosurface transparency */
+    nmaps = opt_get_num_answers(params->isosurf_transp_map);
+    nconsts = opt_get_num_answers(params->isosurf_transp_const);
+
+    if ((nmaps + nconsts > 0) && (nisosurf != nmaps + nconsts))
+	G_fatal_error(_("Inconsistent number of attributes (<%s> %d: <%s> %d, <%s> %d"),
+		      params->isosurf_level->key, nisosurf, params->isosurf_transp_map->key, nmaps,
+		      params->isosurf_transp_const->key, nconsts);
+
+    /* isosurface shininess */
+    nmaps = opt_get_num_answers(params->isosurf_shine_map);
+    nconsts = opt_get_num_answers(params->isosurf_shine_const);
+
+    if ((nmaps + nconsts > 0) && (nisosurf != nmaps + nconsts))
+	G_fatal_error(_("Inconsistent number of attributes (<%s> %d: <%s> %d, <%s> %d"),
+			params->isosurf_level->key, nisosurf, params->isosurf_shine_map->key, nmaps,
+			params->isosurf_shine_const->key, nconsts);
+
+    /* slice transparency */
+    nconsts = opt_get_num_answers(params->slice_transp);
+    if (nslices > 0 && nslices != nconsts)
+	G_fatal_error(_("Inconsistent number of attributes (<%s> %d: <%s> %d)"),
+			params->slice->key, nslices, params->slice_transp->key, nconsts);
+
+    /* slice position */
+    ncoords = opt_get_num_answers(params->slice_pos);
+    if (nslices > 0 && ncoords != 6 * nslices)
+	G_fatal_error(_("Inconsistent number of attributes (<%s> %d: <%s> %d x 6)"),
+			  params->slice->key, nslices, params->slice_pos->key, ncoords/6);
+
     return;
 }
 
@@ -783,7 +1041,7 @@
 		 const char *elev_map, const char *elev_const,
 		 const char *map_name, const char *const_name)
 {
-    if ((nmaps > 0 && nelevs != nmaps) || (nconsts > 0 && nelevs != nconsts))
+    if ((nmaps + nconsts > 0) && (nelevs != nmaps + nconsts))
 	G_fatal_error(_("Inconsistent number of attributes (<%s/%s> %d: <%s> %d, <%s> %d"),
 		      elev_map, elev_const, nelevs, map_name, nmaps,
 		      const_name, nconsts);

Added: grass/branches/develbranch_6/misc/m.nviz.image/cplane.c
===================================================================
--- grass/branches/develbranch_6/misc/m.nviz.image/cplane.c	                        (rev 0)
+++ grass/branches/develbranch_6/misc/m.nviz.image/cplane.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -0,0 +1,72 @@
+/*!
+   \file cplane.c
+
+   \brief Cutting plane subroutine
+
+   (C) 2011 by the GRASS Development Team
+
+   This program is free software under the GNU General Public
+   License (>=v2). Read the file COPYING that comes with GRASS
+   for details.
+
+   \author Anna Kratochvilova <kratochanna gmail.com> (Google SoC 2010/2011)
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grass/glocale.h>
+
+#include "local_proto.h"
+
+/*!
+   \brief Draw cutting planes and set their attributes
+
+   \param params module parameters
+   \param data nviz data
+ */
+void draw_cplane(const struct GParams *params, nv_data * data)
+{
+    int i, id, ncplanes;
+    float trans_x, trans_y, trans_z;
+    float rot_x, rot_y, rot_z;
+    int fence;
+
+    ncplanes = opt_get_num_answers(params->cplane);
+    for (i = 0; i < ncplanes; i++) {
+	id = atoi(params->cplane->answers[i]);
+
+	if (id < 0 || id > Nviz_num_cplanes(data))
+	    G_fatal_error(_("Cutting plane number <%d> not found"), id);
+
+	Nviz_on_cplane(data, id);
+
+	trans_x = atof(params->cplane_pos->answers[i * 3 + 0]);
+	trans_y = atof(params->cplane_pos->answers[i * 3 + 1]);
+	trans_z = atof(params->cplane_pos->answers[i * 3 + 2]);
+	Nviz_set_cplane_translation(data, id, trans_x, trans_y, trans_z);
+
+	rot_x = 0;
+	rot_y = atof(params->cplane_tilt->answers[i]);
+	rot_z = atof(params->cplane_rot->answers[i]);
+	Nviz_set_cplane_rotation(data, id, rot_x, rot_y, rot_z);
+    }
+
+    const char *shading = params->cplane_shading->answers[0];
+
+    if (strcmp(shading, "clear") == 0)
+	fence = 0;
+    else if (strcmp(shading, "top") == 0)
+	fence = 1;
+    else if (strcmp(shading, "bottom") == 0)
+	fence = 2;
+    else if (strcmp(shading, "blend") == 0)
+	fence = 3;
+    else if (strcmp(shading, "shaded") == 0)
+	fence = 4;
+    else
+	fence = 0;
+    Nviz_set_fence_color(data, fence);
+
+    return;
+}

Modified: grass/branches/develbranch_6/misc/m.nviz.image/description.html
===================================================================
--- grass/branches/develbranch_6/misc/m.nviz.image/description.html	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/misc/m.nviz.image/description.html	2011-11-25 15:10:47 UTC (rev 49357)
@@ -23,7 +23,13 @@
 
 <h2>AUTHOR</h2>
 
-Martin Landa (Google Summer of Code 2008/2010)
+<a href="http://geo.fsv.cvut.cz/gwiki/Landa">Martin
+Landa</a>, <a href="http://grass.osgeo.org/wiki/WxNviz_GSoC_2008">Google
+Summer of Code 2008</a> (mentor: Michael Barton)
+and <a href="http://grass.osgeo.org/wiki/WxNviz_GSoC_2010">Google
+Summer of Code 2010</a> (mentor: Helena Mitasova)<br>
+Anna Kratochvilova, <a href="http://grass.osgeo.org/wiki/WxNviz_GSoC_2011">Google
+Summer of Code 2011</a> (mentor: Martin Landa)
 
 <p>
 <i>Last changed: $Date$</i>

Modified: grass/branches/develbranch_6/misc/m.nviz.image/local_proto.h
===================================================================
--- grass/branches/develbranch_6/misc/m.nviz.image/local_proto.h	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/misc/m.nviz.image/local_proto.h	2011-11-25 15:10:47 UTC (rev 49357)
@@ -21,16 +21,22 @@
 	*vpoints, *vpoint_size, *vpoint_marker, *vpoint_color, *vpoint_width, *vpoint_pos,
     /* volumes */
 	*volume, *volume_mode, *volume_shade, *volume_pos, *volume_res, *isosurf_level,
+	*isosurf_color_map, *isosurf_color_const, *isosurf_transp_map, *isosurf_transp_const,
+	*isosurf_shine_map, *isosurf_shine_const, *slice_pos, *slice, *slice_transp,
     /* misc */
 	*exag, *bgcolor,
+    /* cutting planes */
+	*cplane, *cplane_pos, *cplane_rot, *cplane_tilt, *cplane_shading,
     /* viewpoint */
-	*pos, *height, *persp, *twist,
+	*pos, *height, *persp, *twist, *focus,
     /* output */
 	*output, *format, *size,
     /* lighting */
 	*light_pos, *light_color, *light_bright, *light_ambient,
     /* fringe */
-	*fringe, *fringe_color, *fringe_elev;
+	*fringe, *fringe_color, *fringe_elev,
+    /* north arrow */
+	*north_arrow, *north_arrow_size, *north_arrow_color;
 };
 
 /* args.c */
@@ -52,7 +58,11 @@
 /* volume.c */
 int load_rasters3d(const struct GParams *, nv_data *);
 int add_isosurfs(const struct GParams *, nv_data *);
+int add_slices(const struct GParams *, nv_data *);
 
+/* cutting planes */
+void draw_cplane(const struct GParams *, nv_data *);
+
 /* write_img.c */
 int write_img(const char *, int);
 

Modified: grass/branches/develbranch_6/misc/m.nviz.image/main.c
===================================================================
--- grass/branches/develbranch_6/misc/m.nviz.image/main.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/misc/m.nviz.image/main.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -34,6 +34,7 @@
 
     int i, ret;
     int red, grn, blu;
+    float size;
     double vp_height, z_exag;	/* calculated viewpoint height, z-exag */
     int width, height;		/* output image size */
     char *output_name;
@@ -116,6 +117,11 @@
 	add_isosurfs(params, &data);
     }
 
+    /* define slices for displaying volumes */
+    if (params->slice->answer) {
+	add_slices(params, &data);
+    }
+
     /* focus on loaded data */
     Nviz_set_focus_map(MAP_OBJ_UNDEFINED, -1);
 
@@ -147,6 +153,11 @@
 				atof(params->pos->answers[1]));
     Nviz_set_viewpoint_twist(atoi(params->twist->answer));
     Nviz_set_viewpoint_persp(atoi(params->persp->answer));
+    if (params->focus->answer) {
+    Nviz_set_focus(&data, atof(params->focus->answers[0]),
+						  atof(params->focus->answers[1]), 
+						  atof(params->focus->answers[2]));
+    }
 
     /* set lights */
     Nviz_set_light_position(&data, 1,
@@ -183,11 +194,28 @@
 	Nviz_new_fringe(&data, -1, Nviz_color_from_str(params->fringe_color->answer),
 			atof(params->fringe_elev->answer), nw, ne, sw, se);
     }
-    
+
+/* draw north arrow */
+    if (params->north_arrow->answer) {
+	
+	if (!params->north_arrow_size->answer)
+	    size = Nviz_get_longdim(&data) / 8.;
+	else
+	    size = atof(params->north_arrow_size->answer);
+
+	Nviz_set_arrow(&data, atoi(params->north_arrow->answers[0]),
+			      atoi(params->north_arrow->answers[1]),
+			      size, Nviz_color_from_str(params->north_arrow_color->answer));
+	Nviz_draw_arrow(&data);
+    }
+
     GS_clear(data.bgcolor);
 
+    /* cutting planes */
+    if(params->cplane->answer)
+        draw_cplane(params, &data);
+
     /* draw */
-    Nviz_draw_cplane(&data, -1, -1);
     Nviz_draw_all(&data);
 
     /* write to image */

Modified: grass/branches/develbranch_6/misc/m.nviz.image/surface.c
===================================================================
--- grass/branches/develbranch_6/misc/m.nviz.image/surface.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/misc/m.nviz.image/surface.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -40,10 +40,7 @@
     nelev_map = opt_get_num_answers(params->elev_map);
     nelev_const = opt_get_num_answers(params->elev_const);
 
-    if (nelev_map > 0)
-	nelevs = nelev_map;
-    else
-	nelevs = nelev_const;
+    nelevs = nelev_const + nelev_map;
 
     /* topography (required) */
     for (i = 0; i < nelevs; i++) {
@@ -61,10 +58,10 @@
 				  0.0, data);
 	}
 	else {
-	    if (i < nelev_const && strcmp(params->elev_const->answers[i], "")) {
+	    if (i-nelev_map < nelev_const && strcmp(params->elev_const->answers[i-nelev_map], "")) {
 		id = Nviz_new_map_obj(MAP_OBJ_SURF,
 				      NULL,
-				      atof(params->elev_const->answers[i]),
+				      atof(params->elev_const->answers[i-nelev_map]),
 				      data);
 	    }
 	    else {
@@ -74,9 +71,16 @@
 	}
 
 	/* set position */
-	x = atof(params->surface_pos->answers[i]);
-	y = atof(params->surface_pos->answers[i+1]);
-	z = atof(params->surface_pos->answers[i+2]);
+    if (opt_get_num_answers(params->surface_pos) != 3 * nelevs){
+        x = atof(params->surface_pos->answers[0]);
+        y = atof(params->surface_pos->answers[1]);
+        z = atof(params->surface_pos->answers[2]);
+    }
+    else {
+        x = atof(params->surface_pos->answers[i*3+0]);
+        y = atof(params->surface_pos->answers[i*3+1]);
+        z = atof(params->surface_pos->answers[i*3+2]);
+    }
 
 	GS_set_trans(id, x, y, z);
     }
@@ -111,19 +115,25 @@
 			  data);
 	}
 	/* check for color value */
-	else if (i < ncolor_const &&
-		 strcmp(params->color_const->answers[i], "")) {
+	else if (i-ncolor_map < ncolor_const &&
+		 strcmp(params->color_const->answers[i-ncolor_map], "")) {
 	    Nviz_set_attr(id, MAP_OBJ_SURF, ATT_COLOR, CONST_ATT, NULL,
 			  Nviz_color_from_str(params->color_const->
-					      answers[i]), data);
+					      answers[i-ncolor_map]), data);
 	}
 	else {			/* use by default elevation map for coloring */
-	    Nviz_set_attr(id, MAP_OBJ_SURF, ATT_COLOR, MAP_ATT,
-			  G_fully_qualified_name(params->elev_map->answers[i],
-						 mapset), -1.0, data);
-	    G_verbose_message(_("Color attribute not defined, using default <%s>"),
-			      G_fully_qualified_name(params->elev_map->
-						     answers[i], mapset));
+	    if (nelev_map > 0){
+	        Nviz_set_attr(id, MAP_OBJ_SURF, ATT_COLOR, MAP_ATT,
+			      G_fully_qualified_name(params->elev_map->answers[i],
+						     mapset), -1.0, data);
+	        G_verbose_message(_("Color attribute not defined, using default <%s>"),
+				  G_fully_qualified_name(params->elev_map->
+							 answers[i], mapset));
+        }
+        else{
+            G_fatal_error(_("Missing color attribute for surface %d"),
+			      i + 1);
+        }
 	}
 	/* mask */
 	if (i < nmask_map && strcmp(params->mask_map->answers[i], "")) {
@@ -139,10 +149,10 @@
 						 answers[i], mapset), -1.0,
 			  data);
 	}
-	else if (i < ntransp_const &&
-		 strcmp(params->transp_const->answers[i], "")) {
+	else if (i-ntransp_map < ntransp_const &&
+		 strcmp(params->transp_const->answers[i-ntransp_map], "")) {
 	    Nviz_set_attr(id, MAP_OBJ_SURF, ATT_TRANSP, CONST_ATT, NULL,
-			  atof(params->transp_const->answers[i]), data);
+			  atof(params->transp_const->answers[i-ntransp_map]), data);
 	}
 
 	/* shininess */
@@ -152,10 +162,10 @@
 						 answers[i], mapset), -1.0,
 			  data);
 	}
-	else if (i < nshine_const &&
-		 strcmp(params->shine_const->answers[i], "")) {
+	else if (i-nshine_map < nshine_const &&
+		 strcmp(params->shine_const->answers[i-nshine_map], "")) {
 	    Nviz_set_attr(id, MAP_OBJ_SURF, ATT_SHINE, CONST_ATT, NULL,
-			  atof(params->shine_const->answers[i]), data);
+			  atof(params->shine_const->answers[i-nshine_map]), data);
 	}
 
 	/* emission */
@@ -164,10 +174,10 @@
 			  G_fully_qualified_name(params->emit_map->answers[i],
 						 mapset), -1.0, data);
 	}
-	else if (i < nemit_const &&
-		 strcmp(params->emit_const->answers[i], "")) {
+	else if (i-nemit_map < nemit_const &&
+		 strcmp(params->emit_const->answers[i-nemit_map], "")) {
 	    Nviz_set_attr(id, MAP_OBJ_SURF, ATT_EMIT, CONST_ATT, NULL,
-			  atof(params->emit_const->answers[i]), data);
+			  atof(params->emit_const->answers[i-nemit_map]), data);
 	}
 
 	/*
@@ -229,7 +239,7 @@
 	}
 
 	/* style */
-	if (strcmp(params->style->answers[i], "wire") == 0) {
+	if (strcmp(style, "wire") == 0) {
 	    draw_mode |= DM_GRID_WIRE;
 	}
 	else {			/* surface */
@@ -237,7 +247,7 @@
 	}
 
 	/* shading */
-	if (strcmp(params->shade->answers[i], "flat") == 0) {
+	if (strcmp(shade, "flat") == 0) {
 	    draw_mode |= DM_FLAT;
 	}
 	else {			/* gouraud */

Modified: grass/branches/develbranch_6/misc/m.nviz.image/vector.c
===================================================================
--- grass/branches/develbranch_6/misc/m.nviz.image/vector.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/misc/m.nviz.image/vector.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -85,9 +85,9 @@
 			      0.0, data);
 
 	/* set position */
-	x = atof(position->answers[i]);
-	y = atof(position->answers[i+1]);
-	z = atof(position->answers[i+2]);
+	x = atof(position->answers[i*3+0]);
+	y = atof(position->answers[i*3+1]);
+	z = atof(position->answers[i*3+2]);
 
 	if (map_obj_type == MAP_OBJ_VECT)
 	    GV_set_trans(id, x, y, z);

Modified: grass/branches/develbranch_6/misc/m.nviz.image/volume.c
===================================================================
--- grass/branches/develbranch_6/misc/m.nviz.image/volume.c	2011-11-25 14:58:21 UTC (rev 49356)
+++ grass/branches/develbranch_6/misc/m.nviz.image/volume.c	2011-11-25 15:10:47 UTC (rev 49357)
@@ -13,6 +13,7 @@
 */
 
 #include <stdlib.h>
+#include <string.h>
 
 #include <grass/G3d.h>
 #include <grass/glocale.h>
@@ -29,7 +30,8 @@
 */
 int load_rasters3d(const struct GParams *params, nv_data *data)
 {
-  int i, nvol, id;
+    int i, nvol, id;
+    float x, y, z;
     char *mapset;
     
     nvol = opt_get_num_answers(params->volume);
@@ -45,6 +47,20 @@
 			      G_fully_qualified_name(params->volume->answers[i],
 						     mapset),
 			      0.0, data);
+    
+	/* set position */
+	if (opt_get_num_answers(params->volume_pos) != 3 * nvol) {
+	    x = atof(params->volume_pos->answers[0]);
+	    y = atof(params->volume_pos->answers[1]);
+	    z = atof(params->volume_pos->answers[2]);
+	}
+	else {
+	    x = atof(params->volume_pos->answers[i*3+0]);
+	    y = atof(params->volume_pos->answers[i*3+1]);
+	    z = atof(params->volume_pos->answers[i*3+2]);
+	}
+    
+	GVL_set_trans(id, x, y, z);
     }
 
     return 1;
@@ -61,8 +77,12 @@
 int add_isosurfs(const struct GParams *params, nv_data *data)
 {
     int i;
-    int num, level, nvols, *vol_list, id, nisosurfs;
+    float level;
+    int num, nvols, *vol_list, id, nisosurfs;
+    int ncolor_map, ncolor_const, ntransp_map, ntransp_const, nshine_map, nshine_const;
+    int res, draw_mode;
     char **tokens;
+    const char *mapset, *style;
     
     vol_list = GVL_get_vol_list(&nvols);
 
@@ -72,7 +92,7 @@
 	    G_fatal_error(_("Error tokenize '%s'"), 
 			  params->isosurf_level->answers[i]);
 	num = atoi(tokens[0]);
-	level = atoi(tokens[1]);
+	level = atof(tokens[1]);
 	G_free_tokens(tokens);
 
 	if (num > nvols) {
@@ -95,11 +115,192 @@
 	}
 
 	/* color */
-	if (GVL_isosurf_set_att_map(id, nisosurfs-1, ATT_COLOR, params->volume->answers[0]) < 0) {
-	    G_fatal_error(_("Unable to set isosurface (%d) attribute (%d) of volume %d"),
-			  nisosurfs-1, ATT_COLOR, id);
+	ncolor_map = opt_get_num_answers(params->isosurf_color_map);
+	ncolor_const = opt_get_num_answers(params->isosurf_color_const);
+
+	if (i < ncolor_map && strcmp(params->isosurf_color_map->answers[i], "")) {
+	    mapset = G_find_grid3(params->isosurf_color_map->answers[i], "");
+
+	    if (mapset == NULL) {
+		G_fatal_error(_("3d raster map <%s> not found"),
+			      params->isosurf_color_map->answers[i]);
+	    }
+
+	    if (GVL_isosurf_set_att_map(id, nisosurfs-1, ATT_COLOR, 
+					params->isosurf_color_map->answers[i]) < 0)
+		G_fatal_error(_("Unable to set isosurface (%d) attribute (%d) of volume %d"),
+				nisosurfs-1, ATT_COLOR, id);
 	}
+	else if (i-ncolor_map < ncolor_const &&
+		 strcmp(params->isosurf_color_const->answers[i-ncolor_map], "")) {
+
+	    if (GVL_isosurf_set_att_const(id, nisosurfs-1, ATT_COLOR,
+			    Nviz_color_from_str(params->isosurf_color_const->answers[i-ncolor_map])) < 0)
+		G_fatal_error(_("Unable to set isosurface (%d) attribute (%d) of volume %d"),
+				nisosurfs-1, ATT_COLOR, id);
+	}
+	else {			/* use by default 3d raster map for coloring */
+	    GVL_isosurf_set_att_map(id, nisosurfs-1, ATT_COLOR, params->volume->answers[num-1]);
+	    G_verbose_message(_("Color attribute not defined, using default <%s>"),
+				params->volume->answers[num-1]);
+	}
+
+	/* transparency */
+	ntransp_map = opt_get_num_answers(params->isosurf_transp_map);
+	ntransp_const = opt_get_num_answers(params->isosurf_transp_const);
+
+	if (i < ntransp_map && strcmp(params->isosurf_transp_map->answers[i], "")) {
+	    if (GVL_isosurf_set_att_map(id, nisosurfs-1, ATT_TRANSP, 
+					params->isosurf_transp_map->answers[i]) < 0)
+		G_fatal_error(_("Unable to set isosurface (%d) attribute (%d) of volume %d"),
+				nisosurfs-1, ATT_TRANSP, id);
+	}
+	else if (i-ntransp_map < ntransp_const &&
+		 strcmp(params->isosurf_transp_const->answers[i-ntransp_map], "")) {
+	    if (GVL_isosurf_set_att_const(id, nisosurfs-1, ATT_TRANSP,
+					  atof(params->isosurf_transp_const->answers[i-ntransp_map])) < 0)
+		G_fatal_error(_("Unable to set isosurface (%d) attribute (%d) of volume %d"),
+				nisosurfs-1, ATT_TRANSP, id);
+	}
+
+	/* shine */
+	nshine_map = opt_get_num_answers(params->isosurf_shine_map);
+	nshine_const = opt_get_num_answers(params->isosurf_shine_const);
+
+	if (i < nshine_map && strcmp(params->isosurf_shine_map->answers[i], "")) {
+	    if (GVL_isosurf_set_att_map(id, nisosurfs-1, ATT_SHINE, 
+					params->isosurf_shine_map->answers[i]) < 0)
+		G_fatal_error(_("Unable to set isosurface (%d) attribute (%d) of volume %d"),
+				nisosurfs-1, ATT_SHINE, id);
+	}
+	else if (i-nshine_map < nshine_const &&
+		 strcmp(params->isosurf_shine_const->answers[i-nshine_map], "")) {
+	    if (GVL_isosurf_set_att_const(id, nisosurfs-1, ATT_SHINE,
+					  atof(params->isosurf_shine_const->answers[i-nshine_map])) < 0)
+		G_fatal_error(_("Unable to set isosurface (%d) attribute (%d) of volume %d"),
+				nisosurfs-1, ATT_SHINE, id);
+	}
     }
-    
+
+        /* set draw resolution and shading after isosurfaces are added*/
+    for (i = 0; i < nvols; i++) {
+
+	id = vol_list[i];
+	/* set resolution */
+	if (opt_get_num_answers(params->volume_res) != nvols)
+	    res = atof(params->volume_res->answers[0]);
+	else
+	    res = atof(params->volume_res->answers[i]);
+
+	GVL_isosurf_set_drawres(id, res, res, res);
+
+	/* set shading */
+	if (opt_get_num_answers(params->volume_shade) != nvols)
+	    style = params->volume_shade->answers[0];
+	else
+	    style = params->volume_shade->answers[i];
+
+	draw_mode = 0;
+
+	if (strcmp(style, "flat") == 0) {
+	    draw_mode |= DM_FLAT;
+	}
+	else {
+	    draw_mode |= DM_GOURAUD;
+	}
+	
+	GVL_isosurf_set_drawmode(id, draw_mode);
+    }
+
     return 1;
 }
+
+int add_slices(const struct GParams *params, nv_data *data)
+{
+    int i;
+    int num, nvols, *vol_list, id, nslices, axis;
+    int res, draw_mode;
+    char **tokens;
+    const char* style;
+
+    vol_list = GVL_get_vol_list(&nvols);
+
+    for (i = 0; params->slice->answers[i]; i++) {
+	tokens = G_tokenize(params->slice->answers[i], ":");
+	if (G_number_of_tokens(tokens) != 2) 
+	    G_fatal_error(_("Error tokenize '%s'"), 
+			  params->slice->answers[i]);
+	num = atoi(tokens[0]);
+
+	if (!strcmp(tokens[1],"x") || !strcmp(tokens[1],"X"))
+	    axis = 0;
+	else if (!strcmp(tokens[1],"y") || !strcmp(tokens[1],"Y"))
+	    axis = 1;
+	else if (!strcmp(tokens[1],"z") || !strcmp(tokens[1],"Z"))
+	    axis = 2;
+	else
+	    G_fatal_error(_("Wrong name for axis: %s"), 
+			  tokens[1]);
+	G_free_tokens(tokens);
+
+	if (num > nvols) {
+	    G_fatal_error(_("Volume set number %d is not available"), 
+			  num);
+	}
+
+	id = vol_list[num-1];
+	if (GVL_slice_add(id) < 0) {
+	    G_fatal_error(_("Unable to add slice (volume set %d)"),
+			  id);
+	}
+
+	nslices = GVL_slice_num_slices(id);
+
+	if (GVL_slice_set_pos(id, nslices-1, atof(params->slice_pos->answers[i*6+0]),
+					     atof(params->slice_pos->answers[i*6+1]),
+					     atof(params->slice_pos->answers[i*6+2]),
+					     atof(params->slice_pos->answers[i*6+3]),
+					     atof(params->slice_pos->answers[i*6+4]),
+					     atof(params->slice_pos->answers[i*6+5]),
+					     axis) < 0) 
+	    G_fatal_error(_("Unable to set slice (%d) position of volume %d"),
+			  nslices-1, id);
+
+	/* set transparency */
+	if (GVL_slice_set_transp(id, nslices-1, atoi(params->slice_transp->answers[i])) < 0)
+	    G_fatal_error(_("Unable to set slice (%d) transparency of volume %d"),
+			  nslices-1, id);
+    }
+
+    /* set draw resolution and shading after slices are added*/
+    for (i = 0; i < nvols; i++) {
+
+	id = vol_list[i];
+	/* set resolution */
+	if (opt_get_num_answers(params->volume_res) != nvols)
+	    res = atof(params->volume_res->answers[0]);
+	else
+	    res = atof(params->volume_res->answers[i]);
+
+	GVL_slice_set_drawres(id, res, res, res);
+
+	/* set shading */
+	if (opt_get_num_answers(params->volume_shade) != nvols)
+	    style = params->volume_shade->answers[0];
+	else
+	    style = params->volume_shade->answers[i];
+
+	draw_mode = 0;
+
+	if (strcmp(style, "flat") == 0) {
+	    draw_mode |= DM_FLAT;
+	}
+	else {
+	    draw_mode |= DM_GOURAUD;
+	}
+
+	GVL_slice_set_drawmode(id, draw_mode);
+    }
+
+    return 1;
+}



More information about the grass-commit mailing list