Index: /trunk/gui/apps/driver
===================================================================
--- /trunk/gui/apps/driver	(revision 16)
+++ /trunk/gui/apps/driver	(revision 17)
@@ -44,4 +44,13 @@
 option add *TemperatureGauge.textBackground white
 option add *Switch.textBackground white
+
+switch $tcl_platform(platform) {
+    unix - windows {
+        event add <<PopupMenu>> <ButtonPress-3>
+    }
+    macintosh {
+        event add <<PopupMenu>> <Control-ButtonPress-1>
+    }
+}
 
 #
Index: /trunk/gui/scripts/analyzer.tcl
===================================================================
--- /trunk/gui/scripts/analyzer.tcl	(revision 16)
+++ /trunk/gui/scripts/analyzer.tcl	(revision 17)
@@ -379,5 +379,8 @@
         }
 
-        if {"" != $label} {
+        set hidden [$xmlobj get output.$item.hide]
+        set hidden [expr {"" != $hidden && $hidden}]
+
+        if {"" != $label && !$hidden} {
             set haveresults 1
         }
@@ -396,5 +399,8 @@
             }
 
-            if {"" != $label} {
+            set hidden [$xmlobj get output.$item.hide]
+            set hidden [expr {"" != $hidden && $hidden}]
+
+            if {"" != $label && !$hidden} {
                 if {![info exists _label2page($label)]} {
                     set name "page[incr _pages]"
Index: /trunk/gui/scripts/cloud.tcl
===================================================================
--- /trunk/gui/scripts/cloud.tcl	(revision 16)
+++ /trunk/gui/scripts/cloud.tcl	(revision 17)
@@ -20,4 +20,5 @@
 
     public method points {}
+    public method mesh {}
     public method size {}
     public method dimensions {}
@@ -156,4 +157,13 @@
 
 # ----------------------------------------------------------------------
+# USAGE: mesh
+#
+# Returns the vtk object representing the mesh.
+# ----------------------------------------------------------------------
+itcl::body Rappture::Cloud::mesh {} {
+    return $this-points
+}
+
+# ----------------------------------------------------------------------
 # USAGE: size
 #
@@ -170,10 +180,7 @@
 # ----------------------------------------------------------------------
 itcl::body Rappture::Cloud::dimensions {} {
-puts "dimensions for cloud [$_cloud element -as id]"
-puts [string range [$_cloud xml] 0 300]
     # count the dimensions with real limits
     set dims 0
     foreach d {x y z} {
-puts "checking dims: $d => $_limits(${d}min) $_limits(${d}max)"
         if {$_limits(${d}min) != $_limits(${d}max)} {
             incr dims
Index: /trunk/gui/scripts/contourresult.tcl
===================================================================
--- /trunk/gui/scripts/contourresult.tcl	(revision 16)
+++ /trunk/gui/scripts/contourresult.tcl	(revision 17)
@@ -86,11 +86,14 @@
 
     protected method _rebuild {}
+    protected method _clear {}
     protected method _zoom {option}
     protected method _move {option x y}
     protected method _slice {option args}
+    protected method _3dView {theta phi}
     protected method _fixLimits {}
     protected method _color2rgb {color}
 
     private variable _dlist ""     ;# list of data objects
+    private variable _dims ""      ;# dimensionality of data objects
     private variable _obj2color    ;# maps dataobj => plotting color
     private variable _obj2width    ;# maps dataobj => line width
@@ -98,7 +101,9 @@
     private variable _obj2vtk      ;# maps dataobj => vtk objects
     private variable _actors       ;# list of actors for each renderer
+    private variable _lights       ;# list of lights for each renderer
     private variable _click        ;# info used for _move operations
     private variable _slicer       ;# vtk transform used for 3D slice plane
     private variable _limits       ;# autoscale min/max for all axes
+    private variable _view         ;# view params for 3D view
 
     private common _counter 0      ;# used for auto-generated names
@@ -117,6 +122,8 @@
 
     set _slicer(axis) ""
-    set _slicer(xform) ""
+    set _slicer(plane) ""
     set _slicer(readout) ""
+    set _view(theta) 0
+    set _view(phi) 0
 
     itk_component add controls {
@@ -263,4 +270,7 @@
 # ----------------------------------------------------------------------
 itcl::body Rappture::ContourResult::destructor {} {
+    _clear
+    after cancel [itcl::code $this _rebuild]
+
     rename $this-renWin ""
     rename $this-ren ""
@@ -270,6 +280,4 @@
     rename $this-ren2 ""
     rename $this-iren2 ""
-
-    after cancel [itcl::code $this _rebuild]
 }
 
@@ -402,20 +410,5 @@
 # ----------------------------------------------------------------------
 itcl::body Rappture::ContourResult::_rebuild {} {
-    # clear out any old constructs
-    foreach ren [array names _actors] {
-        foreach actor $_actors($ren) {
-            $ren RemoveActor $actor
-        }
-        set _actors($ren) ""
-    }
-    foreach dataobj [array names _obj2vtk] {
-        foreach cmd $_obj2vtk($dataobj) {
-            rename $cmd ""
-        }
-        set _obj2vtk($dataobj) ""
-    }
-    set _slicer(axis) ""
-    set _slicer(xform) ""
-    set _slicer(readout) ""
+    _clear
 
     # determine the dimensionality from the topmost (raised) object
@@ -423,7 +416,7 @@
     set dataobj [lindex $dlist end]
     if {$dataobj != ""} {
-        set dims [$dataobj components -dimensions]
+        set _dims [$dataobj components -dimensions]
     } else {
-        set dims "0D"
+        set _dims "0D"
     }
 
@@ -432,9 +425,7 @@
     foreach dataobj [get] {
         foreach comp [$dataobj components] {
-            set pd $this-polydata$_counter
-            vtkPolyData $pd
-            $pd SetPoints [$dataobj mesh $comp]
-            [$pd GetPointData] SetScalars [$dataobj values $comp]
-
+            #
+            # LOOKUP TABLE FOR COLOR CONTOURS
+            #
             # use vmin/vmax if possible, otherwise get from data
             if {$_limits(vmin) == "" || $_limits(vmax) == ""} {
@@ -445,9 +436,4 @@
             }
 
-            set tr $this-triangles$_counter
-            vtkDelaunay$dims $tr
-            $tr SetInput $pd
-            $tr SetTolerance 0.0000000000001
-
             set lu $this-lookup$_counter
             vtkLookupTable $lu
@@ -456,5 +442,5 @@
             $lu Build
 
-            lappend _obj2vtk($dataobj) $pd $tr $lu
+            lappend _obj2vtk($dataobj) $lu
 
             #
@@ -462,5 +448,5 @@
             #
             if {$_counter == 0} {
-                if {$dims == "3D"} {
+                if {$_dims == "3D"} {
                     pack $itk_component(slicer) -side bottom -padx 4 -pady 4
                     pack $itk_component(zslice) -side bottom -padx 4 -pady 4
@@ -469,32 +455,125 @@
 
                     #
+                    # 3D LIGHTS (on both sides of all three axes)
+                    #
+                    set xm [expr {0.5*($_limits(xmax)+$_limits(xmin))}]
+                    set ym [expr {0.5*($_limits(ymax)+$_limits(ymin))}]
+                    set zm [expr {0.5*($_limits(zmax)+$_limits(zmin))}]
+                    set xr [expr {$_limits(xmax)-$_limits(xmin)}]
+                    set yr [expr {$_limits(ymax)-$_limits(ymin)}]
+                    set zr [expr {$_limits(zmax)-$_limits(zmin)}]
+
+                    set lt $this-lightxm$_counter
+                    vtkLight $lt
+                    $lt SetColor 1 1 1
+                    $lt SetAttenuationValues 0 0 0
+                    $lt SetFocalPoint $xm $ym $zm
+                    $lt SetPosition [expr {$xm-$xr}] 0 0
+                    $this-ren AddLight $lt
+                    lappend _lights($this-ren) $lt
+
+                    set lt $this-lightxp$_counter
+                    vtkLight $lt
+                    $lt SetColor 1 1 1
+                    $lt SetAttenuationValues 0 0 0
+                    $lt SetFocalPoint $xm $ym $zm
+                    $lt SetPosition [expr {$xm+$xr}] 0 0
+                    $this-ren AddLight $lt
+                    lappend _lights($this-ren) $lt
+
+                    set lt $this-lightym$_counter
+                    vtkLight $lt
+                    $lt SetColor 1 1 1
+                    $lt SetAttenuationValues 0 0 0
+                    $lt SetFocalPoint $xm $ym $zm
+                    $lt SetPosition 0 [expr {$ym-$yr}] 0
+                    $this-ren AddLight $lt
+                    lappend _lights($this-ren) $lt
+
+                    set lt $this-lightyp$_counter
+                    vtkLight $lt
+                    $lt SetColor 1 1 1
+                    $lt SetAttenuationValues 0 0 0
+                    $lt SetFocalPoint $xm $ym $zm
+                    $lt SetPosition 0 [expr {$ym+$yr}] 0
+                    $this-ren AddLight $lt
+                    lappend _lights($this-ren) $lt
+
+                    set lt $this-lightzm$_counter
+                    vtkLight $lt
+                    $lt SetColor 1 1 1
+                    $lt SetAttenuationValues 0 0 0
+                    $lt SetFocalPoint $xm $ym $zm
+                    $lt SetPosition 0 0 [expr {$zm-$zr}]
+                    $this-ren AddLight $lt
+                    lappend _lights($this-ren) $lt
+
+                    set lt $this-lightzp$_counter
+                    vtkLight $lt
+                    $lt SetColor 1 1 1
+                    $lt SetAttenuationValues 0 0 0
+                    $lt SetFocalPoint $xm $ym $zm
+                    $lt SetPosition 0 0 [expr {$zm+$zr}]
+                    $this-ren AddLight $lt
+                    lappend _lights($this-ren) $lt
+
+                    #
+                    # 3D DATA SET
+                    #
+                    set mesh [$dataobj mesh $comp]
+                    switch -- [$mesh GetClassName] {
+                      vtkPoints {
+                        # handle cloud of 3D points
+                        set pd $this-polydata$_counter
+                        vtkPolyData $pd
+                        $pd SetPoints $mesh
+                        [$pd GetPointData] SetScalars [$dataobj values $comp]
+
+                        set tr $this-triangles$_counter
+                        vtkDelaunay3D $tr
+                        $tr SetInput $pd
+                        $tr SetTolerance 0.0000000000001
+                        set source [$tr GetOutput]
+
+                        set mp $this-mapper$_counter
+                        vtkPolyDataMapper $mp
+
+                        lappend _obj2vtk($dataobj) $pd $tr $mp
+                      }
+                      vtkUnstructuredGrid {
+                        # handle 3D grid with connectivity
+                        set gr $this-grdata$_counter
+                        vtkUnstructuredGrid $gr
+                        $gr ShallowCopy $mesh
+                        [$gr GetPointData] SetScalars [$dataobj values $comp]
+                        set source $gr
+
+                        lappend _obj2vtk($dataobj) $gr
+                      }
+                      default {
+                        error "don't know how to handle [$mesh GetClassName] data"
+                      }
+                    }
+
+                    #
                     # 3D CUT PLANE
                     #
                     set pl $this-cutplane$_counter
-                    vtkPlaneSource $pl
-                    $pl SetResolution 50 50
-
-                    set xf $this-cpxform$_counter
-                    vtkTransform $xf
-                    set _slicer(xform) $xf
+                    vtkPlane $pl
+                    set _slicer(plane) $pl
                     _slice axis "z"
 
-                    set pdf $this-cpfilter$_counter
-                    vtkTransformPolyDataFilter $pdf
-                    $pdf SetInput [$pl GetOutput]
-                    $pdf SetTransform $xf
-
-                    set pb $this-cpprobe$_counter
-                    vtkProbeFilter $pb
-                    $pb SetInput [$pdf GetOutput]
-                    $pb SetSource [$tr GetOutput]
-
-                    lappend _obj2vtk($dataobj) $pl $xf $pdf $pb
-
-                    set mp $this-mapper$_counter
+                    set ct $this-cutter$_counter
+                    vtkCutter $ct
+                    $ct SetInput $source
+                    $ct SetCutFunction $pl
+
+                    set mp $this-cutmapper$_counter
                     vtkPolyDataMapper $mp
-                    $mp SetInput [$pb GetOutput]
+                    $mp SetInput [$ct GetOutput]
                     $mp SetScalarRange $v0 $v1
                     $mp SetLookupTable $lu
+
+                    lappend _obj2vtk($dataobj) $pl $ct $mp
 
                     set ac $this-actor$_counter
@@ -505,10 +584,9 @@
                     $this-ren AddActor $ac
                     lappend _actors($this-ren) $ac
-
-                    lappend _obj2vtk($dataobj) $mp $ac
+                    lappend _obj2vtk($dataobj) $ac
 
                     set olf $this-3dolfilter$_counter
                     vtkOutlineFilter $olf
-                    $olf SetInput [$tr GetOutput]
+                    $olf SetInput $source
 
                     set olm $this-3dolmapper$_counter
@@ -552,7 +630,18 @@
                     pack forget $itk_component(slicer)
 
+                    set pd $this-polydata$_counter
+                    vtkPolyData $pd
+                    $pd SetPoints [$dataobj mesh $comp]
+                    [$pd GetPointData] SetScalars [$dataobj values $comp]
+
+                    set tr $this-triangles$_counter
+                    vtkDelaunay2D $tr
+                    $tr SetInput $pd
+                    $tr SetTolerance 0.0000000000001
+                    set source [$tr GetOutput]
+
                     set mp $this-mapper$_counter
                     vtkPolyDataMapper $mp
-                    $mp SetInput [$tr GetOutput]
+                    $mp SetInput $source
                     $mp SetScalarRange $v0 $v1
                     $mp SetLookupTable $lu
@@ -566,5 +655,5 @@
                     lappend _actors($this-ren) $ac
 
-                    lappend _obj2vtk($dataobj) $mp $ac
+                    lappend _obj2vtk($dataobj) $pd $tr $mp $ac
                 }
             }
@@ -576,5 +665,5 @@
                 set cf $this-clfilter$_counter
                 vtkContourFilter $cf
-                $cf SetInput [$tr GetOutput]
+                $cf SetInput $source
                 $cf GenerateValues 20 $v0 $v1
 
@@ -601,5 +690,5 @@
             set olf $this-olfilter$_counter
             vtkOutlineFilter $olf
-            $olf SetInput [$tr GetOutput]
+            $olf SetInput $source
 
             set olm $this-olmapper$_counter
@@ -645,17 +734,44 @@
     _zoom reset
 
-    if {$dims == "3D"} {
-        # allow interactions in 3D
-        catch {blt::busy release $itk_component(area)}
-    } else {
-        # prevent interactions in 2D
-        blt::busy hold $itk_component(area) -cursor left_ptr
-        bind $itk_component(area)_Busy <ButtonPress> \
-            [itcl::code $this _move click %x %y]
-        bind $itk_component(area)_Busy <B1-Motion> \
-            [itcl::code $this _move drag %x %y]
-        bind $itk_component(area)_Busy <ButtonRelease> \
-            [itcl::code $this _move release %x %y]
-    }
+    # prevent interactions -- use our own
+    blt::busy hold $itk_component(area) -cursor left_ptr
+    bind $itk_component(area)_Busy <ButtonPress> \
+        [itcl::code $this _move click %x %y]
+    bind $itk_component(area)_Busy <B1-Motion> \
+        [itcl::code $this _move drag %x %y]
+    bind $itk_component(area)_Busy <ButtonRelease> \
+        [itcl::code $this _move release %x %y]
+}
+
+# ----------------------------------------------------------------------
+# USAGE: _clear
+#
+# Used internally to clear the drawing area and tear down all vtk
+# objects in the current scene.
+# ----------------------------------------------------------------------
+itcl::body Rappture::ContourResult::_clear {} {
+    # clear out any old constructs
+    foreach ren [array names _actors] {
+        foreach actor $_actors($ren) {
+            $ren RemoveActor $actor
+        }
+        set _actors($ren) ""
+    }
+    foreach ren [array names _lights] {
+        foreach light $_lights($ren) {
+            $ren RemoveLight $light
+            rename $light ""
+        }
+        set _lights($ren) ""
+    }
+    foreach dataobj [array names _obj2vtk] {
+        foreach cmd $_obj2vtk($dataobj) {
+            rename $cmd ""
+        }
+        set _obj2vtk($dataobj) ""
+    }
+    set _slicer(axis) ""
+    set _slicer(plane) ""
+    set _slicer(readout) ""
 }
 
@@ -671,16 +787,20 @@
     switch -- $option {
         in {
-            set camera [$this-ren GetActiveCamera]
-            set zoom [$camera Zoom 1.25]
+            [$this-ren GetActiveCamera] Zoom 1.25
             $this-renWin Render
         }
         out {
-            set camera [$this-ren GetActiveCamera]
-            set zoom [$camera Zoom 0.8]
+            [$this-ren GetActiveCamera] Zoom 0.8
             $this-renWin Render
         }
         reset {
-            $this-ren ResetCamera
-            [$this-ren GetActiveCamera] Zoom 1.5
+            if {$_dims == "3D"} {
+                [$this-ren GetActiveCamera] SetViewAngle 30
+                $this-ren ResetCamera
+                _3dView 45 45
+            } else {
+                $this-ren ResetCamera
+                [$this-ren GetActiveCamera] Zoom 1.5
+            }
             $this-renWin Render
             $this-renWin2 Render
@@ -703,4 +823,6 @@
             set _click(x) $x
             set _click(y) $y
+            set _click(theta) $_view(theta)
+            set _click(phi) $_view(phi)
         }
         drag {
@@ -712,10 +834,26 @@
                 set dx [expr {double($x-$_click(x))/$w}]
                 set dy [expr {double($y-$_click(y))/$h}]
-                foreach actor $_actors($this-ren) {
-                    foreach {ax ay az} [$actor GetPosition] break
-                    $actor SetPosition [expr {$ax+$dx}] [expr {$ay-$dy}] 0
+
+                if {$_dims == "2D"} {
+                    #
+                    # Shift the contour plot in 2D
+                    #
+                    foreach actor $_actors($this-ren) {
+                        foreach {ax ay az} [$actor GetPosition] break
+                        $actor SetPosition [expr {$ax+$dx}] [expr {$ay-$dy}] 0
+                    }
+                    $this-renWin Render
+                } elseif {$_dims == "3D"} {
+                    #
+                    # Rotate the camera in 3D
+                    #
+                    set theta [expr {$_view(theta) - $dy*180}]
+                    if {$theta < 2} { set theta 2 }
+                    if {$theta > 178} { set theta 178 }
+                    set phi [expr {$_view(phi) - $dx*360}]
+
+                    _3dView $theta $phi
+                    $this-renWin Render
                 }
-                $this-renWin Render
-
                 set _click(x) $x
                 set _click(y) $y
@@ -743,5 +881,5 @@
 # ----------------------------------------------------------------------
 itcl::body Rappture::ContourResult::_slice {option args} {
-    if {$_slicer(xform) == ""} {
+    if {$_slicer(plane) == ""} {
         # no slicer? then bail out!
         return
@@ -754,4 +892,13 @@
             set axis [lindex $args 0]
 
+            switch -- $axis {
+                x { $_slicer(plane) SetNormal 1 0 0 }
+                y { $_slicer(plane) SetNormal 0 1 0 }
+                z { $_slicer(plane) SetNormal 0 0 1 }
+                default {
+                    error "bad axis \"$axis\": should be x, y, z"
+                }
+            }
+
             set _slicer(axis) $axis
             $itk_component(slicer) set 50
@@ -770,54 +917,27 @@
             set newval [lindex $args 0]
 
-            switch -- $_slicer(axis) {
-                x {
-                    # Rotate 90 around x-axis -- switch y/z scales
-                    # limit z-motion according to y-scale
-                    set min ymin; set max ymax
-                    # switch scales for y/z
-                    set sx [expr {$_limits(xmax)-$_limits(xmin)}]
-                    set sy [expr {$_limits(zmax)-$_limits(zmin)}]
-                    set sz [expr {$_limits(ymax)-$_limits(ymin)}]
-                }
-                y {
-                    # Rotate 90 around x-axis -- switch x/z scales
-                    # limit z-motion according to x-scale
-                    set min xmin; set max xmax
-                    # switch scales for x/z
-                    set sx [expr {$_limits(zmax)-$_limits(zmin)}]
-                    set sy [expr {$_limits(ymax)-$_limits(ymin)}]
-                    set sz [expr {$_limits(xmax)-$_limits(xmin)}]
-                }
-                z {
-                    # No rotation -- treat z-axis normally
-                    set min zmin; set max zmax
-                    set sx [expr {$_limits(xmax)-$_limits(xmin)}]
-                    set sy [expr {$_limits(ymax)-$_limits(ymin)}]
-                    set sz [expr {$_limits(zmax)-$_limits(zmin)}]
-                }
-            }
-
-            set zval [expr {0.01*($newval-50)
-                *($_limits($max)-$_limits($min))
-                  + 0.5*($_limits($max)+$_limits($min))}]
-
-            $_slicer(xform) Identity
-            switch -- $_slicer(axis) {
-                x { $_slicer(xform) RotateX 90 }
-                y { $_slicer(xform) RotateY 90 }
-                z { # all set }
-                default { error "bad axis \"$axis\": should be x, y, z" }
-            }
-            $_slicer(xform) Translate 0 0 $zval
-            $_slicer(xform) Scale $sx $sy $sz
+            set xm [expr {0.5*($_limits(xmax)+$_limits(xmin))}]
+            set ym [expr {0.5*($_limits(ymax)+$_limits(ymin))}]
+            set zm [expr {0.5*($_limits(zmax)+$_limits(zmin))}]
+
+            set a $_slicer(axis)
+            set newval [expr {0.01*($newval-50)
+                *($_limits(${a}max)-$_limits(${a}min))
+                  + 0.5*($_limits(${a}max)+$_limits(${a}min))}]
 
             # show the current value in the readout
             if {$_slicer(readout) != ""} {
-                set a $_slicer(axis)
-                set newval [expr {0.01*($newval-50)
-                    *($_limits(${a}max)-$_limits(${a}min))
-                      + 0.5*($_limits(${a}max)+$_limits(${a}min))}]
                 $_slicer(readout) SetInput "$a = $newval"
             }
+
+            # keep a little inside the volume, or the slice will disappear!
+            if {$newval == $_limits(${a}min)} {
+                set range [expr {$_limits(${a}max)-$_limits(${a}min)}]
+                set newval [expr {$newval + 1e-6*$range}]
+            }
+
+            # xfer new value to the proper dimension and move the cut plane
+            set ${a}m $newval
+            $_slicer(plane) SetOrigin $xm $ym $zm
 
             $this-renWin Render
@@ -827,4 +947,38 @@
         }
     }
+}
+
+# ----------------------------------------------------------------------
+# USAGE: _3dView <theta> <phi>
+#
+# Used internally to change the position of the camera for 3D data
+# sets.  Sets the camera according to the angles <theta> (angle from
+# the z-axis) and <phi> (angle from the x-axis in the x-y plane).
+# Both angles are in degrees.
+# ----------------------------------------------------------------------
+itcl::body Rappture::ContourResult::_3dView {theta phi} {
+    set deg2rad 0.0174532927778
+    set xn [expr {sin($theta*$deg2rad)*cos($phi*$deg2rad)}]
+    set yn [expr {sin($theta*$deg2rad)*sin($phi*$deg2rad)}]
+    set zn [expr {cos($theta*$deg2rad)}]
+
+    set xm [expr {0.5*($_limits(xmax)+$_limits(xmin))}]
+    set ym [expr {0.5*($_limits(ymax)+$_limits(ymin))}]
+    set zm [expr {0.5*($_limits(zmax)+$_limits(zmin))}]
+
+    set cam [$this-ren GetActiveCamera]
+    set zoom [$cam GetViewAngle]
+    $cam SetViewAngle 30
+
+    $cam SetFocalPoint $xm $ym $zm
+    $cam SetPosition [expr {$xm-$xn}] [expr {$ym-$yn}] [expr {$zm+$zn}]
+    $cam ComputeViewPlaneNormal
+    $cam SetViewUp 0 0 1  ;# z-dir is up
+    $cam OrthogonalizeViewUp
+    $this-ren ResetCamera
+    $cam SetViewAngle $zoom
+
+    set _view(theta) $theta
+    set _view(phi) $phi
 }
 
Index: /trunk/gui/scripts/curve.tcl
===================================================================
--- /trunk/gui/scripts/curve.tcl	(revision 16)
+++ /trunk/gui/scripts/curve.tcl	(revision 17)
@@ -117,5 +117,5 @@
     switch -- $which {
         x { set pos 0 }
-        y { set pos 1 }
+        y - v { set pos 1 }
         default {
             error "bad option \"$which\": should be x or y"
Index: /trunk/gui/scripts/editor.tcl
===================================================================
--- /trunk/gui/scripts/editor.tcl	(revision 16)
+++ /trunk/gui/scripts/editor.tcl	(revision 17)
@@ -91,4 +91,21 @@
         [itcl::code $this _click %X %Y]
 
+    itk_component add emenu {
+        menu $itk_component(editor).menu -tearoff 0
+    } {
+        usual
+        ignore -tearoff
+        ignore -background -foreground
+    }
+    $itk_component(emenu) add command -label "Cut" -accelerator "^X" \
+        -command [list event generate $itk_component(editor) <<Cut>>]
+    $itk_component(emenu) add command -label "Copy" -accelerator "^C" \
+        -command [list event generate $itk_component(editor) <<Copy>>]
+    $itk_component(emenu) add command -label "Paste" -accelerator "^V" \
+        -command [list event generate $itk_component(editor) <<Paste>>]
+    bind $itk_component(editor) <<PopupMenu>> {
+        tk_popup %W.menu %X %Y
+    }
+
     eval itk_initialize $args
 }
@@ -104,4 +121,7 @@
 itcl::body Rappture::Editor::activate {} {
     set e $itk_component(editor)
+    if {[winfo ismapped $e]} {
+        return  ;# already mapped -- nothing to do
+    }
 
     set info ""
@@ -234,4 +254,8 @@
     if {[winfo containing $x $y] != $itk_component(editor)} {
         deactivate
+    } else {
+        # make sure the editor has keyboard focus!
+        # it loses focus sometimes during cut/copy/paste operations
+        focus -force $itk_component(editor)
     }
 }
Index: /trunk/gui/scripts/field.tcl
===================================================================
--- /trunk/gui/scripts/field.tcl	(revision 16)
+++ /trunk/gui/scripts/field.tcl	(revision 17)
@@ -92,6 +92,7 @@
     }
     foreach name [array names _comp2vtk] {
-        set cobj [lindex $_comp2vtk($name) 0]
-        Rappture::Cloud::release $cobj
+        set mobj [lindex $_comp2vtk($name) 0]
+        set class [$mobj info class]
+        ${class}::release $mobj
 
         set fobj [lindex $_comp2vtk($name) 1]
@@ -147,6 +148,6 @@
     }
     if {[info exists _comp2vtk($what)]} {
-        set cobj [lindex $_comp2vtk($what) 0]
-        return [$cobj points]
+        set mobj [lindex $_comp2vtk($what) 0]
+        return [$mobj mesh]
     }
     error "bad option \"$what\": should be [join [lsort [array names _comp2dims]] {, }]"
@@ -305,6 +306,7 @@
     }
     foreach name [array names _comp2vtk] {
-        set cobj [lindex $_comp2vtk($name) 0]
-        Rappture::Cloud::release $cobj
+        set mobj [lindex $_comp2vtk($name) 0]
+        set class [$mobj info class]
+        ${class}::release $mobj
 
         set fobj [lindex $_comp2vtk($name) 1]
@@ -390,6 +392,14 @@
             set path [$_field get $cname.mesh]
             if {[$_xmlobj element $path] != ""} {
-                set cobj [Rappture::Cloud::fetch $_xmlobj $path]
-                if {[$cobj dimensions] > 1} {
+                switch -- [$_xmlobj element -as type $path] {
+                    cloud {
+                        set mobj [Rappture::Cloud::fetch $_xmlobj $path]
+                    }
+                    mesh {
+                        set mobj [Rappture::Mesh::fetch $_xmlobj $path]
+                    }
+                }
+
+                if {[$mobj dimensions] > 1} {
                     #
                     # 2D/3D data
@@ -407,6 +417,6 @@
                     }
 
-                    set _comp2dims($cname) "[$cobj dimensions]D"
-                    set _comp2vtk($cname) [list $cobj $farray]
+                    set _comp2dims($cname) "[$mobj dimensions]D"
+                    set _comp2vtk($cname) [list $mobj $farray]
                     incr _counter
                 } else {
@@ -418,5 +428,5 @@
                     set yv [blt::vector create y$_counter]
 
-                    set vtkpts [$cobj points]
+                    set vtkpts [$mobj points]
                     set max [$vtkpts GetNumberOfPoints]
                     for {set i 0} {$i < $max} {incr i} {
@@ -424,5 +434,6 @@
                         $xv append $xval
                     }
-                    Rappture::Cloud::release $cobj
+                    set class [$mobj info class]
+                    ${class}::release $mobj
 
                     set values [$_field get $cname.values]
Index: /trunk/gui/scripts/gauge.tcl
===================================================================
--- /trunk/gui/scripts/gauge.tcl	(revision 16)
+++ /trunk/gui/scripts/gauge.tcl	(revision 17)
@@ -38,4 +38,5 @@
 
     public method value {args}
+    public method edit {option}
 
     protected method _redraw {}
@@ -87,4 +88,24 @@
     bind $itk_component(value) <Enter> [itcl::code $this _hilite value on]
     bind $itk_component(value) <Leave> [itcl::code $this _hilite value off]
+
+    bind $itk_component(value) <<Cut>> [itcl::code $this edit cut]
+    bind $itk_component(value) <<Copy>> [itcl::code $this edit copy]
+    bind $itk_component(value) <<Paste>> [itcl::code $this edit paste]
+
+    itk_component add emenu {
+        menu $itk_component(value).menu -tearoff 0
+    } {
+        usual
+        ignore -tearoff
+    }
+    $itk_component(emenu) add command -label "Cut" -accelerator "^X" \
+        -command [list event generate $itk_component(value) <<Cut>>]
+    $itk_component(emenu) add command -label "Copy" -accelerator "^C" \
+        -command [list event generate $itk_component(value) <<Copy>>]
+    $itk_component(emenu) add command -label "Paste" -accelerator "^V" \
+        -command [list event generate $itk_component(value) <<Paste>>]
+    bind $itk_component(value) <<PopupMenu>> {
+        tk_popup %W.menu %X %Y
+    }
 
     itk_component add editor {
@@ -196,4 +217,36 @@
 
 # ----------------------------------------------------------------------
+# USAGE: edit cut
+# USAGE: edit copy
+# USAGE: edit paste
+#
+# Used internally to handle cut/copy/paste operations for the current
+# value.  Usually invoked by <<Cut>>, <<Copy>>, <<Paste>> events, but
+# can also be called directly through this method.
+# ----------------------------------------------------------------------
+itcl::body Rappture::Gauge::edit {option} {
+    switch -- $option {
+        cut {
+            edit copy
+            _editor popup
+            $itk_component(editor) value ""
+            $itk_component(editor) deactivate
+        }
+        copy {
+            clipboard clear
+            clipboard append $_value
+        }
+        paste {
+            _editor popup
+            $itk_component(editor) value [clipboard get]
+            $itk_component(editor) deactivate
+        }
+        default {
+            error "bad option \"$option\": should be cut, copy, paste"
+        }
+    }
+}
+
+# ----------------------------------------------------------------------
 # USAGE: _redraw
 #
Index: /trunk/gui/scripts/loader.tcl
===================================================================
--- /trunk/gui/scripts/loader.tcl	(revision 16)
+++ /trunk/gui/scripts/loader.tcl	(revision 17)
@@ -96,8 +96,13 @@
                         set label "Example #$_counter"
                     }
-                    $itk_component(combo) choices insert end $obj $label
-
-                    if {[string equal $defval [file tail $fname]]} {
-                        $xmlobj put $path.default $label
+
+                    # if this is new, add it
+                    set i [$itk_component(combo) choices index -label $label]
+                    if {$i < 0} {
+                        $itk_component(combo) choices insert end $obj $label
+
+                        if {[string equal $defval [file tail $fname]]} {
+                            $xmlobj put $path.default $label
+                        }
                     }
                 }
Index: /trunk/gui/scripts/mainwin.tcl
===================================================================
--- /trunk/gui/scripts/mainwin.tcl	(revision 16)
+++ /trunk/gui/scripts/mainwin.tcl	(revision 17)
@@ -13,4 +13,5 @@
 # ======================================================================
 package require Itk
+package require BLT
 
 option add *MainWin.mode desktop widgetDefault
@@ -31,8 +32,10 @@
 
     public method draw {option args}
+    public method syncCutBuffer {option args}
 
     protected method _redraw {}
 
     private variable _contents ""  ;# frame containing app
+    private variable _sync         ;# to sync current selection and cut buffer
     private variable _bgscript ""  ;# script of background drawing cmds
     private variable _bgparser ""  ;# parser for bgscript
@@ -96,4 +99,87 @@
     set btags [bindtags $itk_component(hull)]
     bindtags $itk_component(hull) [lappend btags RapptureMainWin]
+
+    set _sync(cutbuffer) ""
+    set _sync(selection) ""
+    syncCutBuffer ifneeded
+}
+
+# ----------------------------------------------------------------------
+# USAGE: syncCutBuffer ifneeded
+# USAGE: syncCutBuffer transfer <offset> <maxchars>
+# USAGE: syncCutBuffer lostselection
+#
+# Invoked automatically whenever the mouse pointer enters or leaves
+# a main window to sync the cut buffer with the primary selection.
+# This helps applications work properly with the "Copy/Paste with
+# Desktop" option on the VNC applet for the nanoHUB.
+#
+# The "ifneeded" option syncs the cutbuffer and the primary selection
+# if either one has new data.
+#
+# The "fromselection" option syncs from the primary selection to the
+# cut buffer.  If there's a primary selection, it gets copied to the
+# cut buffer.
+# ----------------------------------------------------------------------
+itcl::body Rappture::MainWin::syncCutBuffer {option args} {
+    set mainwin $itk_component(hull)
+    switch -- $option {
+        ifneeded {
+            #
+            # See if the incoming cut buffer has changed.
+            # If so, then sync the new input to the primary selection.
+            #
+            set s [blt::cutbuffer get]
+            if {"" != $s && ![string equal $s $_sync(cutbuffer)]} {
+                set _sync(cutbuffer) $s
+
+                if {![string equal $s $_sync(selection)]
+                      && [selection own -selection PRIMARY] != $mainwin} {
+                    set _sync(selection) $s
+
+                    clipboard clear
+                    clipboard append -- $s
+                    selection handle -selection PRIMARY $mainwin \
+                        [itcl::code $this syncCutBuffer transfer]
+                    selection own -selection PRIMARY -command \
+                        [itcl::code $this syncCutBuffer lostselection] \
+                        $mainwin
+                }
+            }
+
+            #
+            # See if the selection has changed.  If so, then sync
+            # the new input to the cut buffer, so it's available
+            # outside the VNC client.
+            #
+            set s ""
+            if {[catch {selection get -selection PRIMARY} s] || "" == $s} {
+                if {[catch {clipboard get} s]} {
+                    set s ""
+                }
+            }
+            if {"" != $s && ![string equal $s $_sync(selection)]} {
+                set _sync(selection) $s
+                blt::cutbuffer set $s
+            }
+
+            # do this again soon
+            after 1000 [itcl::code $this syncCutBuffer ifneeded]
+        }
+        transfer {
+            if {[llength $args] != 2} {
+                error "wrong # args: should be \"syncCutBuffer transfer offset max\""
+            }
+            set offset [lindex $args 0]
+            set maxchars [lindex $args 1]
+            return [string range $_currseln $offset [expr {$offset+$maxchars-1}]]
+        }
+        lostselection {
+            # nothing to do
+        }
+        default {
+            error "bad option \"$option\": should be ifneeded, transfer, or lostselection"
+        }
+    }
 }
 
@@ -127,4 +213,5 @@
         }
 
+        set bd 0  ;# optional border
         set sw [winfo width $itk_component(area)]
         set sh [winfo height $itk_component(area)]
@@ -133,6 +220,6 @@
         set w [winfo reqwidth $itk_component(app)]
         set h [winfo reqheight $itk_component(app)]
-        if {$w > $sw} {
-            set $w $sw
+        if {$w > $sw-2*$bd} {
+            set $w [expr {$sw-2*$bd}]
             set clip 1
         }
@@ -141,9 +228,9 @@
             n {
                 set x [expr {$sw/2}]
-                set y 0
+                set y $bd
             }
             s {
                 set x [expr {$sw/2}]
-                set y $sh
+                set y [expr {$sh-$bd}]
             }
             center {
@@ -152,26 +239,26 @@
             }
             w {
-                set x 0
+                set x $bd
                 set y [expr {$sh/2}]
             }
             e {
-                set x $sw
+                set x [expr {$sw-$bd}]
                 set y [expr {$sh/2}]
             }
             nw {
-                set x 0
-                set y 0
+                set x $bd
+                set y $bd
             }
             ne {
-                set x $sw
-                set y 0
+                set x [expr {$sw-$bd}]
+                set y $bd
             }
             sw {
-                set x 0
-                set y $sh
+                set x $bd
+                set y [expr {$sh-$bd}]
             }
             se {
-                set x $sw
-                set y $sh
+                set x [expr {$sw-$bd}]
+                set y [expr {$sh-$bd}]
             }
         }
Index: /trunk/gui/scripts/mesh.tcl
===================================================================
--- /trunk/gui/scripts/mesh.tcl	(revision 16)
+++ /trunk/gui/scripts/mesh.tcl	(revision 17)
@@ -23,5 +23,6 @@
     public method points {}
     public method elements {}
-    public method size {}
+    public method mesh {}
+    public method size {{what -points}}
     public method dimensions {}
     public method limits {which}
@@ -36,6 +37,4 @@
     private variable _units "m"  ;# system of units for this mesh
     private variable _limits     ;# limits xmin, xmax, ymin, ymax, ...
-    private variable _npts 0     ;# number of points
-    private variable _nelems 0   ;# number of elements
 
     private common _xp2obj       ;# used for fetch/release ref counting
@@ -105,8 +104,16 @@
     }
 
+    # create the vtk objects containing points and connectivity
+    vtkPoints $this-points
+    vtkVoxel $this-vox
+    vtkUnstructuredGrid $this-grid
+
     foreach lim {xmin xmax ymin ymax zmin zmax} {
         set _limits($lim) ""
     }
 
+    #
+    # Extract each node and add it to the points list.
+    #
     foreach comp [$xmlobj children -type node $path] {
         set xyz [$xmlobj get $path.$comp]
@@ -133,7 +140,21 @@
             }
         }
-        incr _npts
-    }
-    set _nelems [llength [$xmlobj children -type element $path]]
+        $this-points InsertNextPoint $x $y $z
+    }
+    $this-grid SetPoints $this-points
+
+    #
+    # Extract each element and add it to the mesh.
+    #
+    foreach comp [$xmlobj children -type element $path] {
+        set nlist [$_mesh get $comp.nodes]
+        set i 0
+        foreach n $nlist {
+            [$this-vox GetPointIds] SetId $i $n
+            incr i
+        }
+        $this-grid InsertNextCell [$this-vox GetCellType] \
+            [$this-vox GetPointIds]
+    }
 }
 
@@ -144,4 +165,8 @@
     # don't destroy the _xmlobj! we don't own it!
     itcl::delete object $_mesh
+
+    rename $this-points ""
+    rename $this-vox ""
+    rename $this-grid ""
 }
 
@@ -153,5 +178,5 @@
 itcl::body Rappture::Mesh::points {} {
     # not implemented
-    return ""
+    return $this-points
 }
 
@@ -198,10 +223,29 @@
 
 # ----------------------------------------------------------------------
-# USAGE: size
+# USAGE: mesh
+#
+# Returns the vtk object representing the mesh.
+# ----------------------------------------------------------------------
+itcl::body Rappture::Mesh::mesh {} {
+    return $this-grid
+}
+
+# ----------------------------------------------------------------------
+# USAGE: size ?-points|-elements?
 #
 # Returns the number of points in this mesh.
 # ----------------------------------------------------------------------
-itcl::body Rappture::Mesh::size {} {
-    return $_npts
+itcl::body Rappture::Mesh::size {{what -points}} {
+    switch -- $what {
+        -points {
+            return [$this-points GetNumberOfPoints]
+        }
+        -elements {
+            return [$this-points GetNumberOfCells]
+        }
+        default {
+            error "bad option \"$what\": should be -points or -elements"
+        }
+    }
 }
 
Index: /trunk/gui/scripts/textentry.tcl
===================================================================
--- /trunk/gui/scripts/textentry.tcl	(revision 16)
+++ /trunk/gui/scripts/textentry.tcl	(revision 17)
@@ -109,15 +109,27 @@
         if {$_mode == "entry"} {
             $itk_component(entry) configure -state normal
+            $itk_component(emenu) entryconfigure "Cut" -state normal
+            $itk_component(emenu) entryconfigure "Copy" -state normal
+            $itk_component(emenu) entryconfigure "Paste" -state normal
             $itk_component(entry) delete 0 end
             $itk_component(entry) insert 0 $newval
             if {!$itk_option(-editable)} {
                 $itk_component(entry) configure -state disabled
+                $itk_component(emenu) entryconfigure "Cut" -state disabled
+                $itk_component(emenu) entryconfigure "Copy" -state disabled
+                $itk_component(emenu) entryconfigure "Paste" -state disabled
             }
         } elseif {$_mode == "text"} {
             $itk_component(text) configure -state normal
+            $itk_component(tmenu) entryconfigure "Cut" -state normal
+            $itk_component(tmenu) entryconfigure "Copy" -state normal
+            $itk_component(tmenu) entryconfigure "Paste" -state normal
             $itk_component(text) delete 1.0 end
             $itk_component(text) insert end $newval
             if {!$itk_option(-editable)} {
                 $itk_component(text) configure -state disabled
+                $itk_component(tmenu) entryconfigure "Cut" -state disabled
+                $itk_component(tmenu) entryconfigure "Copy" -state disabled
+                $itk_component(tmenu) entryconfigure "Paste" -state disabled
             }
         }
@@ -231,4 +243,17 @@
                 -foreground $itk_option(-textforeground)
 
+            itk_component add emenu {
+                menu $itk_component(entry).menu -tearoff 0
+            }
+            $itk_component(emenu) add command -label "Cut" -accelerator "^X" \
+                -command [list event generate $itk_component(entry) <<Cut>>]
+            $itk_component(emenu) add command -label "Copy" -accelerator "^C" \
+                -command [list event generate $itk_component(entry) <<Copy>>]
+            $itk_component(emenu) add command -label "Paste" -accelerator "^V" \
+                -command [list event generate $itk_component(entry) <<Paste>>]
+            bind $itk_component(entry) <<PopupMenu>> {
+                tk_popup %W.menu %X %Y
+            }
+
             $itk_component(entry) insert end $val
             if {!$itk_option(-editable)} {
@@ -270,7 +295,23 @@
             $itk_component(scrollbars) contents $itk_component(text)
 
+            itk_component add tmenu {
+                menu $itk_component(text).menu -tearoff 0
+            }
+            $itk_component(tmenu) add command -label "Cut" -accelerator "^X" \
+                -command [list event generate $itk_component(text) <<Cut>>]
+            $itk_component(tmenu) add command -label "Copy" -accelerator "^C" \
+                -command [list event generate $itk_component(text) <<Copy>>]
+            $itk_component(tmenu) add command -label "Paste" -accelerator "^V" \
+                -command [list event generate $itk_component(text) <<Paste>>]
+            bind $itk_component(text) <<PopupMenu>> {
+                tk_popup %W.menu %X %Y
+            }
+
             $itk_component(text) insert end $val
             if {!$itk_option(-editable)} {
                 $itk_component(text) configure -state disabled
+                $itk_component(menu) entryconfigure "Cut" -state disabled
+                $itk_component(menu) entryconfigure "Copy" -state disabled
+                $itk_component(menu) entryconfigure "Paste" -state disabled
             }
             set _mode "text"
@@ -306,6 +347,12 @@
     if {$_mode == "entry"} {
         $itk_component(editor) configure -state $state
+        $itk_component(emenu) entryconfigure "Cut" -state $state
+        $itk_component(emenu) entryconfigure "Copy" -state $state
+        $itk_component(emenu) entryconfigure "Paste" -state $state
     } elseif {$_mode == "text"} {
         $itk_component(text) configure -state $state
+        $itk_component(tmenu) entryconfigure "Cut" -state $state
+        $itk_component(tmenu) entryconfigure "Copy" -state $state
+        $itk_component(tmenu) entryconfigure "Paste" -state $state
     }
 }
Index: /trunk/gui/scripts/tool.tcl
===================================================================
--- /trunk/gui/scripts/tool.tcl	(revision 16)
+++ /trunk/gui/scripts/tool.tcl	(revision 17)
@@ -191,5 +191,6 @@
         if {"" != [$newobj element -as type $path.current]} {
             set val [$newobj get $path.current]
-            if {[string length $val] > 0} {
+            if {[string length $val] > 0
+                  || [llength [$newobj children $path.current]] == 0} {
                 $_path2widget($path) value $val
             } else {
