Index: trunk/gui/apps/driver
===================================================================
--- trunk/gui/apps/driver	(revision 5)
+++ trunk/gui/apps/driver	(revision 6)
@@ -82,5 +82,5 @@
 
 # open the XML file containing the material library
-set lib [Rappture::Library::open -std library.xml]
+set lib [Rappture::library -std library.xml]
                                                                                 
 # open the XML file containing the tool parameters
@@ -89,5 +89,5 @@
     exit 1
 }
-set tool [Rappture::Library::open $toolfile]
+set tool [Rappture::library $toolfile]
                                                                                 
 # open the XML file containing the configuration for this application
@@ -96,5 +96,5 @@
     exit 1
 }
-set config [Rappture::Library::open $configfile]
+set config [Rappture::library $configfile]
 
 # ----------------------------------------------------------------------
@@ -148,8 +148,9 @@
 # ----------------------------------------------------------------------
 set w $win.input
-set ndevs [$config get -count controls.device]
-if {$ndevs > 0} {
-    for {set i 0} {$i < $ndevs} {incr i} {
-        set obj [$config get -object controls.device$i]
+set dfirst ""
+set dlist [$config children -type structure controls]
+if {"" != $dlist} {
+    foreach dname $dlist {
+        set obj [$config element -flavor object controls.$dname]
         set name [$obj get label]
         set devs($name) $obj
@@ -157,5 +158,5 @@
     set devlist [lsort [array names devs]]
 
-    if {$ndevs > 1} {
+    if {[array size devs] > 1} {
         frame $w.devsel
         pack $w.devsel -side top -fill x
@@ -173,4 +174,5 @@
 
     set first [lindex $devlist 0]
+    set dfirst $devs($first)
     Rappture::DeviceViewer1D $w.device -device $devs($first) \
         -tool $tool -library $lib
@@ -185,6 +187,6 @@
 set w $win.output
 Rappture::Analyzer $w.analyze -holdwindow $win.input \
-    -tool $tool -analysis [$config get -object analysis] \
-    -device $devs($first)
+    -tool $tool -analysis [$config element -flavor object analysis] \
+    -device $dfirst
 pack $w.analyze -expand yes -fill both
 
@@ -192,9 +194,8 @@
 # HOOK UP ANY CONTROLS CALLED OUT IN CONFIG.XML
 # ----------------------------------------------------------------------
-set ncntls [$config get -count controls.access]
-for {set i 0} {$i < $ncntls} {incr i} {
-    set name [$config get access$i]
+foreach access [$config children -type access controls] {
+    set name [$config get controls.$access]
     switch -glob -- $name {
-        parameters.ambient* - device* {
+        parameters.ambient* - structure* {
             $win.input.device controls add $name
         }
Index: trunk/gui/scripts/analyzer.tcl
===================================================================
--- trunk/gui/scripts/analyzer.tcl	(revision 5)
+++ trunk/gui/scripts/analyzer.tcl	(revision 6)
@@ -258,5 +258,5 @@
 # ----------------------------------------------------------------------
 itcl::configbody Rappture::Analyzer::tool {
-    if {![Rappture::Library::valid $itk_option(-tool)]} {
+    if {![Rappture::library isvalid $itk_option(-tool)]} {
         error "bad value \"$itk_option(-tool)\": should be Rappture::Library"
     }
@@ -276,5 +276,5 @@
 itcl::configbody Rappture::Analyzer::device {
     if {$itk_option(-device) != ""
-          && ![Rappture::Library::valid $itk_option(-device)]} {
+          && ![Rappture::library isvalid $itk_option(-device)]} {
         error "bad value \"$itk_option(-device)\": should be Rappture::Library"
     }
@@ -289,5 +289,5 @@
 # ----------------------------------------------------------------------
 itcl::configbody Rappture::Analyzer::analysis {
-    if {![Rappture::Library::valid $itk_option(-analysis)]} {
+    if {![Rappture::library isvalid $itk_option(-analysis)]} {
         error "bad value \"$itk_option(-analysis)\": should be Rappture::Library"
     }
@@ -299,5 +299,5 @@
 
     set counter 0
-    foreach item [$itk_option(-analysis) get -children] {
+    foreach item [$itk_option(-analysis) children] {
         switch -glob -- $item {
             xyplot* {
@@ -311,5 +311,5 @@
 
                 set _widgets($item) [Rappture::Xyplot $page.#auto \
-                    -layout [$itk_option(-analysis) get -object $item]]
+                    -layout [$itk_option(-analysis) element -flavor object $item]]
                 pack $_widgets($item) -expand yes -fill both
             }
Index: trunk/gui/scripts/curve.tcl
===================================================================
--- trunk/gui/scripts/curve.tcl	(revision 5)
+++ trunk/gui/scripts/curve.tcl	(revision 6)
@@ -38,9 +38,9 @@
 # ----------------------------------------------------------------------
 itcl::body Rappture::Curve::constructor {libobj path} {
-    if {![Rappture::Library::valid $libobj]} {
+    if {![Rappture::library isvalid $libobj]} {
         error "bad value \"$libobj\": should be LibraryObj"
     }
     set _libobj $libobj
-    set _curve [$libobj get -object $path]
+    set _curve [$libobj element -flavor object $path]
 
     # build up vectors for various components of the curve
@@ -144,10 +144,9 @@
     # vectors for each part.
     #
-    set max [$_curve get -count component]
-    for {set i 0} {$i < $max} {incr i} {
+    foreach cname [$_curve children -type component] {
         set xv ""
         set yv ""
 
-        set xydata [$_curve get component$i.xy]
+        set xydata [$_curve get $cname.xy]
         if {"" != $xydata} {
             set xv [blt::vector create x$_counter]
@@ -163,5 +162,5 @@
 
         if {$xv != "" && $yv != ""} {
-            set _comp2vecs(component$i) [list $xv $yv]
+            set _comp2vecs($cname) [list $xv $yv]
             incr _counter
         }
Index: trunk/gui/scripts/deviceLayout1D.tcl
===================================================================
--- trunk/gui/scripts/deviceLayout1D.tcl	(revision 5)
+++ trunk/gui/scripts/deviceLayout1D.tcl	(revision 6)
@@ -182,5 +182,5 @@
     # see if any of the slabs has a label
     if {$_device != ""} {
-        foreach nn [$_device get -children recipe] {
+        foreach nn [$_device children recipe] {
             if {"" != [$_device get recipe.$nn.label]} {
                 set extra [expr {1.2*[font metrics $fnt -linespace]}] 
@@ -192,5 +192,5 @@
 
     # a little extra height for the molecule image
-    if {[$_device get -exists recipe.molecule]} {
+    if {"" != [$_device element recipe.molecule]} {
         set h [expr {$h+15}]
     }
@@ -210,5 +210,5 @@
     set z 0
     if {$_device != ""} {
-        foreach nn [$_device get -children recipe] {
+        foreach nn [$_device children recipe] {
             switch -glob -- $nn {
                 slab* - molecule* {
@@ -310,5 +310,5 @@
     set h [expr {[winfo height $c]-1}]
     # a little extra height for the molecule image
-    if {[$_device get -exists recipe.molecule]} {
+    if {"" != [$_device element recipe.molecule]} {
         set h [expr {$h-15}]
     }
@@ -361,5 +361,5 @@
     set h [expr {[winfo height $c]-1}]
     # a little extra height for the molecule image
-    if {[$_device get -exists recipe.molecule]} {
+    if {"" != [$_device element recipe.molecule]} {
         set h [expr {$h-15}]
     }
@@ -395,5 +395,5 @@
     set h [expr {[winfo height $c]-1}]
     # a little extra height for the molecule image
-    if {[$_device get -exists recipe.molecule]} {
+    if {"" != [$_device element recipe.molecule]} {
         set h [expr {$h-15}]
     }
@@ -448,5 +448,5 @@
     #
     if {"" != $_device} {
-        set label [$_device get "$elem.label"]
+        set label [$_device get $elem.label]
         if {"" != $label} {
             set y [expr {$y-0.5*$lh}]
@@ -491,5 +491,5 @@
 itcl::configbody Rappture::DeviceLayout1D::library {
     if {$itk_option(-library) != ""} {
-        if {![Rappture::Library::valid $itk_option(-library)]} {
+        if {![Rappture::library isvalid $itk_option(-library)]} {
             error "bad value \"$itk_option(-library)\": should be Rappture::Library"
         }
@@ -508,5 +508,5 @@
 itcl::configbody Rappture::DeviceLayout1D::device {
     if {$itk_option(-device) != ""} {
-        if {![Rappture::Library::valid $itk_option(-device)]} {
+        if {![Rappture::library isvalid $itk_option(-device)]} {
             error "bad value \"$itk_option(-device)\": should be Rappture::Library"
         }
Index: trunk/gui/scripts/deviceViewer1D.tcl
===================================================================
--- trunk/gui/scripts/deviceViewer1D.tcl	(revision 5)
+++ trunk/gui/scripts/deviceViewer1D.tcl	(revision 6)
@@ -186,5 +186,5 @@
     #
     if {$_device != ""} {
-        foreach nn [$_device get -children] {
+        foreach nn [$_device children] {
             if {[string match field* $nn]} {
                 set name [$_device get $nn.label]
@@ -516,9 +516,8 @@
 itcl::body Rappture::DeviceViewer1D::_controlCreate {container libObj path} {
     set presets ""
-    set npre [$libObj get -count $path.preset]
-    for {set i 0} {$i < $npre} {incr i} {
+    foreach pre [$libObj children -type preset $path] {
         lappend presets \
-            [$libObj get $path.preset$i.value] \
-            [$libObj get $path.preset$i.label]
+            [$libObj get $path.$pre.value] \
+            [$libObj get $path.$pre.label]
     }
 
@@ -606,5 +605,5 @@
 itcl::configbody Rappture::DeviceViewer1D::device {
     if {$itk_option(-device) != ""} {
-        if {![Rappture::Library::valid $itk_option(-device)]} {
+        if {![Rappture::library isvalid $itk_option(-device)]} {
             error "bad value \"$itk_option(-device)\": should be Rappture::Library"
         }
@@ -623,5 +622,5 @@
 itcl::configbody Rappture::DeviceViewer1D::tool {
     if {$itk_option(-tool) != ""} {
-        if {![Rappture::Library::valid $itk_option(-tool)]} {
+        if {![Rappture::library isvalid $itk_option(-tool)]} {
             error "bad value \"$itk_option(-tool)\": should be Rappture::Library"
         }
Index: trunk/gui/scripts/field.tcl
===================================================================
--- trunk/gui/scripts/field.tcl	(revision 5)
+++ trunk/gui/scripts/field.tcl	(revision 6)
@@ -43,18 +43,18 @@
 # ----------------------------------------------------------------------
 itcl::body Rappture::Field::constructor {devobj libobj path} {
-    if {![Rappture::Library::valid $devobj]} {
+    if {![Rappture::library isvalid $devobj]} {
         error "bad value \"$devobj\": should be LibraryObj"
     }
-    if {![Rappture::Library::valid $libobj]} {
+    if {![Rappture::library isvalid $libobj]} {
         error "bad value \"$libobj\": should be LibraryObj"
     }
     set _device $devobj
     set _libobj $libobj
-    set _field [$libobj get -object $path]
+    set _field [$libobj element -flavor object $path]
     set _units [$_field get units]
 
     # determine the overall size of the device
     set z0 [set z1 0]
-    foreach elem [$_device get -children recipe] {
+    foreach elem [$_device children recipe] {
         switch -glob -- $elem {
             slab* - molecule* {
@@ -192,12 +192,11 @@
     # vectors for each part.
     #
-    set max [$_field get -count component]
-    for {set i 0} {$i < $max} {incr i} {
+    foreach cname [$_field children -type component] {
         set xv ""
         set yv ""
 
-        set val [$_field get component$i.constant]
+        set val [$_field get $cname.constant]
         if {$val != ""} {
-            set domain [$_field get component$i.domain]
+            set domain [$_field get $cname.domain]
             if {$domain == "" || ![info exists _limits($domain)]} {
                 set z0 0
@@ -217,8 +216,8 @@
 
             set zm [expr {0.5*($z0+$z1)}]
-            set _comp2cntls(component$i) \
-                [list component$i.constant $zm $val "$val$_units"]
+            set _comp2cntls($cname) \
+                [list $cname.constant $zm $val "$val$_units"]
         } else {
-            set xydata [$_field get component$i.xy]
+            set xydata [$_field get $cname.xy]
             if {"" != $xydata} {
                 set xv [blt::vector create x$_counter]
@@ -235,5 +234,5 @@
 
         if {$xv != "" && $yv != ""} {
-            set _comp2vecs(component$i) [list $xv $yv]
+            set _comp2vecs($cname) [list $xv $yv]
             incr _counter
         }
Index: trunk/gui/scripts/xyplot.tcl
===================================================================
--- trunk/gui/scripts/xyplot.tcl	(revision 5)
+++ trunk/gui/scripts/xyplot.tcl	(revision 6)
@@ -96,5 +96,5 @@
     if {$layout != "" && $run != ""} {
         set count 0
-        foreach item [$layout get -children] {
+        foreach item [$layout children] {
           switch -glob -- $item {
             title {
@@ -118,5 +118,5 @@
             field* {
               set name [$layout get $item]
-              if {[$run get -exists output.$name]} {
+              if {"" != [$run element output.$name]} {
                   set fobj [Rappture::Field ::#auto $_device $run output.$name]
                   set _path2obj($name) $fobj
@@ -144,5 +144,5 @@
             curve* {
               set name [$layout get $item]
-              if {[$run get -exists output.$name]} {
+              if {"" != [$run get element output.$name]} {
                   set cobj [Rappture::Curve ::#auto $run output.$name]
                   set _path2obj($name) $cobj
@@ -211,5 +211,5 @@
 itcl::configbody Rappture::Xyplot::layout {
     if {$itk_option(-layout) != ""} {
-        if {![Rappture::Library::valid $itk_option(-layout)]} {
+        if {![Rappture::library isvalid $itk_option(-layout)]} {
             error "bad value \"$itk_option(-layout)\": should be Rappture::Library"
         }
@@ -231,8 +231,8 @@
     }
     if {$itk_option(-run) != ""} {
-        if {![Rappture::Library::valid $itk_option(-run)]} {
+        if {![Rappture::library isvalid $itk_option(-run)]} {
             error "bad value \"$itk_option(-run)\": should be Rappture::Library"
         }
-        set _device [$itk_option(-run) get -object device]
+        set _device [$itk_option(-run) element -flavor object device]
     }
     after cancel [itcl::code $this _rebuild]
Index: trunk/lib/library.xml
===================================================================
--- trunk/lib/library.xml	(revision 6)
+++ trunk/lib/library.xml	(revision 6)
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<library>
+<parameters>
+  <group name="ambient">
+    <number name="temperature">
+      <label>Ambient temperature</label>
+      <units>K</units>
+      <min>0</min>
+      <max>500K</max>
+      <default>300K</default>
+      <preset>
+        <value>300K</value>
+        <label>300K (room temperature)</label>
+      </preset>
+      <preset>
+        <value>77K</value>
+        <label>77K (liquid nitrogen)</label>
+      </preset>
+      <preset>
+        <value>4.2K</value>
+        <label>4.2K (liquid helium)</label>
+      </preset>
+      <preset>
+        <value>0K</value>
+        <label>0 (absolute zero)</label>
+      </preset>
+    </number>
+  </group>
+</parameters>
+<materials>
+  <material name="GaAs">
+    <formula>GaAs</formula>
+    <bandgap>1.422eV</bandgap>
+    <color>#ccccff</color>
+    <electronAffinity>4.070eV</electronAffinity>
+    <dielectricConstant>12.847</dielectricConstant>
+    <NcEffDensityOfStates>0.462e18/cm3</NcEffDensityOfStates>
+    <NvEffDensityOfStates>0.942e19/cm3</NvEffDensityOfStates>
+  </material>
+  <material name="Al(0.3)Ga(0.7)As">
+    <formula>Al(0.3)Ga(0.7)As</formula>
+    <color>#9999ff</color>
+    <bandgap>1.797eV</bandgap>
+    <electronAffinity>3.827eV</electronAffinity>
+    <dielectricConstant>11.9</dielectricConstant>
+    <NcEffDensityOfStates>0.857e18/cm3</NcEffDensityOfStates>
+    <NvEffDensityOfStates>0.111e20/cm3</NvEffDensityOfStates>
+  </material>
+  <material name="Au">
+    <formula>Au</formula>
+    <color>#ffff66</color>
+  </material>
+</materials>
+</library>
Index: trunk/python/Rappture/library.py
===================================================================
--- trunk/python/Rappture/library.py	(revision 5)
+++ trunk/python/Rappture/library.py	(revision 6)
@@ -364,4 +364,5 @@
                         if n.nodeName == type:
                             pos = n
+
                     if pos:
                         pos = pos.nextSibling
Index: trunk/tcl/install
===================================================================
--- trunk/tcl/install	(revision 5)
+++ trunk/tcl/install	(revision 6)
@@ -40,6 +40,4 @@
 set targetdir [file join $dir $package$version]
 
-mkindex scripts
-
 if {![file exists $targetdir]} {
     puts "making directory $targetdir..."
@@ -47,5 +45,25 @@
 }
 
-foreach file [find .] {
+set origdir [pwd]
+foreach context {. ../gui} {
+    cd $context
+
+    foreach file [find .] {
+        set target [file join $targetdir $file]
+        if {[file isdirectory $file]} {
+            puts "making directory $target..."
+            catch {file mkdir $target}
+            file attributes $target -permissions ugo+rx
+        } else {
+            puts "installing $target..."
+            file copy -force $file $target
+            file attributes $target -permissions ugo+r
+        }
+    }
+}
+
+cd ..
+catch {file mkdir [file join $targetdir lib]}
+foreach file [find ./lib] {
     set target [file join $targetdir $file]
     if {[file isdirectory $file]} {
@@ -60,4 +78,6 @@
 }
 
+cd $origdir
+
 set fid [open [file join $targetdir pkgIndex.tcl] w]
 puts $fid "# Tcl package index file"
@@ -69,3 +89,5 @@
 close $fid
 
+mkindex [file join $targetdir scripts]
+
 puts "== $package-$version INSTALLED"
Index: trunk/tcl/scripts/library.tcl
===================================================================
--- trunk/tcl/scripts/library.tcl	(revision 5)
+++ trunk/tcl/scripts/library.tcl	(revision 6)
@@ -12,8 +12,8 @@
 
 namespace eval Rappture { # forward declaration }
-namespace eval Rappture::Library { # forward declaration }
-
-# ----------------------------------------------------------------------
-# USAGE: open ?-std? <file>
+
+# ----------------------------------------------------------------------
+# USAGE: library ?-std? <file>
+# USAGE: library isvalid <object>
 #
 # Used to open a <file> containing an XML description of tool
@@ -23,6 +23,22 @@
 # If the -std flag is included, then the file is treated as the
 # name of a standard file, which is part of the Rappture installation.
-# ----------------------------------------------------------------------
-proc Rappture::Library::open {args} {
+#
+# The isvalid operation checks an <object> to see if it is a valid
+# library object.  Returns 1 if so, and 0 otherwise.
+# ----------------------------------------------------------------------
+proc Rappture::library {args} {
+    # handle the isvalid operation...
+    if {[llength $args] > 1 && [lindex $args 0] == "isvalid"} {
+        if {[llength $args] != 2} {
+            error "wrong # args: should be \"library isvalid object\""
+        }
+        set obj [lindex $args 1]
+        if {[catch {$obj isa ::Rappture::LibraryObj} valid] == 0 && $valid} {
+            return 1
+        }
+        return 0
+    }
+
+    # handle the open operation...
     set stdfile 0
     while {[llength $args] > 1} {
@@ -51,27 +67,19 @@
 
 # ----------------------------------------------------------------------
-# USAGE: valid <obj>
-#
-# Checks to see if the given object is a valid Library.  Returns 1
-# if so, and 0 otherwise.
-# ----------------------------------------------------------------------
-proc Rappture::Library::valid {obj} {
-    if {[catch {$obj isa ::Rappture::LibraryObj} valid] == 0 && $valid} {
-        return 1
-    }
-    return 0
-}
-
-# ----------------------------------------------------------------------
 itcl::class Rappture::LibraryObj {
     constructor {info} { # defined below }
     destructor { # defined below }
 
-    public method get {args}
+    public method element {args}
+    public method children {args}
+    public method get {{path ""}}
     public method put {args}
+    public method remove {{path ""}}
     public method xml {}
 
     protected method find {path}
     protected method path2list {path}
+    protected method node2name {node}
+    protected method node2comp {node}
 
     private variable _root 0       ;# non-zero => this obj owns document
@@ -107,22 +115,31 @@
 
 # ----------------------------------------------------------------------
-# USAGE: get ?-exists|-object|-type|-info|-count|-children? ?<path>?
-#
-# Searches the DOM inside this object for the information on the
-# requested <path>.  By default, it returns the -info associated
-# with the path.  The other flags can be used to query other
-# aspects of the information at the requested node.
-# ----------------------------------------------------------------------
-itcl::body Rappture::LibraryObj::get {args} {
-    set format -info
-    while {[llength $args] > 0} {
+# USAGE: element ?-flavor <fval>? ?<path>?
+#
+# Clients use this to query a particular element within the entire
+# data structure.  The path is a string of the form
+# "structure.box(source).corner".  This example represents the tag
+# <corner> within a tag <box id="source"> within a tag <structure>,
+# which must be found at the top level within this document.
+#
+# By default, this method returns the component name "type(id)".
+# This is changed by setting the -flavor argument to "id" (for name
+# of the tail element), to "type" (for the type of the tail element),
+# to "object" (for an object representing the DOM node referenced by
+# the path.
+# ----------------------------------------------------------------------
+itcl::body Rappture::LibraryObj::element {args} {
+    array set params {
+        -flavor component
+    }
+    while {[llength $args] > 1} {
         set first [lindex $args 0]
         if {[string index $first 0] == "-"} {
-            set choices {-exists -object -type -info -count -children}
+            set choices [array names params]
             if {[lsearch $choices $first] < 0} {
                 error "bad option \"$first\": should be [join [lsort $choices] {, }]"
             }
-            set format $first
-            set args [lrange $args 1 end]
+            set params($first) [lindex $args 1]
+            set args [lrange $args 2 end]
         } else {
             break
@@ -130,106 +147,218 @@
     }
     if {[llength $args] > 1} {
-        error "wrong # args: should be \"get ?-exists? ?-object? ?-type? ?-info? ?-count? ?-children? ?path?\""
+        error "wrong # args: should be \"element ?-flavor fval? ?path?\""
     }
     set path [lindex $args 0]
 
     set node [find $path]
-
-    switch -- $format {
-        -exists {
-            if {$node != ""} {
-                return 1
-            }
-            return 0
-        }
-        -object {
-            if {$node != ""} {
-                return [::Rappture::LibraryObj ::#auto $node]
-            }
-            return ""
-        }
-        -info {
-            if {$node != ""} {
-                return [string trim [$node text]]
-            }
-            return ""
-        }
-        -type   {
-            if {$node != ""} {
-                return [$node nodeName]
-            }
-        }
-        -count {
-            if {$node == ""} {
-                return ""
-            }
-            set node [$node parent]
-            set type [lindex [path2list $path] end]
-            set nlist [$node getElementsByTagName $type]
-            return [llength $nlist]
-        }
-        -children {
-            if {$node == ""} {
-                return ""
-            }
-            set rlist ""
-            set nlist [$node childNodes]
-            foreach n $nlist {
-                set type [$n nodeName]
-                if {[regexp {^#} $type]} {
-                    continue
-                }
-                if {![info exists count($type)]} {
-                    set count($type) 0
-                    lappend rlist $type
-                } else {
-                    lappend rlist "$type[incr count($type)]"
-                }
-            }
-            return $rlist
-        }
-    }
-    return ""
-}
-
-# ----------------------------------------------------------------------
-# USAGE: put <path> ?-text|-object? <string>
-#
-# Inserts information into the DOM represented by this object.
-# The <path> is a path to the insertion point, which uses a syntax
-# similar to the "get" method.  The <string> being inserted can either
-# be ordinary text, or another LibraryObj object.
-# ----------------------------------------------------------------------
-itcl::body Rappture::LibraryObj::put {args} {
-    set what "-text"
-    set path [lindex $args 0]
-    set args [lrange $args 1 end]
+    if {$node == ""} {
+        return ""
+    }
+
+    switch -- $params(-flavor) {
+      object {
+          return [::Rappture::LibraryObj ::#auto $node]
+      }
+      component {
+          return [node2comp $node]
+      }
+      id {
+          return [node2name $node]
+      }
+      type {
+          return [$node nodeName]
+      }
+      default {
+          error "bad flavor \"$params(-flavor)\": should be object, id, type, component"
+      }
+    }
+}
+
+# ----------------------------------------------------------------------
+# USAGE: children ?-flavor <fval>? ?-type <name>? ?<path>?
+#
+# Clients use this to query the children of a particular element
+# within the entire data structure.  This is just like the "element"
+# method, but it returns the children of the element instead of the
+# element itself.  If the optional -type argument is specified, then
+# the return list is restricted to children of the specified type.
+#
+# By default, this method returns a list of component names "type(id)".
+# This is changed by setting the -flavor argument to "id" (for tail
+# names of all children), to "type" (for the types of all children),
+# to "object" (for a list of objects representing the DOM nodes for
+# all children).
+# ----------------------------------------------------------------------
+itcl::body Rappture::LibraryObj::children {args} {
+    array set params {
+        -flavor component
+        -type ""
+    }
     while {[llength $args] > 1} {
         set first [lindex $args 0]
-        set args [lrange $args 1 end]
-        if {$first != "-text" && $first != "-object"} {
-            error "bad option \"$first\": should be -object, -text"
-        }
-        set what $first
-    }
-    if {[llength $args] != 1} {
-        error "wrong # args: should be \"put path ?-text? ?-object? string\""
-    }
-    set str [lindex $args 0]
-
-    switch -- $what {
-        -text {
-            set node [find -create $path]
-            foreach n [$node childNodes] {
-                if {[$n nodeType] == "TEXT_NODE"} {
-                    $n delete
-                }
-            }
-            set n [$_document createText $str]
-            $node appendChild $n
-        }
-        -object {
-            error "not yet implemented"
-        }
+        if {[string index $first 0] == "-"} {
+            set choices [array names params]
+            if {[lsearch $choices $first] < 0} {
+                error "bad option \"$first\": should be [join [lsort $choices] {, }]"
+            }
+            set params($first) [lindex $args 1]
+            set args [lrange $args 2 end]
+        } else {
+            break
+        }
+    }
+    if {[llength $args] > 1} {
+        error "wrong # args: should be \"children ?-flavor fval? ?-type name? ?path?\""
+    }
+    set path [lindex $args 0]
+
+    set node [find $path]
+    if {$node == ""} {
+        return ""
+    }
+
+    set nlist ""
+    foreach n [$node childNodes] {
+        set type [$n nodeName]
+        if {[regexp {^#} $type]} {
+            continue
+        }
+        if {$params(-type) != "" && $params(-type) != $type} {
+            continue
+        }
+        lappend nlist $n
+    }
+
+    set rlist ""
+    switch -- $params(-flavor) {
+      object {
+          foreach n $nlist {
+              lappend rlist [::Rappture::LibraryObj ::#auto $n]
+          }
+      }
+      component {
+          foreach n $nlist {
+              lappend rlist [node2comp $n]
+          }
+      }
+      id {
+          foreach n $nlist {
+              lappend rlist [node2name $n]
+          }
+      }
+      type {
+          foreach n $nlist {
+              lappend rlist [$n nodeName]
+          }
+      }
+      default {
+          error "bad flavor \"$params(-flavor)\": should be object, id, type, component"
+      }
+    }
+    return $rlist
+}
+
+# ----------------------------------------------------------------------
+# USAGE: get ?<path>?
+#
+# Clients use this to query the value of a node.  If the path is not
+# specified, it returns the value associated with the root node.
+# Otherwise, it returns the value for the element specified by the
+# path.
+# ----------------------------------------------------------------------
+itcl::body Rappture::LibraryObj::get {{path ""}} {
+    set node [find $path]
+    if {$node == ""} {
+        return ""
+    }
+    return [string trim [$node text]]
+}
+
+# ----------------------------------------------------------------------
+# USAGE: put ?-append yes? ?-id num? ?<path>? <string>
+#
+# Clients use this to set the value of a node.  If the path is not
+# specified, it sets the value for the root node.  Otherwise, it sets
+# the value for the element specified by the path.  If the value is a
+# string, then it is treated as the text within the tag at the tail
+# of the path.  If it is a DOM node or a library, then it is inserted
+# into the tree at the specified path.
+#
+# If the optional id is specified, then it sets the identifier for
+# the tag at the tail of the path.  If the optional append flag is
+# specified, then the value is appended to the current value.
+# Otherwise, the value replaces the current value.
+# ----------------------------------------------------------------------
+itcl::body Rappture::LibraryObj::put {args} {
+    array set params {
+        -id ""
+        -append no
+    }
+    while {[llength $args] > 1} {
+        set first [lindex $args 0]
+        if {[string index $first 0] == "-"} {
+            set choices [array names params]
+            if {[lsearch $choices $first] < 0} {
+                error "bad option \"$first\": should be [join [lsort $choices] {, }]"
+            }
+            set params($first) [lindex $args 1]
+            set args [lrange $args 2 end]
+        } else {
+            break
+        }
+    }
+    if {[llength $args] > 2} {
+        error "wrong # args: should be \"put ?-append bval? ?-id num? ?path? string\""
+    }
+    if {[llength $args] == 2} {
+        set path [lindex $args 0]
+        set str [lindex $args 1]
+    } else {
+        set path ""
+        set str [lindex $args 0]
+    }
+    set node [find -create $path]
+
+    #
+    # Clean up any nodes that don't belong.  If we're appending
+    # the value, then clean up only child <tag> nodes.  Otherwise,
+    # clean up all nodes.
+    #
+    set nlist ""
+    if {$params(-append)} {
+        foreach n [$node childNodes] {
+            if {[$n nodeType] != "TEXT_NODE"} {
+                lappend nlist $n
+            }
+        }
+    } else {
+        set nlist [$node childNodes]
+    }
+    foreach n $nlist {
+        $n delete
+    }
+
+    if {[Rappture::library isvalid $str]} {
+        error "not yet implemented"
+    } else {
+        set n [$_document createText $str]
+        $node appendChild $n
+        if {"" != $params(-id)} {
+            $node setAttribute id $params(-id)
+        }
+    }
+    return ""
+}
+
+# ----------------------------------------------------------------------
+# USAGE: remove ?<path>?
+#
+# Clients use this to remove the specified node.  Removes the node
+# from the tree.
+# ----------------------------------------------------------------------
+itcl::body Rappture::LibraryObj::remove {{path ""}} {
+    set node [find $path]
+    if {$node != ""} {
+        $node delete
     }
 }
@@ -248,7 +377,22 @@
 # USAGE: find ?-create? <path>
 #
-# Searches from the starting node for this object according to the
-# given <path>.  Returns the node found at the end of the path,
-# or "" if the node was not found.
+# Used internally to find a particular element within the root node
+# according to the path, which is a string of the form
+# "typeNN(id).typeNN(id). ...", where each "type" is a tag <type>;
+# if the optional NN is specified, it indicates an index for the
+# <type> tag within its parent; if the optional (id) part is included,
+# it indicates a tag of the form <type id="id">.
+#
+# By default, it looks for an element along the path and returns None
+# if not found.  If the create flag is set, it creates various elements
+# along the path as it goes.  This is useful for "put" operations.
+#
+# If you include "#" instead of a specific number, a node will be
+# created automatically with a new number.  For example, the path
+# "foo.bar#" called the first time will create "foo.bar", the second
+# time "foo.bar1", the third time "foo.bar2" and so forth.
+#
+# Returns an object representing the element indicated by the path,
+# or "" if the path is not found.
 # ----------------------------------------------------------------------
 itcl::body Rappture::LibraryObj::find {args} {
@@ -266,20 +410,27 @@
         error "wrong # args: should be \"find ?-create? path\""
     }
-    set path [path2list [lindex $args 0]]
+    set path [lindex $args 0]
+
+    if {$path == ""} {
+        return $_node
+    }
+    set path [path2list $path]
 
     #
     # Follow the given path and look for all of the parts.
     #
-    set nnum 1
     set lastnode $_node
     set node $lastnode
     foreach part $path {
-        if {[regexp {^([a-zA-Z_]+)([0-9]*)$} $part match type index]
-              && ($index != "" || [$node getElementsByTagName $type] != "")} {
-            #
-            # If the name is like "type2", then look for elements with
-            # the type name and return the one with the given index.
-            # If the name is like "type", then assume the index is 0.
-            #
+        if {![regexp {^(([a-zA-Z_]+#?)([0-9]*))?(\(([^\)]+)\))?$} $part \
+               match dummy type index dummy name]} {
+            error "bad path component \"$part\""
+        }
+        #
+        # If the name is like "type2", then look for elements with
+        # the type name and return the one with the given index.
+        # If the name is like "type", then assume the index is 0.
+        #
+        if {$name == ""} {
             if {$index == ""} {
                 set index 0
@@ -287,13 +438,19 @@
             set nlist [$node getElementsByTagName $type]
             set node [lindex $nlist $index]
-        } elseif {[regexp {^([a-zA-Z_]+)\(([^\)]*)\)$} $part match type name]} {
+        } else {
             #
-            # If the name is like "type(name)", then look for elements
+            # If the name is like "type(id)", then look for elements
             # that match the type and see if one has the requested name.
+            # if the name is like "(id)", then look for any elements
+            # with the requested name.
             #
-            set nlist [$node getElementsByTagName $type]
+            if {$type != ""} {
+                set nlist [$node getElementsByTagName $type]
+            } else {
+                set nlist [$node childNodes]
+            }
             set found 0
             foreach n $nlist {
-                if {[catch {$n getAttribute name} tag]} { set tag "" }
+                if {[catch {$n getAttribute id} tag]} { set tag "" }
                 if {$tag == $name} {
                     set found 1
@@ -302,20 +459,4 @@
             }
             set node [expr {($found) ? $n : ""}]
-        } else {
-            #
-            # Otherwise, the name might be something like "name".
-            # Scan through all elements and see if any has the
-            # matching name.
-            #
-            set nlist [$node childNodes]
-            set found 0
-            foreach n $nlist {
-                if {[catch {$n getAttribute name} tag]} { set tag "" }
-                if {$tag == $part} {
-                    set found 1
-                    break
-                }
-            }
-            set node [expr {($found) ? $n : ""}]
         }
 
@@ -324,17 +465,44 @@
                 return ""
             }
+
+            #
+            # If the "create" flag is set, then create a node
+            # with the specified "type(id)" and continue on.
+            # If the type is "type#", then create a node with
+            # an automatic number.
+            #
             if {![regexp {^([^\(]+)\(([^\)]+)\)$} $part match type name]} {
                 set type $part
                 set name ""
             }
-            set node [$_document createElement $type]
-            $lastnode appendChild $node
-
+
+            if {[string match *# $type]} {
+                set type [string trimright $type #]
+                set node [$_document createElement $type]
+
+                # find the last node of same type and append there
+                set pos ""
+                foreach n [$lastnode childNodes] {
+                    if {[$n nodeName] == $type} {
+                        set pos $n
+                    }
+                }
+                if {$pos != ""} {
+                    set pos [$pos nextSibling]
+                }
+                if {$pos != ""} {
+                    $lastnode insertBefore $node $pos
+                } else {
+                    $lastnode appendChild $node
+                }
+            } else {
+                set node [$_document createElement $type]
+                $lastnode appendChild $node
+            }
             if {"" != $name} {
-                $node setAttribute name $name
+                $node setAttribute id $name
             }
         }
         set lastnode $node
-        incr nnum
     }
     return $node
@@ -366,2 +534,57 @@
     return $path
 }
+
+# ----------------------------------------------------------------------
+# USAGE: node2name <node>
+#
+# Used internally to create a name for the specified node.  If the
+# node doesn't have a specific name ("id" attribute) then a name of
+# the form "type123" is constructed.
+# ----------------------------------------------------------------------
+itcl::body Rappture::LibraryObj::node2name {node} {
+    if {[catch {$node getAttribute id} name]} { set name "" }
+    if {$name == ""} {
+        set pnode [$node parentNode]
+        if {$pnode == ""} {
+            return ""
+        }
+        set type [$node nodeName]
+        set siblings [$pnode getElementsByTagName $type]
+        set index [lsearch $siblings $node]
+        if {$index == 0} {
+            set name $type
+        } else {
+            set name "$type$index"
+        }
+    }
+    return $name
+}
+
+# ----------------------------------------------------------------------
+# USAGE: node2comp <node>
+#
+# Used internally to create a path component name for the specified
+# node.  A path component name has the form "type(id)" or just
+# "type##" if the node doesn't have a name.  This name can be used
+# in a path to uniquely address the component.
+# ----------------------------------------------------------------------
+itcl::body Rappture::LibraryObj::node2comp {node} {
+    set type [$node nodeName]
+    if {[catch {$node getAttribute id} name]} { set name "" }
+    if {$name == ""} {
+        set pnode [$node parentNode]
+        if {$pnode == ""} {
+            return ""
+        }
+        set siblings [$pnode getElementsByTagName $type]
+        set index [lsearch $siblings $node]
+        if {$index == 0} {
+            set name $type
+        } else {
+            set name "$type$index"
+        }
+    } else {
+        set name "${type}($name)"
+    }
+    return $name
+}
