
# old 4.1 interface

array set famboco_noedit {
    maxsize 1 width 1 height 1 ratio 1 minsize 1 dev 1 members 1 color 1
    min 1 level 1
}

proc famboco_load_bcinfo {file} {
    global famboco_info param_info
    
    catch {unset famboco_info}
    catch {unset param_info}
    
    if {$file == ""} return
    
    set category_name ""
    set bname ""
    
    set fp [open $file r]
    while {![eof $fp]} {
	set line [string trim [gets $fp]]
	if {($line == "") || [string match \#* line]} continue
	
	while {[string match *\\\\ $line] && ![eof $fp]} {
	    set line [string trimright $line \\]
	    append line " [string trim [gets $fp]]"
	}
	
	switch -- [lindex $line 0] {
	    format {
		set famboco_info(htmldir) ""
		foreach {n v} [lrange $line 1 end] {
		    set famboco_info($n) $v
		}
		set famboco_info(bctypes) ""
		set famboco_info(ptypes) ""
		set fname $famboco_info(name)
	    }
	    category {
		if ![info exists fname] { error "no format line found yet" }
		set name ""
		foreach {n v} [lrange $line 1 end] { set $n $v }
		set category_name $name
	    }
	    bctype {
		if ![info exists fname] { error "no format line found yet" }
		set name ""; set title ""; set dims ""
		set html ""; set bubble ""; set dims ""
		set pos 0
		foreach {n v} [lrange $line 1 end] { set $n $v }
		if {$name == ""} { error "no arg name in $line" }
		
		if [info exists famboco_info(title,$name)] {
		    error "BC or param $name repeated twice"
		}
		set famboco_info(title,$name) $title
		set famboco_info(dims,$name) $dims
		set famboco_info(html,$name) $html
		set famboco_info(bubble,$name) $bubble
		set famboco_info(pos,$name) $pos
		set famboco_info(category,$name) $category_name
		foreach a {args pre_edit post_edit} {
		    set famboco_info($a,$name) ""
		}
		lappend famboco_info(bctypes) $name
		set bname $name
	    }
	    arg {
		if ![info exists fname] { error "no format line found yet" }
		if ![info exists bname] { error "no bctype line found yet" }
		lappend famboco_info(args,$bname) $line
	    }
	    pre_edit {
		if ![info exists fname] { error "no format line found yet" }
		if ![info exists bname] { error "no bctype line found yet" }
		set famboco_info(pre_edit,$bname) $line
	    }
	    post_edit {
		if ![info exists fname] { error "no format line found yet" }
		if ![info exists bname] { error "no bctype line found yet" }
		set famboco_info(post_edit,$bname) $line
	    }
	    label {
		if ![info exists fname] { error "no format line found yet" }
		if ![info exists bname] { error "no bctype line found yet" }
		lappend famboco_info(args,$bname) $line
	    }
	    stdarg {
		error "param not yet supported"
		if {[llength $line] == 3} {
		    if ![info exists fname] { error "no format line found yet"}
		    if ![info exists bname] { error "no bctype line found yet"}
		    lappend famboco_info(args,$bname) $line
		} else {
		    set famboco_info(stdargs,[lindex $line 1]) $line
		}
	    }
	    stdbctype {
		if ![info exists fname] { error "no format line found yet" }
		error "stdbctype not yet supported"
	    }
	    param {
		if ![info exists fname] { error "no format line found yet" }
		set name ""; set title ""; set dims ""
		set html ""; set bubble ""
		foreach {n v} [lrange $line 1 end] { set $n $v }
		if {$name == ""} { error "no arg name in $line" }
		
		if [info exists famboco_info(title,$name)] {
		    error "BC or param $name repeated twice"
		}
		set famboco_info(title,$name) $title
		set famboco_info(dims,$name) $dims
		set famboco_info(html,$name) $html
		set famboco_info(bubble,$name) $bubble
		set famboco_info(args,$name) ""
		set famboco_info(category,$name) $category_name
		lappend famboco_info(ptypes) $name
		set bname $name
	    }
	    param_node {
		famboco_parse_param_node "" $line $fp
	    }
	    represent_blank_float_args {
		set famboco_info(represent_blank) [lindex $line 1]
	    }
	}
    }
    close $fp
    
    # These are special ones.
    
    set famboco_info(title,THIN) "Thin wall"
    set famboco_info(dims,THIN) ""
    set famboco_info(html,THIN) ""
    set famboco_info(bubble,THIN) "Define thin walls for surfaces"
    set famboco_info(args,THIN) ""
    set famboco_info(category,THIN) "Meshing directives"
    lappend famboco_info(bctypes) THIN
    
    set famboco_info(title,INT) "Internal wall"
    set famboco_info(dims,INT) ""
    set famboco_info(html,INT) ""
    set famboco_info(bubble,INT) "Split faces on this surface"
    set famboco_info(args,INT) ""
    set famboco_info(category,INT) "Meshing directives"
    lappend famboco_info(bctypes) INT
}

proc famboco_parse_param_node {parent line fp} {
    global param_info
    
    set name ""; set title ""; set multiple 0
    set html ""; set bubble ""; set no_create_or_delete 0
    foreach {n v} [lrange $line 1 end] { set $n $v }
    if {$name == ""} { error "no arg name in $line" }
    
    if {$name == "default"} { error "the name of a node can't be default" }
    if {$name == "create_new_node"} { error "the name of a node can't be create_new_node" }
    
    set path ${parent}:$name
    
    if [info exists param_info(title,$path)] {
	error "Param node $path repeated twice"
    }
    
    lappend param_info(children,$parent) $name
    
    set param_info(title,$path) $title
    set param_info(multiple,$path) $multiple
    set param_info(no_create_or_delete,$path) $no_create_or_delete
    set param_info(html,$path) $html
    set param_info(bubble,$path) $bubble
    set param_info(constraints,$path) ""
    foreach a {args pre_edit post_edit} {
	set param_info($a,$path) ""
    }
    
    while {![eof $fp]} {
	set line [string trim [gets $fp]]
	if {($line == "") || [string match \#* line]} continue
	
	while {[string match *\\\\ $line] && ![eof $fp]} {
	    set line [string trimright $line \\]
	    append line " [string trim [gets $fp]]"
	}
	
	switch -- [lindex $line 0] {
	    constraint {
		lappend param_info(constraints,$path) $line
	    }
	    arg {
		lappend param_info(args,$path) $line
	    }
	    
	    end {
		if {[lindex $line 1] != $name} { 
		    error "$line doesn't match $name"
		}
		return
	    }
	    param_node {
		famboco_parse_param_node $path $line $fp
	    }
	    pre_edit {
		set param_info(pre_edit,$path) [lindex $line 1]
	    }
	    post_edit {
		set param_info(post_edit,$path) [lindex $line 1]
	    }
	}
    }
}

proc famboco_load {file} {
    set list ""
    set fp [open $file r]
    while {![eof $fp]} {
	set line [gets $fp]
	while 1 {
	    set ll [expr [string length $line] - 1]
	    if {[string index $line $ll] == "\\"} {
		incr ll -1
		set line [string range $line 0 $ll][gets $fp]
	    } else {
		break
	    }
	}
	lappend list $line
    }
    close $fp
    famboco_load_from_list $list
}

proc famboco_load_old {file} {
    global boco_nfamilies boco_family_name boco_data
    global famboco_data
    
    boco_load $file
    
    catch {unset famboco_data}
    for {set i 1} {$i < $boco_nfamilies} {incr i} {
	set name $boco_family_name($i)
	set famboco_data($name) ""
	foreach bc $boco_data($name) {
	    lappend famboco_data($name) [concat 1 $bc]
	}
    }
}

proc famboco_load_from_list {list} {
    global famboco_info famboco_data famboco_no_warn_no_bc famboco_noedit
    
    catch {unset famboco_data}
    
    foreach line $list {
	set line [string trim $line]
	if {($line == "") || ([string match \#* $line] && \
				  ![string match \#!* $line])} continue
	set act 1
	if [string match \#!* $line] {
	    set act 0
	    set line [string range $line 2 end]
	}
	set fam [lindex $line 0]
	set bc $act
	foreach arg [lrange $line 1 end] { lappend bc $arg }
	if {[llength $bc] > 1} {
	    if {![info exists famboco_info(title,[lindex $bc 1])] && \
		    ![info exists famboco_noedit([lindex $bc 1])] && \
		    ![info exists famboco_no_warn_no_bc]} {
		if [info exists famboco_info] {
		    mess "Warning: no bc type \"[lindex $bc 1]\" defined\n" red
		}
	    }
	    lappend famboco_data($fam) $bc
	} elseif ![info exists famboco_data($fam)] {
	    set famboco_data($fam) ""
	}
    }
}

proc famboco_save {file {fams ""}} {
    global famboco_data whoami
    
    if [info exists whoami] { set by " written by $whoami" } else { set by "" }

    set fp [open $file w]
    puts $fp "\# family_boco file$by"
    foreach bc [famboco_save_to_list $fams] {
	puts $fp $bc
    }
    close $fp
}

proc famboco_save_old {file {fams ""}} {
    global famboco_data
    global boco_old_nfamilies boco_old_family_name boco_old_data
    
    if {$fams == ""} {
	set fams [lsort [array names famboco_data]]
    }
    set boco_old_nfamilies [llength $fams]
    catch {unset boco_old_data}
    set i 1
    foreach f $fams {
	set boco_old_family_name($i) $f
	set boco_old_data($f) ""
	foreach bc $famboco_data($f) {
	    lappend boco_old_data($f) [lrange $bc 1 end]
	}
	incr i
    }
    boco_save $file boco_old
}

proc famboco_save_to_list {{fams ""}} {
    global famboco_data
    
    if {$fams == ""} {
	set fams [lsort [array names famboco_data]]
    }
    
    set list ""
    foreach fam $fams {
	set got 0
	foreach bc $famboco_data($fam) {
	    set line ""
	    if ![lindex $bc 0] {
		append line "\#!"
	    } else {
		set got 1
	    }
	    append line "\"$fam\" \"[lindex $bc 1]\""
	    foreach a [lrange $bc 2 end] {
		if {[scan $a %g xxx] == 1} {
		    append line " $a"
		} elseif {$a != ""} {
		    append line " \"$a\""
		}
	    }
	    lappend list $line
	}
	if !$got {
	    lappend list "\"$fam\""
	}
    }
    return $list
}

# ---------------------------------------------------------------

proc famboco_get_info {sol} {
    global famboco_edit famboco_info env
    
    set famboco_edit(solver) $sol

    if {$sol == "Generic"} {
	famboco_load_bcinfo ""
    } else {
	if ![famboco_set_solver "" $sol] { return 0 }
    }
    
    set famboco_edit(only_un) 0
    set famboco_edit(curnum) 0
    set famboco_edit(curfam) ""
    set famboco_edit(clipboard) ""
    set famboco_edit(clipboard_full) 0
    
    if [info exists famboco_info(htmldir)] {
	set famboco_edit(htmldir) [glob $famboco_info(htmldir)]
	if {[file pathtype $famboco_edit(htmldir)] == "relative"} {
	    set famboco_edit(htmldir) $env(ICEM_ACN)/$famboco_edit(htmldir)
	}
    } else {
	set famboco_edit(htmldir) $env(ICEM_ACN)/help
    }
    return 1
}

proc famboco_edit {{sol Generic} {show_fam_func ""}} {
    global famboco_info famboco_data fam_visible fam_dimension famboco_edit
    global env
    
    if ![famboco_get_info $sol] { return 0 }
    
    set famboco_edit(show_fam_func) $show_fam_func
    
    set backup [array get famboco_data]
    
    foreach f [array names fam_visible] {
	if ![info exists famboco_data($f)] { set famboco_data($f) "" }
    }
    
    form_kill .famboco
    set d [form_init .famboco "Family boundary conditions"]
    
    form_label $d.soll "Solver" {0 0}
    form_entry $d.soln famboco_edit(solver) string {0 1 -cols 2}
    $d.soln configure -width 10
    
    if [info exists fam_dimension] {
    } else {
	form_listbox $d.faml "Families" "" "famboco_edit_setfam $d 0" "" 6x12 \
	    {1 0 -cols 3}
    }
    form_checkbutton $d.onlyun "Only unassigned" famboco_edit(only_un) \
	{10 0 -cols 3}
    $d.onlyun configure -command "famboco_edit_populate $d"
    
    set famboco_edit(bcwin) [form_scroller $d.bcs 300 {0 3 -rows 11} 1 400]
    
    form_frame $d.buts flat 0 {11 0 -cols 4}
    form_button $d.buts.sets "Select solver" "famboco_set_solver $d" {0 0}
    form_button $d.buts.nfam "New family" "famboco_edit_newfam $d" {0 1}
    form_button $d.buts.nbc "New b. c." "famboco_edit_newbc $d" {0 2} \
	famboco_edit(curnum)
    form_button $d.buts.pbc "Paste b. c." "famboco_edit_pastebc $d" {0 3} \
	{{famboco_edit(clipboard_full) 1}}
    
    form_finish bot 1 0
    
    famboco_edit_populate $d
    
    if ![form_wait .famboco] {
	unset famboco_data
	array set famboco_data $backup
	if {($show_fam_func != "") && ($famboco_edit(curfam) != "")} {
	    $show_fam_func $famboco_edit(curfam) 0
	}
	form_kill .famboco
	return 0
    }
    famboco_edit_getfam 0
    form_kill .famboco
    return 1
}

proc famboco_is_bcinfo {f} {
    global famboco_edit_format_tmp
    
    if ![file isfile $f] { return 0 }
    if {[file extension $f] != ".bcinfo"} { return 0 }
    
    set ok 0
    set fp [open $f r]
    set nl 0
    while 1 {
	set line [string trim [gets $fp]]
	if [string match format* $line] {
	    set famboco_edit_format_tmp [lindex $line 2]
	    set ok 1
	    break
	}
	if {[incr nl] > 100} break  ;# better not be too many comments here
    }
    close $fp
    return $ok
}

proc famboco_set_solver {d {sol ""}} {
    global famboco_edit env famboco_edit_format_tmp solution
    
    if {[info exists solution(solver)] && ($solution(solver) != "") &&
	($solution(solver) == $sol)} { return 1 }
    
    if ![info exists famboco_edit(solver_list)] {
        set tdir $env(ICEM_ACN)/icemcfd/output-interfaces
	set files [concat [glob -nocomplain $tdir/*.bcinfo] \
		       [glob -nocomplain ./*.bcinfo]]
	foreach f $files {
	    if [famboco_is_bcinfo $f] {
		set file_for($famboco_edit_format_tmp) $f
	    }
	}
	set famboco_edit(solver_list) \
	    [list [list "Generic" ""] \
		 [list "Select custom .bcinfo file" ""]]
	foreach f [array names file_for] {
	    lappend famboco_edit(solver_list) [list $f $file_for($f)]
	}
    }
    set names2 ""
    foreach n $famboco_edit(solver_list) {
	set file_for([lindex $n 0]) [lindex $n 1]
	if {[lindex $n 1] != ""} {
	    lappend names2 [lindex $n 0]
	}
    }

    set names [concat {{Generic} {Select custom .bcinfo file}} [lsort $names2]]
    if {$sol == ""} {
	set mess " Select a solver, or a custom boundary\n\
		condition information file.  The Generic\n\
		solver type gives you the old-style BC\n\
		editing capability (similar to the mesher\n\
		interfaces and Mulcad)."
	set sol [choose_one $mess $names "" -0-0]
    }
    if {$sol == ""} { return 0 }
    if {$sol == "Select custom .bcinfo file"} {
	set file [file_selection -message "Select a custom .bcinfo file" \
		     -ok_func famboco_is_bcinfo]
	if {$file == ""} { return 0 }
	
	# Get the format again.
	famboco_is_bcinfo $file
	set famboco_edit(solver) $famboco_edit_format_tmp
	famboco_load_bcinfo $file
	if {$d != ""} {
	    famboco_edit_setfam $d 0 $famboco_edit(curfam)
	}
    } else {
	if ![info exists file_for($sol)] {
	    mess "Warning: no solver BC information for $sol\n" red
	    return 1
	}
	set famboco_edit(solver) $sol
	famboco_load_bcinfo $file_for($sol)
	if {$d != ""} {
	    famboco_edit_setfam $d 0 $famboco_edit(curfam)
	}
    }
    if {[info commands solution_set_solver] != ""} {
	solution_set_solver $famboco_edit(solver) 0
    }
    return 1
}

proc famboco_edit_setfam {d params fam} {
    global famboco_data famboco_param_data famboco_edit famboco_info
    global famboco_noedit
    
    famboco_edit_getfam $params
    
    foreach w [winfo children $famboco_edit(bcwin)] { destroy $w }
    
    help_clear $famboco_edit(bcwin)
    bubble_clear $famboco_edit(bcwin)
    
    set famboco_edit(curfam) $fam
    if {$fam == ""} return
    
    set famboco_edit(curnum) 1
    set famboco_edit(noedit) ""
    
    if $params {
	foreach p $famboco_info(ptypes) {
	    if {$famboco_info(category,$p) == $fam} {
		if [info exists famboco_param_data($p)] {
		    famboco_edit_onebc 1 [concat 1 $p $famboco_param_data($p)]
		} else {
		    famboco_edit_newparam $p
		}
	    }
	}
	
    } else {
	foreach bc $famboco_data($fam) {
	    if [info exists famboco_noedit([lindex $bc 1])] {
		lappend famboco_edit(noedit) $bc
	    } else {
		famboco_edit_onebc 0 $bc
	    }
	}
    }
    set famboco_edit(curfam) $fam
    form_scroller_update $d.bcs 1 1
    
    if {$famboco_edit(show_fam_func) != ""} {
	$famboco_edit(show_fam_func) $fam 1
    }
}

proc famboco_edit_onebc {params bc} {
    global famboco_info famboco_data fam_dimension famboco_edit
    
    set num $famboco_edit(curnum)
    incr famboco_edit(curnum)
    set d $famboco_edit(bcwin).bc$num
    form_frame $d ridge 2 "$num 0"
    
    if !$params {
	catch {unset famboco_edit(active,$num)} ;# kill the trace
	set famboco_edit(active,$num) [lindex $bc 0]
    }
    set type [lindex $bc 1]
    set type2 [lindex $bc 2]  ;# some bc types are like this
    set famboco_edit(swap_arg01,$num) 0
    set bcargs [lrange $bc 2 end]
    if [info exists famboco_info(title,$type)] {
	set title "$famboco_info(title,$type)"
	set dims $famboco_info(dims,$type)
	set args $famboco_info(args,$type)
	set html $famboco_info(html,$type)
	set bubble $famboco_info(bubble,$type)
	
    } elseif {[info exists famboco_info(title,$type2)] &&
	      ($famboco_info(pos,$type2) == 1)} {
	set title "$famboco_info(title,$type2)"
	set dims $famboco_info(dims,$type2)
	set args $famboco_info(args,$type2)
	set html $famboco_info(html,$type2)
	set bubble $famboco_info(bubble,$type2)
	
	set bcargs [concat [list $type] [lrange $bc 3 end]]
	set type $type2
	set famboco_edit(swap_arg01,$num) 1
	
    } else {
	set title "$type"
	set dims ""
	set args ""
	set html ""
	set bubble ""
	foreach {name def} {
	    "String 2" "" "Integer" 0 "Real 1" 0.0 \
		"Real 2" 0.0 "Real 3" 0.0 "Real 4" 0.0 \
		"Real 5" 0.0 "Real 6" 0.0 "Real 7" 0.0 \
		"Real 8" 0.0 "Real 9" 0.0} {
	    lappend args [list arg type string title $name default $def]
	}
    }
    
    set bcargs2 ""
    foreach b $bcargs {
	if {[info exists famboco_info(represent_blank)] &&
	    ($famboco_info(represent_blank) == $b)} { set b "" }
	lappend bcargs2 $b
    }
    
    set famboco_edit(type,$num) $type
    famboco_edit_fillin_args $d famboco_edit $num $title $args $bcargs2 $html \
	$bubble $params ""
}

proc famboco_edit_fillin_args {d varname num title args bcargs html bubble params nodename_binding} {
    global $varname
    
    set bctitle $title
    set ${varname}(node_title,$num) $title
    form_label_variable $d.name ${varname}(node_title,$num) {0 0 -cols -}
    form_colwidth $d 0 2 M
    set ${varname}(count,$num) [llength $args]
    
    if {$html != ""} {
	help_define $d.name "" [set ${varname}(htmldir)]/[lindex $html 0] \
	    [lindex $html 1]
    }
    if {$bubble != ""} {
	bubble_define $d.name $bubble
    }
    
    # First set the pos values for all the args.
    set defp 1
    foreach a $args {
	set pos ""
	foreach {n p} [lrange $a 1 end] {
	    if {$n == "pos"} { set pos $p }
	}
	if {$pos == ""} { set pos $defp; incr defp }
	if {$pos == 0} { set pos 1 }
	set argpos($a) $pos
    }
    
    set i 1
    foreach a $args {
	set title none; set name ""
	foreach {n p} [lrange $a 1 end] { set $n $p }
	if {$name == ""} {
	    set key_for_title($title) $i
	} else {
	    set key_for_title($title) $name
	}
	incr i
    }
    
    # Let's do all these at the end
    set traces_to_do ""
    
    set i 1
    foreach a $args {
	set v [lindex $bcargs [expr $argpos($a) - 1]]
	
	set type ""; set title ""; set default ""; set options ""
	set choices ""; set active ""; set html ""; set bubble ""
	set variable ""; set name ""; set nodename 0; set command ""
	set width ""; set allow_others 0; set gui_type "";
	set post_choice ""
	
	foreach {n p} [lrange $a 1 end] { set $n $p }
	set is_label [expr {[lindex $a 0] == "label"}]
	
	if {$name == ""} { set name $i }
	
	catch {unset ${varname}(val,$num,$name)} ;# kill the trace
	set ${varname}(argpos,$num,$name) $argpos($a)
	
	set ${varname}(opts,$num,$name) $choices
	set ${varname}(allow_others,$num,$name) $allow_others
	set ${varname}(valtype,$num,$name) $type
	
	if $params {
	    set act ""
	} else {
	    set act ${varname}(active,$num)
	}
	foreach tt $active {
	    set acttitle [lindex $tt 0]
	    set poss [lrange $tt 1 end]
	    if ![info exists key_for_title($acttitle)] {
		# Maybe this goes somewhere else ...
		if [string match */* $acttitle] {
		    puts "Ignoring $acttitle"
		    continue
		}
		error "No arg $acttitle exists in bctype $bctitle\
			(active data is $active)"
	    }
	    set vn ${varname}(val,$num,$key_for_title($acttitle))
	    lappend act [concat [list $vn] $poss]
	}
	set ${varname}(activedata,$num,$name) $act
	
	set wid $d.val$name
	set put_help $wid
	
	set pickf ""; set pickf1 ""
	if {$post_choice != ""} {
	    set pickf "$post_choice $num \"$name\" \"${varname}(val,$num,$name)\""
	    set pickf1 "$pickf \\\"\[set \"${varname}(val,$num,$name)\"\]\\\""
	}
	
	if $is_label {
	    form_label_variable $d.lab$name ${varname}(val,$num,$name) "$i 1 -cols 2" $act
	    
	    if {($variable == "") || ($options == "")} {
		error "variable and options must be set"
	    }
	    # Figure out what variable that is.
	    set vn ${varname}(val,$num,$key_for_title($variable))
	    set cmd ""
	    set gotdef 0
	    foreach opt $options {
		set vals [lindex $opt 0]
		set text [lindex $opt 1]
		if {$vals == ""} {
		    append cmd " { set ${varname}(val,$num,$name) \{$text\} }"
		    set gotdef 1
		} else {
		    foreach v $vals {
			append cmd "if {\$$vn == \"$v\"} { set ${varname}(val,$num,$name) \{$text\} } else"
		    }
		}
	    }
	    if !$gotdef {
		append cmd " { set ${varname}(val,$num,$name) \{\} }"
	    }
	    append cmd "; list"
	    
	    lappend traces_to_do [list trace variable $vn w $cmd] $cmd
	    	    
	} elseif {$type == "button"} {
	    form_button $wid $title $command "$i 1 -cols 2" $act
	
	} elseif {$type == "fixed"} {
	    set ${varname}(val,$num,$name) $v
	    
	} elseif {$choices == ""} {
	    form_label $d.lab$name $title "$i 1" $act
	    set ${varname}(val,$num,$name) $v
	    
	    if {$gui_type == "filename"} {
		form_frame $wid flat 0 "$i 2"
		file_select_entry $wid.e ${varname}(val,$num,$name) 0 0 \
		    $title "0 0" $act "" "" "" "" "" $pickf
		set wid $wid.e
		if {$width == ""} { set width 12 }
	    } else {
		form_entry $wid ${varname}(val,$num,$name) $type \
		    "$i 2" $act
	    }
	    
	    if $nodename {
		bind $wid <Return> $nodename_binding
	    } else {
		bind $wid <Return> ""
	    }
	    if {$width != ""} { $wid configure -width $width }
	    
	} elseif {$choices == "0 1"} {
	    set ${varname}(val,$num,$name) $v
	    form_checkbutton $wid $title ${varname}(val,$num,$name) \
		"$i 1 -cols 2" $act
	    $wid configure -command $pickf1

	} else {
	    form_label $d.lab$name $title "$i 1" $act
	    set optnames ""
	    set ${varname}(val,$num,$name) ""
	    foreach o $choices {
		set internal_val [lindex $o 1]
		if {$internal_val == ""} { set internal_val [lindex $o 0] }
		if {$internal_val == $v} {
		    set ${varname}(val,$num,$name) [lindex $o 0]
		}
		lappend optnames [lindex $o 0]
	    }
	    if {[set ${varname}(val,$num,$name)] == ""} {
		if $allow_others {
		    set ${varname}(val,$num,$name) $v
		} else {
		    mess "Warning: can't handle bc $title value $v\n" red
		    set ${varname}(val,$num,$name) [lindex $optnames 0]
		}
	    }
	    form_frame $wid flat 0 "$i 2"
	    
	    if {($gui_type == "radio") && !$allow_others} {
		# Should we put them on a line or multiple lines?
		set totl 0
		foreach o $choices {
		    incr totl [expr [string length [lindex $o 0]] + 2]
		}
		set r 0; set c 0; set j 0
		foreach o $choices {
		    form_radiobutton $wid.ch$j [lindex $o 0] \
			${varname}(val,$num,$name) [lindex $o 0] "$r $c" $act
		    $wid.ch$j configure -command $pickf1
		    lappend put_help $wid.ch$j
		    incr j
		    if {$totl <= 16} { incr c } else { incr r }
		}
		
	    } else {
		futil_listentry $wid.e ${varname}(val,$num,$name) \
		    "list $optnames" "0 0" $act $pickf1 $allow_others
		if {$width == ""} { set width 12 }
		$wid.e configure -width $width
		set wid $wid.e
	    }
	}
	
	foreach hwid $put_help {
	    if {$html != ""} {
		help_define $hwid "" \
		    [set ${varname}(htmldir)]/[lindex $html 0] \
		    [lindex $html 1]
	    }
	    if {$bubble != ""} {
		bubble_define $hwid $bubble
	    }
	}
	incr i
    }
    foreach tr $traces_to_do { eval $tr }
    if !$params {
	form_frame $d.buts flat 0 {100 1 -cols 2}
	# does not work yet ...  form_checkbutton $d.buts.active Active ${varname}(active,$num) {0 0}
	form_button $d.buts.delete Delete "famboco_edit_delete $d $num" {0 1}
	form_button $d.buts.copy Copy "famboco_edit_copy $d $num" {0 2}
    }
}

proc famboco_edit_getfam {params} {
    global famboco_data famboco_param_data famboco_edit famboco_info
    
    set fam $famboco_edit(curfam)
    if {$fam == ""} return
    
    if {$famboco_edit(show_fam_func) != ""} {
	$famboco_edit(show_fam_func) $fam 0
    }
    
    if $params {
	for {set i 1} {$i < $famboco_edit(curnum)} {incr i} {
	    set bc [famboco_edit_getnum $params $i]
	    if {$bc != ""} {
		set famboco_param_data([lindex $bc 0]) [lrange $bc 1 end]
	    }
	}
    } else {
	set famboco_data($fam) ""
	for {set i 1} {$i < $famboco_edit(curnum)} {incr i} {
	    set bc [famboco_edit_getnum $params $i]
	    if {$bc != ""} {
		set bc1 ""
		foreach b $bc {
		    if {$b == -123456} { set b 0 }
		    lappend bc1 $b
		}
		lappend famboco_data($fam) $bc1
	    }
	}
        foreach bc $famboco_edit(noedit) { lappend famboco_data($fam) $bc }
    }
}

proc famboco_edit_getnum {params i} {
    global famboco_data famboco_edit famboco_info
    
    if $params {
	set bc [list $famboco_edit(type,$i)]
    } else {
	if ![info exists famboco_edit(active,$i)] { return "" }
	set bc [list $famboco_edit(active,$i) $famboco_edit(type,$i)]
    }
    set useopts 1
    if [expr {$famboco_edit(solver) == "Generic"}] {
	famboco_make_edit_vals_generic $i
	set useopts 0
    }
    
    set maxpos 0
    for {set j 1} {$j <= $famboco_edit(count,$i)} {incr j} {
	if $useopts {
	    set opts $famboco_edit(opts,$i,$j)
	} else {
	    set opts ""
	}
	set val $famboco_edit(val,$i,$j)
	if {$opts != ""} {
	    set nval ""
	    foreach o $opts {
		if {[lindex $o 0] == $val} {
		    set nval [lindex $o 1]
		    break
		}
	    }
	    if {$nval == ""} {
		mess "Warning: can't handle bc value $val\n" red
		set nval [lindex [lindex $opts 0] 0]
	    }
	    set val $nval
	}
	if  {[info exists famboco_info(represent_blank)] &&
	     ([string trim $val] == "") &&
	     ($famboco_edit(valtype,$i,$j) == "float_blank")} {
	    set val $famboco_info(represent_blank)
	}
	
	set pp $famboco_edit(argpos,$i,$j)
	
	set is_act [form_checkstate $famboco_edit(activedata,$i,$j)]
	
	# Be careful that inactive slots don't overwrite active ones.
	if $is_act {
	    set argfound($pp) $val
	} else {
	    if ![info exists argfound($pp)] {
		set argfound($pp) $val
	    }
	}
	if {$pp > $maxpos} { set maxpos $pp }
    }
    for {set j 1} {$j <= $maxpos} {incr j} {
	if [info exists argfound($j)] {
	    lappend bc $argfound($j)
	} else {
	    lappend bc -123456
	}
    }
    if $famboco_edit(swap_arg01,$i) {
	set bc [concat [list [lindex $bc 0] [lindex $bc 2] [lindex $bc 1]] \
		    [lrange $bc 3 end]]
    }
    
    return $bc
}

proc famboco_make_edit_vals_generic {i} {
    global famboco_edit
    
    set bc ""
    for {set j 1} {$j <= $famboco_edit(count,$i)} {incr j} {
	set opts $famboco_edit(opts,$i,$j)
	set val $famboco_edit(val,$i,$j)
	if {$opts != ""} {
	    set nval ""
	    foreach o $opts {
		if {[lindex $o 0] == $val} {
		    set nval [lindex $o 1]
		    break
		}
	    }
	    if {$nval == ""} {
		mess "Warning: can't handle bc value $val\n" red
		set nval [lindex [lindex $opts 0] 0]
	    }
	    set val $nval
	}
	lappend bc $val
    }
    set str0 [lindex $bc 0]
    if 0 {
	this seems like it is wrong
	if {($str0 == "") || ([scan $str0 %g xx] == 1)} {
	    set str0 ""
	    set n 0
	} else {
	    set n 1
	}
    }
    set n 1
    
    set famboco_edit(count,$i) 11
    set famboco_edit(val,$i,1) $str0
    for {set k 2} {$k < 12} {incr k} {
	set b [lindex $bc $n]; incr n
	if {[scan $b %g xx] != 1} { set b 0.0 }
	set famboco_edit(val,$i,$k) $b
    }
}

proc famboco_edit_delete {subform num} {
    global famboco_edit
    destroy $subform
    unset famboco_edit(active,$num)
}

proc famboco_edit_copy {subform num} {
    global famboco_edit
    
    set famboco_edit(clipboard) [famboco_edit_getnum 0 $num]
    
    if {$famboco_edit(clipboard) != ""} {
	set famboco_edit(clipboard_full) 1
    }
}

proc famboco_edit_pastebc {d} {
    global famboco_edit
    famboco_edit_newbc $d $famboco_edit(clipboard)
    # this is not desirable    set famboco_edit(clipboard_full) 0
}

proc famboco_edit_newbc {d {vals ""}} {
    global famboco_edit famboco_info
    
    if {$vals != ""} {
	set bc $vals
    } elseif [info exists famboco_info(bctypes)] {
	foreach t $famboco_info(bctypes) {
	    lappend bcat($famboco_info(category,$t)) $famboco_info(title,$t)
	    set bt($famboco_info(title,$t)) $t
	}
	set btypes ""
	set offs 0
	foreach cc [lsort [array names bcat]] {
	    if {$cc != ""} {
		lappend btypes "$cc:"
		set offs 1
	    }
	    foreach bb [lsort $bcat($cc)] {
		if $offs {
		    lappend btypes "    $bb"
		} else {
		    lappend btypes "$bb"
		}
	    }
	}
	set ch [string trim [choose_one "Select a BC type" $btypes "" -0-0]]
	if {$ch == ""} return
	if ![info exists bt($ch)] {
	    error "You didn't select a valid type."
	}
	set type $bt($ch)
	
	set bc [list 1 $type]
	set maxpos 0
	set defp 1
	set swap 0
	foreach arg $famboco_info(args,$type) {
	    set def ""; set pos ""
	    foreach {n v} [lrange $arg 1 end] {
		if {$n == "default"} { set def $v }
		if {$n == "pos"} { set pos $v }
	    }
	    if {$pos == ""} { set pos $defp; incr defp }
	    if {$pos == 0} { set pos 1; set swap 1 }
	    if {$pos > $maxpos} { set maxpos $pos }
	    set argpos($pos) $def
	}
	for {set i 1} {$i <= $maxpos} {incr i} {
	    if [info exists argpos($i)] {
		lappend bc $argpos($i)
	    } else {
		lappend bc -123456  ;# safer than ""
	    }
	}
	if $swap {
	    set bc [concat [list [lindex $bc 0] [lindex $bc 2] [lindex $bc 1]] \
			[lrange $bc 3 end]]
	}
	
    } else {
	set type [question_box .query "Query" "Boundary condition type (String 1)?" "" -0-0]
	if {$type == ""} return
	set bc [list 1 [string toupper $type] "" 0 0 0 0 0 0 0 0 0 0]
    }
    
    famboco_edit_onebc 0 $bc
    
    form_scroller_update $d.bcs 0
}

proc famboco_edit_newparam {type} {
    global famboco_edit famboco_info
    
    set bc [list 1 $type]
    set maxpos 0
    set defp 1
    foreach arg $famboco_info(args,$type) {
	set def ""; set pos ""
	foreach {n v} [lrange $arg 1 end] {
	    if {$n == "default"} { set def $v }
	    if {$n == "pos"} { set pos $v }
	}
	if {$pos == ""} { set pos $defp; incr defp }
	if {$pos > $maxpos} { set maxpos $pos }
	set argpos($pos) $def
    }
    for {set i 1} {$i <= $maxpos} {incr i} {
	if [info exists argpos($i)] {
	    lappend bc $argpos($i)
	} else {
	    lappend bc -123456  ;# safer than ""
	}
    }
    
    famboco_edit_onebc 1 $bc
}

proc famboco_edit_newfam {d} {
    global famboco_data
    
    set f [question_box .query "Query" "New family name?" "" -0-0]
    if {$f != ""} {
	set famboco_data($f) ""
	famboco_edit_populate $d
    }
}

proc famboco_edit_populate {d} {
    global famboco_data fam_dimension famboco_edit
    
    if [info exists fam_dimension] {
    } else {
	set fams ""
	foreach f [lsort [array names famboco_data]] {
	    if {$f == "__param__"} continue
	    if {$famboco_edit(only_un) && ($famboco_data($f) != "")} continue
	    lappend fams $f
	}
	form_listbox_set $d.faml $fams
    }
}

# ------------------------------------------------------------

proc famboco_edit_params {{sname ""}} {
    global param_info param_edit param_values solution solver_info
    global project_dir
    
    if {$sname == ""} {
	set sname [lindex $solver_info($solution(solver)) 1]
    }
    set file $project_dir/mesh/$sname.par
    
    if [file exists $file] {
	famboco_edit_load_param_values $file
    }
    
    if {![info exists param_info]} {
	mess "There is no parameter data available\n"
	return 0
    }
    
    if {[package versions BWidget] == ""} {
	error "Can't edit parameters - BWidgets not loaded"
    }
    
    if [info exists param_values] {
	set saved_values [array get param_values]
    } else {
	set saved_values ""
    }
    
    set form .param_edit
    form_kill .param_edit
    set d [form_init $form "Solver parameters" "" {accept cancel}]
    form_frame $d.t ridge 2 {0 0 -sticky nwes}
    form_frame $d.d ridge 2 {0 1 -sticky nwes}
    $d.t configure -width 200
    $d.d configure -width 400
    
    set tree $d.t.tree
    Tree $tree -relief flat -borderwidth 0 -height 25 -width 25 \
	 -yscrollcommand "$d.t.scroll set"
    $tree bindText <1> famboco_edit_params_select
    scrollbar $d.t.scroll -command "$tree yview" -background #cdb79e
    grid $tree -row 0 -column 0 -sticky nwes
    grid $d.t.scroll -row 0 -column 1 -sticky nwes
    
    catch {unset param_edit}
    set param_edit(all_nodes) ""
    set param_edit(cur_node) ""
    set param_edit(tree) $tree
    set param_edit(frame) $d.d
    famboco_edit_params_build
    
    futil_setup_switch $d.d param_edit(cur_node) "" "" 0
    
    set first [lindex $param_info(children,) 0]
    famboco_edit_params_select :$first
    
    form_finish bot

    if [form_wait .param_edit] {
	set curnode [$tree selection get]
	if {$curnode != ""} {
	    famboco_edit_params_after_edit $curnode
	}
	
	foreach v [array names param_edit] {
	    if [string match val,* $v] {
		set pname [string range $v 4 end]
		set pval $param_edit(val,$pname)
		if {[info exists param_edit(opts,$pname)] &&
		    ($param_edit(opts,$pname) != "")} {
		    #puts "checking for $pval in $param_edit(opts,$pname)"
		    set ok 0
		    foreach ch $param_edit(opts,$pname) {
			if {[lindex $ch 0] == $pval} {
			    if {[lindex $ch 1] != ""} {
				set pval [lindex $ch 1]
			    }
			    set ok 1
			    break
			}
		    }
		    if {!$ok && !$param_edit(allow_others,$pname)} {
			mess "Couldn't find $pname value $pval in list\n" red
		    }
		}
		set param_values($pname) $pval
	    }
	}
	famboco_edit_save_param_values $file
	return 1
    } else {
	array set param_values $saved_values
	return 0
    }
}

# Actually build the tree

proc famboco_edit_params_build {} {
    global param_edit param_info param_values
    
    set param_edit(info_node,) ""
    famboco_edit_params_fill_tree "" 1
    #parray param_edit    
    #parray param_info
    #parray param_values
}

# Fill in the tree below this node.

proc famboco_edit_params_fill_tree {node do_mult} {
    global param_info param_edit param_values
    
    set tree $param_edit(tree)
    set frame $param_edit(frame)
    
    set pnode $param_edit(info_node,$node)

    #puts "famboco_edit_params_fill_tree $node $do_mult"
    #puts "\tpnode $pnode"
    
    # We can do one of two things.  If this is a multiple node then 
    # create all the instances of it as children.  If it's not then
    # just create its children as usual.
    if {$do_mult && ($pnode != "") && $param_info(multiple,$pnode)} {
	#puts "\tmultiple instances"
	set param_edit(is_multiple,$node) 1
	
	foreach a $param_info(args,$pnode) {
	    foreach {p v} [lrange $a 1 end] {
		if {$p == "name"} { set aname $v }
		if {($p == "nodename") && $v} {
		    set param_edit(name_arg,$pnode) $aname
		    break
		}
	    }
	}
	foreach pp [array names param_values] {
	    if [string match $node*/* $pp] {
		set ll [expr [string length $node] + 1]
		set ch [lindex [split [string range $pp $ll end] ,] 0]
		if ![string match *:* $ch] {
		    set inst($ch) 1
		}
	    }
	}
	foreach ch [lsort [array names inst]] {
	    set chnode $node/$ch
	    if ![$tree exists $chnode] {
		$tree insert end $node $chnode -text $ch -open 1
		lappend param_edit(all_nodes) $chnode
	    }
	    catch {destroy $frame.$chnode}
	    form_choiceframe $frame.$chnode
	    set param_edit(info_node,$chnode) $pnode
	    
	    # Now call this function a second time but just make the normal
	    # children of this instance.
	    famboco_edit_params_fill_tree $chnode 0
	}
	
	if !$param_info(no_create_or_delete,$pnode) {
	    set cnew ${node}/create_new_node
	    if ![$tree exists $cnew] {
		$tree insert end $node $cnew -text "Create new"
		set param_edit(info_node,$cnew) $pnode
		set param_edit(create_new_node,$cnew) 1
	    }
	}
	
    } elseif [info exists param_info(children,$pnode)] {
	#puts "\tregular children"
	foreach ch $param_info(children,$pnode) {
	    set chnode $node:$ch
	    if ![$tree exists $chnode] {
		if [info exists param_info(title,$pnode:$ch)] {
		    set title $param_info(title,$pnode:$ch)
		} else {
		    set title ""
		}
		set par $node; if {$par == ""} { set par root }
		
		$tree insert end $par $chnode -text $title -open 1
		catch {destroy $frame.$chnode}
		form_choiceframe $frame.$chnode
		lappend param_edit(all_nodes) $chnode
		set param_edit(info_node,$chnode) $pnode:$ch
	    }
	    famboco_edit_params_fill_tree $chnode 1
	}
    }
}

proc famboco_edit_params_create_new {crnode nameval vals} {
    global param_info param_edit param_values
    
    #parray param_edit
    
    set tree $param_edit(tree)
    set frame $param_edit(frame)
    set pnode $param_edit(info_node,$crnode)
    
    #puts $param_edit(all_nodes)
    
    if [info exists param_edit(create_new_node,$crnode)] {
	set parent [$tree parent $crnode]
    } else {
	set parent $crnode
    }
    
    set rebuild 1
    if {$nameval == ""} {
	set nval [lindex [split $parent :] end]
	for {set i 0} {[$tree exists $parent/$nval$i]} {incr i} {}
	set nameval $nval$i
    }
    set node $parent/$nameval
    if [$tree exists $node] { set rebuild 0 }
    
    if $rebuild {
	set pos [$tree index $crnode]
	$tree insert $pos $parent $node -text $nameval -open 1
	form_choiceframe $frame.$node
	lappend param_edit(all_nodes) $node
	set param_edit(info_node,$node) $pnode
    }
    
    foreach {t v} $vals {
	set param_values($node,$t) $v
    }
    if [info exists param_edit(name_arg,$pnode)] {
	set param_values($node,$param_edit(name_arg,$pnode)) $nameval
    }
    
    famboco_edit_params_fill_tree $node 0
    catch {unset param_edit(val,$node,DELETED)} ;# could be there from previous
    return $node
}

proc famboco_edit_params_select {node} {
    global param_info param_edit param_values
    
    set tree $param_edit(tree)
    set frame $param_edit(frame)
    set pnode $param_edit(info_node,$node)
    
    set nameval ""
    if [info exists param_edit(create_new_node,$node)] {
	set node [famboco_edit_params_create_new $node "" ""]
    }
    
    set curnode [$tree selection get]
    if {$curnode != ""} {
	famboco_edit_params_after_edit $curnode
	$tree selection clear
    }
    $tree selection set $node
    set param_edit(cur_node) $node
    
    if {$param_info(pre_edit,$pnode) != ""} {
	eval $param_info(pre_edit,$pnode) $node
    }
    
    set win $frame.$node
    
    if {[winfo children $win] != ""} return
    if [info exists param_edit(is_multiple,$node)] return
    
    set args $param_info(args,$pnode)
    set bcargs ""
    foreach a $args {
	set name ""; set type ""; set title ""; set default ""
	foreach {n p} [lrange $a 1 end] { set $n $p }
	if [info exists param_values($node,$name)] {
	    lappend bcargs $param_values($node,$name)
	} elseif {($name == "name") && ($nameval != "")} {
	    lappend bcargs $nameval
	} else {
	    lappend bcargs $default
	}
    }
    if {$param_info(multiple,$pnode) && 
	!$param_info(no_create_or_delete,$pnode)} {
	lappend args [list arg type button title Delete command \
                          "famboco_edit_params_delete x $node"]
    }
    set html $param_info(html,$pnode)
    set bubble $param_info(bubble,$pnode)
    if {$nameval != ""} {
	set title $nameval
    } else {
	set title $param_info(title,$pnode)
    }
    famboco_edit_fillin_args $win param_edit $node \
	$title $args $bcargs $html $bubble 1 \
	"famboco_edit_params_after_edit $node"
}

proc famboco_edit_params_delete {dd node} {
    global param_edit param_values
    
    # We reverse them so that the children get deleted before the parents -
    # in the reverse order they were added to the list.
    if [ info exists param_edit(all_nodes) ] {
        foreach descendant [lreverse $param_edit(all_nodes)] {
	    if [string match $node:* $descendant] {
	        famboco_edit_params_delete $dd $descendant
	    }
        }
    }
    
    if [ info exists param_edit(tree) ] {
        set tree $param_edit(tree)
        set frame $param_edit(frame)
    
        set par [$tree parent $node]
        set n [lsearch [$tree nodes $par] $node]
        $tree delete $node
        set param_edit(cur_node) ""
        set nch [llength [$tree nodes $par]]
        if {$nch > 1} {
	    if {$n == $nch - 1} { incr n -1 }
	    famboco_edit_params_select [lindex [$tree nodes $par] $n]
        }
    }
    set param_edit(val,$node,DELETED) 1
    catch {destroy $frame.$node}
    
    foreach pp [array names param_values] {
	if [string match $node* $pp] {
	    unset param_values($pp)
	}
    }
}

# At this point don't change the nodenames or anything, just the text in
# 2 places.

proc famboco_edit_params_after_edit {node} {
    global param_edit param_info
    
    set tree $param_edit(tree)
    set frame $param_edit(frame)
    
    #puts "famboco_edit_params_after_edit $tree $node"
    if [info exists param_edit(val,$node,name)] {
	famboco_edit_params_rename $node $param_edit(val,$node,name)
    }
    set pnode $param_edit(info_node,$node)
    if {$param_info(post_edit,$pnode) != ""} {
	eval $param_info(post_edit,$pnode) $node
    }
}

proc famboco_edit_params_rename {node nname} {
    global param_edit
    
    set tree $param_edit(tree)
    
    set otitle $param_edit(node_title,$node)
    set param_edit(node_title,$node) \
	"[lindex [split $otitle :] 0]: $nname"
    $tree itemconfigure $node -text $nname
    set param_edit(val,$node,name) $nname
}

proc famboco_edit_load_param_values {file} {
    global param_values param_info
    
    mess "Loading solver parameters from $file.\n"
    catch {unset param_values}
    set fp [open $file r]
    set path [list ""]
    while {![eof $fp]} {
	set line [string trim [gets $fp]]
	if [string match \#* $line] continue
	if {[lindex $line 0] == "begin"} {
	    set nname [lindex $line 1]
	    set ninst [lindex $line 2]
	    if {$ninst != ""} {
		lappend path $nname/$ninst
	    } else {
		lappend path $nname
	    }
	    set ipath [join $path :]
	    if {[info exists param_info(multiple,$ipath)] && \
		    $param_info(multiple,$ipath) && ([lindex $line 2] != "")} {
		append path /[lindex $line 2]
	    }
	} elseif {[lindex $line 0] == "end"} {
	    set last [lindex [split [lindex $path end] /] 0]
	    set path [lrange $path 0 [expr [llength $path] - 2]]
	    if {$last != [lindex $line 1]} {
		error "$line doesn't match $last"
	    }
	} elseif {([llength $path] > 1) && ([llength $line] == 2)} {
	    set param_values([join $path :],[lindex $line 0]) [lindex $line 1]
	}
    }
    close $fp
}

proc famboco_edit_param_name {curnode} {
    global param_values
    
    set last [lindex $curnode end]
    if ![string match */* $last] { return $last }
    set curnode [join $curnode :]
    if [info exists param_values($curnode,name)] {
	return "[lindex [split $last /] 0] $param_values($curnode,name)"
    } else {
	return [split $last /]
    }
}

proc famboco_edit_list_is_prefix {shorter longer} {
    set ns [llength $shorter]; set nl [llength $longer]
    for {set i 0} {$i < $ns} {incr i} {
	if {[lindex $shorter $i] != [lindex $longer $i]} {
	    return 0
	}
    }
    return 1
}

proc famboco_edit_save_param_values {file} {
    global param_values param_info
    
    parray param_values
    
    mess "Saving solver parameters to $file.\n"
    set fp [open $file w]
    puts $fp "\# solver params file written on [cmd_date] by\
		[cmd_whoami]@[cmd_uname_n]\n"
    set curnode ""
    foreach v [lsort [array names param_values]] {
	set thisnode1 [lindex [split $v ,] 0]
	set thisnode [split $thisnode1 :]
	set thisval [lindex [split $v ,] 1]
	set is_deleted [info exists param_values($thisnode1,DELETED)]
	
	if {$thisnode != $curnode} {
	    #puts "go from $thisnode to $curnode"
	    set len [llength $curnode]
	    set neednl 0
	    # While the current one is not a prefix of the new one, close
	    # levels in the current one until we get to a prefix.
	    while {![famboco_edit_list_is_prefix $curnode $thisnode]} {
		if ![info exists param_values([join $curnode :],DELETED)] {
		    set tag [lindex $curnode end]
		    for {set i $len} {$i > 2} {incr i -1} {
			puts -nonewline $fp "    "
		    }
		    if {$tag != ""} {
			puts $fp "end [famboco_edit_param_name $curnode]"
		    }
		}
		set curnode [lrange $curnode 0 [expr $len - 2]]
		incr len -1
		incr neednl
	    }
	    foreach tag [lrange $thisnode [llength $curnode] end] {
		if !$is_deleted {
		    if $neednl { puts $fp ""; set neednl 0 }
		    for {set i $len} {$i > 1} {incr i -1} {
			puts -nonewline $fp "    "
		    }
		}
		lappend curnode $tag
		if !$is_deleted {
		    if {$tag != ""} {
			puts $fp "begin [famboco_edit_param_name $curnode]"
		    }
		}
		incr len
	    }
	}
	if !$is_deleted {
	    for {set i [llength $thisnode]} {$i > 1} {incr i -1} {
		puts -nonewline $fp "    "
	    }
	    puts $fp [list $thisval $param_values($v)]
	}
    }
    if ![info exists param_values([join $curnode :],DELETED)] {
	while {$curnode != {{}}} {
	    set len [llength $curnode]
	    for {set i $len} {$i > 2} {incr i -1} { puts -nonewline $fp "    " }
	    puts $fp "end [famboco_edit_param_name $curnode]"
	    set curnode [lrange $curnode 0 [expr [llength $curnode] - 2]]
	}
    }
    close $fp
}

# ----------------------------------------------------------------------------

## Add a new child to a given parent node.  The parent must be a multiple
## node, i.e. a container for multiple children.  The node will be given
## the initial <b>name</b> and the values specified in the list which must
## be a set of name/value pairs.

proc ic_param_add_node {parent name values} {
    global param_edit
    if [info exists param_edit(create_new_node,$parent/create_new_node)] {
        return [famboco_edit_params_create_new $parent/create_new_node \
		$name $values]
    } else {
        return [famboco_edit_params_create_new $parent $name $values]    
    }
}

## Delete the given node.

proc ic_param_delete_node {node} {
    famboco_edit_params_delete "" $node
}

## List the children of the given node.

# proc ic_param_list_children {node} {
#     global param_edit
#     
#     set tree $param_edit(tree)
#     return [$tree nodes $node]
# }
proc ic_param_list_children {node {tail 0}} {
    global param_edit param_values
    
    if {[info exists param_edit(tree)] && [winfo exists $param_edit(tree)]} {
	if [$param_edit(tree) exists $node] {
	    return [$param_edit(tree) nodes $node]
	} else {
	    return ""
	}
    } elseif $tail {
	foreach ch [ic_param_node_list $node/*] {
	    set rest [string range $ch [string length $node/] end]
	    set found([lindex [split $rest :] 0]) 1
	}
    } else {
	foreach ch [ic_param_node_list $node/*] {
	    set rest [string range $ch [string length $node/] end]
	    set found($node/[lindex [split $rest :] 0]) 1
	}
	foreach ch [ic_param_node_list $node:*] {
	    set rest [string range $ch [string length $node:] end]
	    set found($node:[lindex [split $rest :] 0]) 1
	}
    }
    return [array names found]
}

## List the parameter values for the given node.  This returns a list of
## name/value pairs.

proc ic_param_get_values {node} {
    global param_edit param_values
    
    set ret ""
    foreach pp [array names param_edit] {
	if [string match val,$node,* $pp] {
	    set t [lindex [split $pp ,] 2]
	    set v $param_edit($pp)
	    
	    if [info exists param_edit(opts,$node,$t)] {
		foreach opt $param_edit(opts,$node,$t) {
		    if {[lindex $opt 0] == $v} {
			set v [lindex $opt 1]
			break
		    }
		}
	    }
	    lappend ret $t $v
	}
    }
    # This is a bit annoying.  If this is a node we haven't edited before
    # then its values will be in the param_values array and not param_edit.
    if {$ret == ""} {
	foreach pp [array names param_values] {
	    if [string match $node,* $pp] {
		set t [lindex [split $pp ,] 1]
		set v $param_values($pp)
		lappend ret $t $v
	    }
	}
    }
    return $ret
}

## Change the parameter values on the given node.  The values list must 
## contain name/value pairs.

proc ic_param_set_values {node values} {
    global param_edit param_values
    
    foreach {t v} $values {
	set param_values($node,$t) $v
	
	if [info exists param_edit(val,$node,$t)] {
	    if [info exists param_edit(opts,$node,$t)] {
		foreach opt $param_edit(opts,$node,$t) {
		    if {[lindex $opt 1] == $v} {
			set v [lindex $opt 0]
			break
		    }
		}
	    }
	    set param_edit(val,$node,$t) $v
	}
    }
}
