
# This code is shared by all the ICEM CFD GUI's.
#
# Copyright (c) 1996 Pacific Marketing and Consulting, Inc,
#       and ICEM Systems, GmbH.  All rights reserved.
# Author:  Wayne A. Christopher, wayne@icemcfd.com
#

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

proc famboco_load_bcinfo {file {quiet 0}} {
    global famboco_info param_info famboco_tmp
    
    if !$quiet { mess "Loading $file\n" }
    
    catch {unset famboco_info}
    catch {unset param_info}
    
    set famboco_info(predefs) ""
    
    if {$file == ""} {
	    array set famboco_info {
	        all_bcs 0
	        ignore_dims 0
	        bcinfo_file ""
	        has_icons 0
	        htmldir ""
	        name Generic
	        predefs ""
	        ptypes ""
	        tclfile ""
	        translator ""
	        generic 1
	        categories ""
	    }
	    return
    }
    
    set famboco_tmp(category_name) ""
    set famboco_tmp(partbc) 0
    set famboco_tmp(bname) ""
    set famboco_info(bcinfo_file) $file
    set famboco_info(has_icons) 0
    set famboco_info(generic) 0
    set fp [open $file r]
    while {![eof $fp]} {
	    set line [string trim [gets $fp]]
	    famboco_load_line $file $line $fp
    }
    close $fp
    
    # Ensure that all bctypes have pos and name arguments and that the name arguments
    # are unique (there can be multiple args with the same pos)
    foreach b $famboco_info(bctypes) {
	catch {unset name_used}
	set nargs ""
	set defp 1
	foreach arg $famboco_info(args,$b) {
	    set pos ""; set name ""; set bc_name ""
	    foreach {n v} [lrange $arg 1 end] {
		if {$n == "pos"} { set pos $v }
		if {$n == "name"} { set name $v }
		if {$n == "bc_name"} { set bc_name $v }
	    }
	    if {$pos == ""} { set pos $defp; incr defp; lappend arg pos $pos }
	    if {$name == ""} {
		if {$bc_name == ""} {
		    set name arg$pos
		} else {
		    set name $bc_name
		}
		if [info exists name_used($name)] {
		    set n 1
		    while {[info exists name_used(${name}_$n)]} { incr n }
		    set name ${name}_$n
		}
		lappend arg name $name
		
	    } else {
		if [info exists name_used($name)] {
		    error "name $name used twice for $b!"
		}
	    }
	    set name_used($name) $pos
	    
	    lappend nargs $arg
	}
	set famboco_info(args,$b) $nargs
    }
	
    # Just in case there are random other things in there
    foreach a {
	args pre_edit post_edit bcicon_func category title dims
	html bubble
    } {
	set famboco_info($a,__generic__) ""
    }
    foreach a {
	pos default_active
    } {
	set famboco_info($a,__generic__) 0
    }
    
    foreach predef [glob -nocomplain [file rootname $file].*.par] {
	set fp [open $predef r]
	set title [file tail $predef]
	while {![eof $fp]} {
	    set line [gets $fp]
	    if [string match "problem_type_defaults *" $line] {
		set title [lindex $line 1]
		break
	    }
	}
	close $fp
	lappend famboco_info(predefs) [list $predef $title]
    }
}

proc famboco_load_line {filename line fp} {
    global famboco_info param_info famboco_tmp
    
    if {($line == "") || [string match \#* $line]} return
    
    while {[string match *\\\\ $line] && ![eof $fp]} {
	set line [string trimright $line \\]
	append line " [string trim [gets $fp]]"
    }
    if [catch {lindex $line 0} line0] {
	puts $line0
	puts $line
	exit 1
    }
    switch -glob -- $line0 {
	format {
	    set famboco_info(htmldir) ""
	    set famboco_info(solvers) ""
	    set famboco_info(all_bcs) 0
	    set famboco_info(ignore_dims) 0
	    foreach {n v} [lrange $line 1 end] {
		set famboco_info($n) $v
	    }
	    set famboco_info(bctypes) ""
	    set famboco_info(ptypes) ""
	    if [info exists famboco_info(default_bcicon_func)] {
		set famboco_info(has_icons) 1
	    }
	    set famboco_tmp(fname) $famboco_info(name)
	}
	category {
	    if ![info exists famboco_tmp(fname)] { error "no format line found yet" }
	    set name ""
	    set famboco_tmp(partbc) 0
	    foreach {n v} [lrange $line 1 end] { set $n $v }
	    set famboco_tmp(category_name) $name
	    set famboco_info(cat_args,$name) [lrange $line 1 end]
	    lappend famboco_info(categories) $name
	}
	define_choices {
	    set famboco_info(choice_def,[lindex $line 1]) [lindex $line 2]
	}
	define_active {
	    set famboco_info(active_def,[lindex $line 1]) [lindex $line 2]
	}
	bctype {
	    if ![info exists famboco_tmp(fname)] { error "no format line found yet" }
	    
	    set dims ""; set bcicon_func ""; set name ""
	    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"
	    }
	    if !$famboco_info(ignore_dims) {
		set famboco_info(dims,$name) $dims
	    }
	    foreach {n v} [lrange $line 1 end] {
		switch $n {
		    name - dims {}
		    default {
			set famboco_info($n,$name) $v
		    }
		}
	    }
	    set famboco_info(category,$name) $famboco_tmp(category_name)
	    set famboco_info(partbc,$name) $famboco_tmp(partbc)
	    if {$bcicon_func != ""} { set famboco_info(has_icons) 1 }
	    foreach a {args pre_edit post_edit} {
		set famboco_info($a,$name) ""
	    }
	    foreach {n v} {
		title "" html "" bubble "" bcicon_func ""
		pos 0 default_active 0 short_title ""
		tab_title "" tab_icons "" has_name 0
		apply_dims "" merge_bctypes "" bcicon_type ""
		icon_arg_extra "" text_icon ""
		symbol_type "" symbol_params "" symbol_label ""
	    } {
		if ![info exists famboco_info($n,$name)] {
		    set famboco_info($n,$name) $v
		}
	    }
	    lappend famboco_info(bctypes) $name
	    set famboco_tmp(bname) $name
	}
	arg {
	    if ![info exists famboco_tmp(fname)] { error "no format line found yet" }
	    if ![info exists famboco_tmp(bname)] { error "no bctype line found yet" }
	    lappend famboco_info(args,$famboco_tmp(bname)) $line
	}
	*_equivalent {
	    if ![info exists famboco_tmp(fname)] { error "no format line found yet" }
	    if ![info exists famboco_tmp(bname)] { error "no bctype line found yet" }
	    set famboco_info([lindex $line 0],$famboco_tmp(bname)) [lindex $line 1]
	}
	symbol_type - symbol_params - symbol_label - text_icon - pre_edit - post_edit {
	    if ![info exists famboco_tmp(fname)] { error "no format line found yet" }
	    if ![info exists famboco_tmp(bname)] { error "no bctype line found yet" }
	    set famboco_info([lindex $line 0],$famboco_tmp(bname)) [lrange $line 1 end]
	}
	label {
	    if ![info exists famboco_tmp(fname)] { error "no format line found yet" }
	    if ![info exists famboco_tmp(bname)] { error "no bctype line found yet" }
	    lappend famboco_info(args,$famboco_tmp(bname)) $line
	}
	stdarg {
	    if {[llength $line] == 2} {
		if ![info exists famboco_tmp(fname)] { error "no format line found yet"}
		if ![info exists famboco_tmp(bname)] { error "no bctype line found yet"}
		lappend famboco_info(args,$famboco_tmp(bname)) $famboco_info(stdargs,[lindex $line 1])
	    } else {
		set sname ""
		foreach {n v} [lrange $line 1 end] { if {$n == "name"} { set sname $v } }
		if {$sname == ""} { error "no name found in stdarg" }
		set famboco_info(stdargs,$sname) [concat arg [lrange $line 1 end]]
	    }
	}
	stdbctype {
	    if ![info exists famboco_tmp(fname)] { error "no format line found yet" }
	    error "stdbctype not yet supported"
	}
	param {
	    if ![info exists famboco_tmp(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) $famboco_tmp(category_name)
	    lappend famboco_info(ptypes) $name
	    set famboco_tmp(bname) $name
	}
	param_node {
	    famboco_parse_param_node "" $line $fp "" $filename 0 ;# don't use $famboco_tmp(category_name)
	}
	include {
	    # only support this for param nodes for now
	    # famboco_include_file $filename [lindex $line 1] [lindex $line 2]
	}
	represent_blank_float_args {
	    set famboco_info(represent_blank) [lindex $line 1]
	}
	icon_arg_extra - bcicon_type - ansys_equivalent_HIDE - ls_dyna3d_equivalent_HIDE - wpatran_equivalent_HIDE {}
	default {
	    puts "Warning: skipped $line"
	}
    }
}

proc famboco_include_file {filename sub only path ignore} {
    if {[file pathtype $sub] == "relative"} {
	set sub [file dirname $filename]/$sub
    }
    set nest 0
    set sfp [open $sub r]
    while {![eof $sfp]} {
	set sline [string trim [gets $sfp]]
	if {($sline == "") || [string match \#* $sline]} continue
	while {[string match *\\\\ $sline] && ![eof $sfp]} {
	    set sline [string trimright $sline \\]
	    append sline " [string trim [gets $sfp]]"
	}
	if {$only != ""} {
	    if [string match $only* $sline] {
		famboco_parse_param_node $path $sline $sfp "" $sub $ignore
	    }
	}
    }
    close $sfp
}

proc famboco_expand_dup_blocks {} {
    global famboco_info
    
    foreach bctype $famboco_info(bctypes) {
	# Loop until all the dup_blocks are flattened out.  We might have to
	# go through multiple times because they can be nested.
	while 1 {
	    set newargs ""
	    # First create new expanded args ...
	    set inblock 0
	    set blocksfound 0
	    set dupsize 0
	    set mindup 0
	    set maxdup 0
	    set dupargs {}
	    set dupname ""
	    catch {unset eq_dup}
	    foreach arg $famboco_info(args,$bctype) {
		catch {unset a}; array set a [lrange $arg 1 end]
		if $inblock {
		    lappend dupargs $arg
		    if {[llength $dupargs] == $dupsize} {
			# Now add in all the dups between min and max
			for {set num 0} {$num < $maxdup} {incr num} {
			    lappend newargs {arg type separator}
			    foreach darg $dupargs {
				catch {unset nactive}
				set nactive(active) ""
				set narg arg
				foreach {n v} [lrange $darg 1 end] {
				    switch -glob $n {
					title {
					    lappend narg $n "$v $num"
					}
					name {
					    lappend narg $n ${v}_$num
					    set eq_dup($v) $maxdup
					}
					*_subtitle {
					    lappend narg $n ${v}_$num
					}
					*active {
					    if {$num > $mindup} {
						set nactive($n) $v
					    } else {
						lappend narg $n $v
					    }
					}
					pos {
					    # deal with this at the bottom
					}
					default {
					    lappend narg $n $v
					}
				    }
				}
				foreach {n v} [array get nactive] {
				    set a1 [list $dupname]
				    for {set i [expr $num + 1]} {$i <= $maxdup} {incr i} {
					lappend a1 $i
				    }
				    lappend v $a1
				    lappend narg $n $v
				}
				lappend newargs $narg
			    }
			}
			# Reset variables
			set dupargs {}
			set inblock 0
			incr blocksfound
		    }
		} elseif [info exists a(dup_block)] {
		    set inblock 1
		    set dupname $a(name)
		    set dupsize $a(dup_block)
		    set mindup $a(dup_min)
		    set maxdup $a(dup_max)
		    unset a(dup_block)
		    unset a(dup_min)
		    unset a(dup_max)
		    set a(choices) ""
		    for {set i $mindup} {$i <= $maxdup} {incr i} {
			lappend a(choices) [list $i $i]
		    }
		    lappend newargs [concat arg [array get a]]
		} else {
		    lappend newargs $arg
		}
	    }
	    if {$dupargs != ""} {
		error "bad dup_blocks for $bctype"
	    }
	    if $blocksfound {
		#puts "BEFORE $bctype"
		#foreach arg $famboco_info(args,$bctype) { puts $arg }
		
		set newargs [famboco_reset_positions $newargs]
		
		#puts "AFTER"
		#foreach arg $newargs { puts $arg }
		
		set famboco_info(args,$bctype) $newargs
		
		# Now deal with the equivalent lines
		foreach eq [array names famboco_info *_equivalent,$bctype] {
		    set nval ""
		    foreach {t v} $famboco_info($eq) {
			if {[lindex $v 0] == "?"} {
			    assume this will not happen for now
			} else {
			    set nv [lindex $v 0]
			    foreach {s1 s2} [lrange $v 1 end] {
				if [info exists eq_dup($s2)] {
				    for {set i 0} {$i < $eq_dup($s2)} {incr i} {
					lappend nv ${s1}_$i ${s2}_$i
				    }
				} else {
				    lappend nv $s1 $s2
				}
			    }
			}
			lappend nval $t $nv
		    }
		    puts "$famboco_info($eq) -->\n$nval"
		    set famboco_info($eq) $nval
		}
	    } else {
		break
	    }
	}
    }
}

proc famboco_reset_positions {arglist} {
    set p 1
    set nargs ""
    foreach arg $arglist {
	catch {unset a}; array set a [lrange $arg 1 end]
	#puts $arg
	if {$a(type) != "separator"} {
	    set a(pos) $p
	    incr p
	    set arg [concat arg [array get a]]
	}
	lappend nargs $arg
    }
    return $nargs
}

# Return a list with triples - bctype defargs argnames

proc famboco_get_default_args_and_names {file expand_dup_blocks} {
    global famboco_info param_info env
    
    set old_famboco_info [array get famboco_info]
    set old_param_info [array get param_info]
    
    famboco_load_bcinfo $file
    if $expand_dup_blocks { famboco_expand_dup_blocks }
    
    set ret ""
    foreach bctype $famboco_info(bctypes) {
	set defs ""
	set defp 1
	set use_defp -1
	foreach a $famboco_info(args,$bctype) {
	    if {[lindex $a 0] == "label"} continue
	    if {$bctype == "PCOMP"} { puts $a }
	    catch {unset av}; array set av [lrange $a 1 end]
	    if {$av(type) == "separator"} continue
	    
	    if ![info exists av(pos)] {
		if {$use_defp == 0} {
		    error "mixed def and not def in $a"
		}
		set use_defp 1
		set av(pos) $defp; incr defp
	    } else {
		if {$use_defp == 1} {
		    error "mixed def and not def in $a"
		}
		set use_defp 0
	    }
	    if {$av(pos) == 0} { set av(pos) 1 }
	    if ![info exists av(name)] { set av(name) arg$av(pos) }
	    if ![info exists av(default)] { set av(default) 0 }
	    lappend defs [list $av(pos) $av(default) $av(name)]
	}
	set defs1 ""; set names1 ""
	foreach def [lsort -index 0 -integer $defs] {
	    lappend defs1 [lindex $def 1]
	    lappend names1 [lindex $def 2]
	}
	#puts [list lappend ret $bctype $defs1 $names1]
	lappend ret $bctype $defs1 $names1
    }
    
    catch {unset famboco_info}
    catch {unset param_info}
    array set famboco_info $old_famboco_info
    array set param_info $old_param_info
    
    return $ret
}

proc famboco_parse_param_node {parent line fp cat filename ignore} {
    global param_info
    
    set name ""; set title ""; set multiple 0; set active ""; set maxnum 0
    set html ""; set bubble ""; set no_create_or_delete 0; set active ""; set tab_icon ""
    set text_icon ""
    
    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" }
    if {$name == "paste_node"} { error "the name of a node can't be paste_node" }
    
    set path ${parent}:$name
    
    if [info exists param_info(title,$path)] {
	error "Param node $path repeated twice"
    }
    if !$ignore {
	lappend param_info(pnodes) $path
	lappend param_info(children,$parent) $name
	foreach p {title multiple maxnum active no_create_or_delete html bubble \
		       tab_icon text_icon} {
	    set param_info($p,$path) [set $p]
	}
	foreach a {args pre_edit post_edit constraints} {
	    set param_info($a,$path) ""
	}
	set param_info(category,$path) $cat
    }
    
    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 {
		if !$ignore {
		    lappend param_info(constraints,$path) $line
		}
	    }
	    arg - label {
		if !$ignore {
		    set arg2 [lindex $line 0]
		    # We have to specially fix up choice_pnodes here ...
		    foreach {n v} [lrange $line 1 end] {
			switch $n {
			    choice_pnode {
				# This is needed when one bcinfo file includes another
				# but it breaks standalone ls_dyna so let's leave it
				# out
				#lappend arg2 $n ${parent}$v
				lappend arg2 $n $v
			    }
			    default {
				lappend arg2 $n $v
			    }
			}
		    }
		    lappend param_info(args,$path) $arg2
		}
	    }
	    end {
		if {[lindex $line 1] != $name} { 
		    error "$line doesn't match $name"
		}
		return
	    }
	    param_node {
		famboco_parse_param_node $path $line $fp $cat $filename $ignore
	    }
	    pre_edit {
		if !$ignore {
		    set param_info(pre_edit,$path) [lindex $line 1]
		}
	    }
	    post_edit {
		if !$ignore {
		    set param_info(post_edit,$path) [lindex $line 1]
		}
	    }
	    include {
		famboco_include_file $filename [lindex $line 1] [lindex $line 2] \
		    $path $ignore
	    }
	}
    }
}

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
    
    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_edit(icon_scale)] {
	set famboco_edit(icon_scale) 1.0
	set famboco_edit(icon_maxnum) 0
	set famboco_edit(show_icons) 1
	set famboco_edit(all_icons) 0
    }
    
    if [info exists famboco_info(htmldir)] {
	#set famboco_info(htmldir) [glob $famboco_info(htmldir)]
	if {[file pathtype $famboco_info(htmldir)] == "relative"} {
	    set famboco_info(htmldir) $env(ICEM_ACN)/$famboco_info(htmldir)
	}
    } else {
	set famboco_info(htmldir) $env(ICEM_ACN)/help
    }
    return 1
}

proc famboco_edit {{sol Generic} {show_fam_func ""} {skip_unused 0}} {
    global famboco_info famboco_data fam_visible fam_dimension famboco_edit
    global env guibase_dir
    
    #puts [list famboco_edit $sol $show_fam_func $skip_unused]
    
    if {[array names fam_visible] == ""} {
	    user_error "There are no parts defined."
	    return 0
    }
    if ![famboco_get_info $sol] { return 0 }
    
    set famboco_edit(show_fam_func) $show_fam_func
    
    #puts [list famboco_edit $sol $show_fam_func $skip_unused]
    
    famboco_load_params ""
    
    set backup [array get famboco_data]
    
    foreach bcim {
	    active inactive volume surface surface_1 surface_2
	    edge node family unknown
    } {
	    image create bitmap bc_${bcim}_image -file $guibase_dir/bc_$bcim.xbm -foreground black -background ""
    }
    
    if !$skip_unused {
	    foreach f [array names fam_visible] {
	        if ![info exists famboco_data($f)] { set famboco_data($f) "" }
	    }
    }
    if {[info exists famboco_info(all_bcs)] && $famboco_info(all_bcs)} {
	    foreach f [array names famboco_data] {
	        set famboco_data($f) [famboco_flesh_out $f $famboco_data($f)]
	    }
    }
    
    set buts [list [list Accept {
	    if [famboco_edit_getfam 0] {
	        form_done .famboco
	        set form_done_flag(.famboco) 1
	    }
    }] cancel]
    
    form_kill .famboco
    form_kill .param_edit
    set d [form_init .famboco "Part boundary conditions" "" $buts]
    set famboco_info(form) .famboco
    set famboco_info(what) famboco
    
    set subs [form_paned $d.p [list "-weight 0" "-weight 1"] h {}]
    pack $d.p -fill both -expand 1
    
    set treefr [lindex $subs 0]
    set descfr [lindex $subs 1]
    set frame [form_scrollwin $descfr.f 400 500 {0 0 -sticky nwes}]
    
    set tree [form_tree $treefr.f {0 0}]
    $tree bindText <1> famboco_edit_select_node
    $tree bindImage <1> famboco_edit_toggle_active
    
    grid columnconfigure $treefr 0 -weight 1000
    grid rowconfigure $treefr 0 -weight 1000
    grid columnconfigure $descfr 0 -weight 1000
    grid rowconfigure $descfr 0 -weight 1000
    
    set famboco_edit(tree) $tree
    set famboco_edit(bcwin) $frame
    
    if {[info exists famboco_info(has_icons)] && $famboco_info(has_icons)} {
	    set bf $descfr.opts
	form_frame $bf flat 0 {1 0}
	#form_checkbutton $bf.oun "Only unassigned" famboco_edit(only_un) {0 0}
	#$bf.oun configure -command "famboco_edit_populate $d"
	form_checkbutton $bf.icon "B.C. icons" famboco_edit(show_icons) \
	    {0 1}
	$bf.icon configure -command "famboco_edit_update_icons"
	form_label $bf.isl " Scale" {0 2}
	form_entry $bf.is famboco_edit(icon_scale) float {0 3} "" 4
	form_label $bf.mnl " Max num" {0 4}
	form_entry $bf.mn famboco_edit(icon_maxnum) int {0 5} "" 4
	form_checkbutton $bf.af "All parts" famboco_edit(all_icons) {0 6} "" 4
	$bf.af configure -command "famboco_edit_update_icons"
	
	bind $bf.is <Return> "famboco_edit_update_icons"
	bind $bf.mn <Return> "famboco_edit_update_icons"
    }
    
    if [info exists famboco_edit(saved_size)] {
	set s $famboco_edit(saved_size)
	.famboco.main.p.f0 configure -width [lindex $s 0] -height [lindex $s 1]
	.famboco.main.p.f1 configure -width [lindex $s 1] -height [lindex $s 2]
    }
    form_finish bot 1 0
    
    famboco_edit_populate $d
    
    if $famboco_edit(all_icons) famboco_edit_update_icons
    
    set ok [form_wait .famboco]
    # this doesn't work
    #set famboco_edit(saved_size) [list [winfo width .famboco.main.p.f0] \
				      [winfo height .famboco.main.p.f0] \
				      [winfo width .famboco.main.p.f1] \
				      [winfo height .famboco.main.p.f1]]
    if !$ok {
	    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
    }
    form_kill .famboco
    #ic_boco_clear_icons
    return 1
}

proc famboco_edit_toggle_active {node} {
    global famboco_edit
    set data [$famboco_edit(tree) itemcget $node -data]
    if {[lindex $data 0] != "bc"} return
    set fam [lindex $data 1]
    set num [lindex $data 2]
    
    if {$famboco_edit(curfam) != $fam} {
	famboco_edit_setfam 0 $fam
    }
    
    if $famboco_edit(active,$fam,$num) {
	set famboco_edit(active,$fam,$num) 0
	$famboco_edit(tree) itemconfigure $node -image bc_inactive_image
    } else {
	set famboco_edit(active,$fam,$num) 1
	$famboco_edit(tree) itemconfigure $node -image bc_active_image
    }
}

proc famboco_flesh_out {fam bcs} {
    set dim [ic_boco_fam_dim $fam]
    famboco_get_bc_types bcat bt $dim
    
    foreach bc $bcs {
	set got([lindex $bc 1]) $bc
    }
    set nbcs ""
    foreach cc [lsort [array names bcat]] {
	foreach btitle $bcat($cc) {
	    set type $bt($btitle)
	    if [info exists got($type)] {
		lappend nbcs $got($type)
	    } else {
		lappend nbcs [famboco_make_default_bc $type -1 $fam 1]
	    }
	}	    
    }
    return $nbcs
}

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
}

# get rid of the for_attributes arg when nastran_attributes.bcinfo gets folded
# into nastran.bcinfo

proc famboco_set_solver {d {sol ""} {for_attributes 0}} {
    global famboco_edit env famboco_edit_format_tmp famboco_info solution

    # BAH
    if {$sol == "commonstruct"} { set sol "Common Structural" }
    
    if {[info exists famboco_edit(solver)] && ($famboco_edit(solver) != "") &&
	    ($famboco_edit(solver) == $sol) && [info exists famboco_info(predefs)]} {
	    return 1
    }
    if [info exists famboco_edit(solver)] { puts "want $sol, have $famboco_edit(solver)" }

    if ![info exists famboco_edit(solver_list)] {
        set tdir $env(ICEM_ACN)/icemcfd/output-interfaces
	    set files [concat [glob -nocomplain $tdir/*.bcinfo] \
		       [glob -nocomplain ./output-interfaces/*.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 0 $famboco_edit(curfam)
	    }
    } else {
	    if {$sol == "commonstruct"} { set sol "Common Structural" }
        if {$sol == "PRECISE"} { set sol "PRECISE (SI)" }
        if ![info exists file_for($sol)] {
	        mess "Warning: no solver BC information for $sol\n" red
	        set famboco_info(predefs) ""
	        set famboco_info(all_bcs) 0
	        set famboco_info(ignore_dims) 0
	        return 1
	    }
	    set famboco_edit(solver) $sol
	    set bcf $file_for($sol)
	    famboco_load_bcinfo $bcf
	    if {$d != ""} {
	        famboco_edit_setfam 0 $famboco_edit(curfam)
	    }
    }
    if {[info commands solution_set_solver] != ""} {
        solution_set_solver $famboco_edit(solver) 0
    }
    return 1
}

proc famboco_edit_select_node {node} {
    global famboco_edit famboco_data
    
    set data [$famboco_edit(tree) itemcget $node -data]
    switch [lindex $data 0] {
	fam {
	    set fam [lindex $data 1]
	    if {$famboco_edit(curfam) != $fam} {
		if ![famboco_edit_setfam 0 $fam] return
	    }
	}
	create_new {
	    set fam [lindex $data 1]
	    if {$famboco_edit(curfam) != $fam} {
		if ![famboco_edit_setfam 0 $fam] return
	    }
	    set num [famboco_edit_newbc "" [lindex $data 2]]
	    if {$num == ""} return
	    #famboco_edit_setfam 0 $fam
	    #famboco_edit_populate_family $fam
	    set d $famboco_edit(bcwin).bc$num
	    set node bc-$fam-$num
	    foreach sl [grid slaves $famboco_edit(bcwin)] { grid forget $sl }
	    form_dotab $d {0 0} news
	}
	paste {
	    set clip $famboco_edit(clipboard)
	    if {$clip == ""} { user_error "No b.c.'s in clipboard" }
	    set fam [lindex $data 1]
	    if {$famboco_edit(curfam) != $fam} {
		if ![famboco_edit_setfam 0 $fam] return
	    }
	    set num [famboco_edit_newbc $clip ""]
	    if {$num == ""} return
	    #famboco_edit_setfam 0 $fam
	    #famboco_edit_populate_family $fam
	    set d $famboco_edit(bcwin).bc$num
	    set node bc-$fam-$num
	    foreach sl [grid slaves $famboco_edit(bcwin)] { grid forget $sl }
	    form_dotab $d {0 0} news
	}
	bc {
	    set fam [lindex $data 1]
	    set num [lindex $data 2]
	    if {$famboco_edit(curfam) != $fam} {
		if ![famboco_edit_setfam 0 $fam] return
	    }
	    set d $famboco_edit(bcwin).bc$num
	    if ![winfo exists $d] {
		set bc [lindex $famboco_data($fam) [expr $num - 1]]
		famboco_edit_onebc 0 $bc $fam $num
	    }
	    foreach sl [grid slaves $famboco_edit(bcwin)] { grid forget $sl }
	    form_dotab $d {0 0} news
	    
	    if $famboco_edit(active,$fam,$num) {
		set im bc_active_image
	    } else {
		set im bc_inactive_image
	    }
	    $famboco_edit(tree) itemconfigure $node -image $im
	    
	    famboco_edit_update_icons $fam $num
	}
	default return
    }
    $famboco_edit(tree) selection set $node
}

proc famboco_edit_update_icons {{sel_fams ""} {num ""}} {
    global famboco_info famboco_edit famboco_data
    
    return ;# don't do this for now
    
    if ![info exists famboco_info(default_bcicon_func)] return
    set func $famboco_info(default_bcicon_func)
    if {[info command $func] == ""} return
    
    if $famboco_edit(all_icons) {
	set fams [array names famboco_data]
    } else {
	set fams [list $famboco_edit(curfam)]
    }
    if {$sel_fams == ""} {
	set sel_fams [list $famboco_edit(curfam)]
    }
    
    ic_boco_clear_icons
    if {($func != "") && $famboco_edit(show_icons)} {
	# Note that here we have to make a list with all the boundary conds
	# currently defined because the icons might depend on multiple ones.
	
	foreach fam $fams {
	    set params [famboco_edit_data_for_bcicon $fam]
	    
	    # The bcicon_func returns a list, each element of which is:
	    # bctype locations color args ...
	    # locations may be "" in which case we should figure it out
	    # bctype is something like arrow
	    # color can be a color spec or the word family_color
	    # args depends on the bctype
	    set what [$func $fam $params]
	    set type bc ;# I don't think we actually need this here
	    
	    foreach ic $what {
		ic_boco_add_icon $type $fam [lindex $ic 0] [lindex $ic 1] \
		    $famboco_edit(icon_scale) $famboco_edit(icon_maxnum) \
		    [lindex $ic 2] [lrange $ic 3 end] ""
	    }
	}
    }
}

proc famboco_edit_data_for_bcicon {fam} {
    global famboco_data famboco_param_data famboco_edit famboco_info
    global famboco_noedit
    
    set params ""
    if {$famboco_edit(curfam) == $fam} {
	# Here the most up to date data is in famboco_edit ...
	
	for {set num 1} {$num < $famboco_edit(curnum)} {incr num} {
	    if {![info exists famboco_edit(active,$fam,$num)] ||
		!$famboco_edit(active,$fam,$num)} continue
	    set thistype $famboco_edit(type,$num)
	    set param $thistype
	    foreach arg $famboco_edit(name_list,$num) {
		lappend param \
		    $famboco_edit(bctitle,$num,$arg) \
		    $famboco_edit(val,$num,$arg)
	    }
	    lappend params $param
	}
    } else {
	foreach bc $famboco_data($fam) {
	    if ![lindex $bc 0] continue
	    
	    foreach var {
		type swap_arg01 title args html bubble bcargs2 dims
	    } val [famboco_edit_onebc_extract_data $bc] {
		set $var $val
	    }
	    
	    set param $type
	    foreach argdata [famboco_edit_onebc_extract_args $args \
				 [lrange $bc 1 end]] {
		foreach avar {name bctitle val valtype} aval $argdata {
		    set $avar $aval
		}
		lappend param $bctitle $val
	    }
	    lappend params $param
	}
    }
    return $params
}

proc famboco_edit_setfam {params fam} {
    global famboco_data famboco_param_data famboco_edit famboco_info
    global famboco_noedit
    if ![famboco_edit_getfam $params] { return 0 }
    foreach w [winfo children $famboco_edit(bcwin)] { destroy $w }
    
    help_clear $famboco_edit(bcwin)
    bubble_clear $famboco_edit(bcwin)
    
    famboco_edit_update_icons "" ""
    
    set famboco_edit(curfam) $fam
    if {$fam == ""} { return 0 } 
    
    set famboco_edit(curnum) 1
    set famboco_edit(noedit) ""
    
    busy 1
    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)] "" -1
		} 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 {
		if !$famboco_info(all_bcs) {
		    famboco_edit_onebc 0 $bc $fam -1
		} else {
		    incr famboco_edit(curnum) 
		}
	    }
	}
    }
    busy 0
    
    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
    }
    
    famboco_edit_update_icons $fam ""
    return 1
}

proc famboco_bc_type_title_dims {bc} {
    global famboco_info
    
    foreach var {
	type swap_arg01 title args html bubble bcargs2 dims
    } val [famboco_edit_onebc_extract_data $bc] {
	set $var $val
    }
    
    return [list $type $title $dims]
}

proc famboco_edit_onebc {params bc fam num} {
    global famboco_info famboco_data fam_dimension famboco_edit
    
    if {$num < 0} {
	set num $famboco_edit(curnum)
	incr famboco_edit(curnum)
    }
    set d $famboco_edit(bcwin).bc$num
    
    form_frame $d flat 0 ""
    
    foreach var {
	type swap_arg01 title args html bubble bcargs2 dims
    } val [famboco_edit_onebc_extract_data $bc] {
	set $var $val
    }
    if {$fam != ""} {
	set active_var famboco_edit(active,$fam,$num)
    } else {
	set active_var ""
    }
    
    set famboco_edit(type,$num) $type
    set famboco_edit(swap_arg01,$num) $swap_arg01
    famboco_edit_fillin_args $d famboco_edit $num $title $args $bcargs2 $html \
	$bubble $params "" $active_var ""
    return [list $num $type]
}

# This is separated out because in some places we need to get at this data

proc famboco_edit_onebc_extract_data {bc} {
    global famboco_info

    set type [lindex $bc 1]
    set type2 [lindex $bc 2]  ;# some bc types are like this
    set swap_arg01 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(generic)] && !$famboco_info(generic) &&
	      [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
	
    } else {
	set title "$type"
	set dims mixed
	set args ""
	set html ""
	set bubble ""
	foreach {name atype def} {
	    "String 2" string "" "Integer" int 0 "Real 1" float 0.0 \
		"Real 2" float 0.0 "Real 3" float 0.0 "Real 4" float 0.0 \
		"Real 5" float 0.0 "Real 6" float 0.0 "Real 7" float 0.0 \
		"Real 8" float 0.0 "Real 9" float 0.0} {
	    lappend args [list arg type $atype title $name default $def]
	}
	#set type __generic__
    }
    
    if {!$famboco_info(generic) && [info exists famboco_info(pos,$type)] && ($famboco_info(pos,$type) == 1)} {
	set swap_arg01 1
    }
    set bcargs2 ""
    foreach b $bcargs {
	if {[info exists famboco_info(represent_blank)] &&
	    ($famboco_info(represent_blank) == $b)} { set b "" }
	lappend bcargs2 $b
    }
    
    set ret [list $type $swap_arg01 $title $args $html $bubble $bcargs2 $dims]
    #puts "RET $ret"
    return $ret
}

proc famboco_something_changed {} {
    global famboco_info
    if {$famboco_info(what) == "famboco"} {
	famboco_edit_update_icons
    }
}

proc famboco_edit_fillin_args {d varname num title args bcargs html bubble params nodename_binding active_var all_active} {
    global $varname famboco_info famboco_data fambco_edit
    
    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
    form_colwidth $d 1 4 M
    set ${varname}(count,$num) [llength $args]
    
    if {($active_var != "") && ![info exists $active_var]} {
	uplevel \#0 set $active_var 1
    }
    if {$html != ""} {
	help_define $d.name "" $famboco_info(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 }
	if [info exists argpos($a)] {
	    user_error "There is more than one arg line \"$a\" ... please change"
	}
	if 0 {
	if [info exists famboco_edit(swap_arg01,$num)] {
	    if {$pos == 0} {
		set pos 1
	    } elseif {$pos == 1} {
		set pos 0
	    }
	}
	}
	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 ${varname}(name_list,$num) ""
    set famboco_info(entries_to_check) ""
    
    #puts "BC $bcargs"
    #puts "ARGS $args"
    
    set entrynum 1
    set rownum 1
    foreach a $args {
	set v [lindex $bcargs [expr $argpos($a) - 1]]

	set type ""; set title ""; set default ""; set options ""
	set choices ""; set active $all_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 ""; set choice_pnode ""; set choice_func ""
	set default_func ""

	foreach {n p} [lrange $a 1 end] { set $n $p }
	set is_label [expr {[lindex $a 0] == "label"}]

	if {$name == ""} { set name $entrynum }

	switch -glob $choice_func {
	    "" { }
	    "families*" {
		set what [lrange $choice_func 1 end]
		if {$what == ""} {
		    set choices [lsort [ic_boco_list_families]]
		} else {
		    catch {unset found}
		    foreach {t dm} {point 0 curve 1 surface 2 material 3} {
			foreach obj [ic_geo_get_objects $t] {
			    set found([ic_geo_get_family $t $obj]) $dm
			}
		    }
		    foreach opt $what {
			foreach f [array names found] {
			    if {$found($f) == $opt} {
				set found_2($f) 1
			    }
			}
			# In case there is no geometry but there is mesh
			foreach f [ic_uns_list_families $opt] {
			    set found_2($f) 1
			}
		    }
		    set choices [lsort [array names found_2]]
		}
	    }
	    default { set choices [eval $choice_func] }
	}

	catch {unset ${varname}(val,$num,$name)} ;# kill the trace
	set ${varname}(argpos,$num,$name) $argpos($a)
	
	set choice_bool [expr {$choices == "choice_boolean"}]
	if [info exists famboco_info(choice_def,$choices)] {
	    set choices $famboco_info(choice_def,$choices)
	}
	if [info exists famboco_info(active_def,$active)] {
	    set active $famboco_info(active_def,$active)
	}
	
	set ${varname}(opts,$num,$name) $choices
	set ${varname}(allow_others,$num,$name) $allow_others
	set ${varname}(choice_pnode,$num,$name) $choice_pnode
	set ${varname}(valtype,$num,$name) $type
	set ${varname}(bctitle,$num,$name) $title
	set ${varname}(argname,$num,$entrynum) $name
	
	lappend ${varname}(name_list,$num) $name
	
	if $params {
	    set act ""
	} else {
	    set act $active_var
	}
	if {$active != ""} {
	    set key ""
	    foreach k [array names key_for_title] {
		lappend key $k ${varname}(val,$num,$key_for_title($k))
	    }
	    set act2 [form_active_replace_varnames $active $key]
	    
	    if {$act != ""} {
		set act [list and $act $act2]
	    } else {
		set act $act2
	    }
	}
	
	set ${varname}(activedata,$num,$name) $act
	#puts "ACT $act"
	
	set wid $d.val$name
	set put_help ""
	
	set pickf ""; set pickf1 famboco_something_changed
	if {$post_choice != ""} {
	    set pickf "$post_choice $num \"$name\" \"${varname}(val,$num,$name)\""
	    set pickf1 "$pickf \\\"\[set \"${varname}(val,$num,$name)\"\]\\\"; famboco_something_changed"
	}
	
	if $is_label {
	    if {($variable == "") || ($options == "")} {
		form_label $d.lab_anon$rownum $title "$rownum 1 -cols 3"
		$d.lab_anon$rownum configure -justify left -relief ridge \
		    -borderwidth 2
		lappend put_help $d.lab_anon$rownum
		set ${varname}(val,$num,$name) ""
	    } else {
		form_label_variable $d.lab$name ${varname}(val,$num,$name) \
		    "$rownum 1 -cols 3" $act
		lappend put_help $d.lab$name
		
		# 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 "$rownum 1 -cols 3" $act
	    lappend put_help $wid
	    set ${varname}(val,$num,$name) ""

	} elseif {$type == "button_box"} {
	    form_frame $wid flat 0 "$rownum 1 -cols 3"
	    set col 0
	    foreach t $title c $command {
		form_button $wid.but$col $t $c "0 $col" $act
		lappend put_help $wid.but$col
		incr col
	    }

	} elseif {$type == "fixed"} {
	    set ${varname}(val,$num,$name) $v

	} elseif {$type == "endlabel"} {
	    form_label $d.lab$name $title "[expr $rownum - 1] 4" $act
	    set ${varname}(val,$num,$name) $v
	    $d.lab$name configure -textvariable ${varname}(val,$num,$name) \
	    	-justify left -relief ridge -borderwidth 2

	} elseif {$type == "hidden"} {
	    set ${varname}(val,$num,$name) $v
	    continue

	} elseif {($choices == "") && ($choice_pnode == "")} {
	    form_label $d.lab$name $title "$rownum 1 -cols 2" $act
	    lappend put_help $d.lab$name
	    set ${varname}(val,$num,$name) $v

	    if {$gui_type == "filename" || $gui_type == "fileopen"} {
                if {$gui_type == "fileopen"} {
                    set newok 0
                } else {
                    set newok 1
                }
		form_frame $wid flat 0 "$rownum 3"
		file_select_entry $wid.e ${varname}(val,$num,$name) 0 $newok \
		    $title "0 0" $act "" "" "" "" "" $pickf
		lappend put_help $wid.e
		set wid $wid.e
		if {$width == ""} { set width 16 }
	    } else {
		form_entry $wid ${varname}(val,$num,$name) $type \
		    "$rownum 3" $act
		lappend famboco_info(entries_to_check) $wid
		lappend put_help $wid
		if {$type == "string"} { set width 20 }
	    }

	    if $nodename {
		bind $wid <Return> "$nodename_binding; famboco_something_changed"
	    } else {
		bind $wid <Return> famboco_something_changed
	    }
	    if {$width != ""} { $wid configure -width $width }

	} elseif {$choices == "0 1"} {
	    set ${varname}(val,$num,$name) $v
	    form_checkbutton $wid $title ${varname}(val,$num,$name) \
		"$rownum 1 -cols 3" $act
	    $wid configure -command $pickf1
	    lappend put_help $wid

	} else {
	    if {$gui_type == "radio"} {
		set at_end $choice_bool
		#set at_end [expr {!$allow_others && ([string length $title] < 8)}]
	    } else {
		set at_end 1
	    }
	    if $at_end { set cols 2 } else { set cols 3 }
	    form_label $d.lab$name $title "$rownum 1 -cols $cols" $act
	    lappend put_help $d.lab$name
	    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 || ($choice_pnode != "")} {
		    set ${varname}(val,$num,$name) $v
		} else {
		    puts "Warning: can't handle bc $title value \"$v\""
		    set ${varname}(val,$num,$name) [lindex $optnames 0]
		}
	    }
	    if {($gui_type == "radio") && !$allow_others} {
		if $at_end {
		    form_frame $wid flat 0 "$rownum 3"
		} else {
		    incr rownum
		    form_frame $wid flat 0 "$rownum 2 -cols 2"
		}

		# 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 {
		    set bub [lindex $o 2]
		    form_radiobutton $wid.ch$j [lindex $o 0] \
			${varname}(val,$num,$name) [lindex $o 0] "$r $c" $act
		    $wid.ch$j configure -command $pickf1 -padx 0 -pady 0
		    if {$bub == ""} {
			lappend put_help $wid.ch$j
		    } else {
			bubble_define $wid.ch$j $bub
		    }
		    incr j
		    if {$totl <= 16} { incr c } else { incr r }
		}

	    } else {
		if {$choice_pnode == ""} {
		    set how_get "list $optnames"
		} else {
		    set how_get "family_boco_pnode_children $choice_pnode $optnames"
		}
		form_frame $wid flat 0 "$rownum 3"
		set ent [futil_listentry $wid.e ${varname}(val,$num,$name) \
			     $how_get "0 0" $act $pickf1 $allow_others]
		lappend famboco_info(entries_to_check) $ent
		if {$width == ""} { set width 16 }
		if ![winfo exists $ent] { set ent $wid.e }
		$ent configure -width $width
		set wid $ent
		lappend put_help $ent
	    }
	}

	foreach hwid $put_help {
	    if {$html != ""} {
		help_define $hwid "" \
		    $famboco_info(htmldir)/[lindex $html 0] \
		    [lindex $html 1]
	    }
	    if {$bubble != ""} {
		bubble_define $hwid $bubble
	    }
	}
	incr rownum
	incr entrynum
    }
    foreach tr $traces_to_do { eval $tr }
    if !$params {
	if !$famboco_info(all_bcs) {
	    form_frame $d.buts flat 0 {100 1 -cols -}
	    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}
	}
    }
}

# This should be extended and used for the function above.

proc famboco_edit_onebc_extract_args {args bcargs} {
    # 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 entrynum 1
    foreach a $args {
	set val [lindex $bcargs $argpos($a)]
	set type ""; set title ""; set name ""
	set choices ""; set choice_func ""
	foreach {n p} [lrange $a 1 end] { set $n $p }
	if {$name == ""} { set name $entrynum }
	
	if {$choice_func != ""} {
	    set choices [eval $choice_func]
	}
	foreach o $choices {
	    set internal_val [lindex $o 1]
	    if {$internal_val == ""} { set internal_val [lindex $o 0] }
	    if {$internal_val == $val} {
		set val [lindex $o 0]
		break
	    }
	}
	
	lappend retval [list $name $title $val $type]
	incr entrynum
    }
    return $retval
}

proc family_boco_pnode_children {pnode args} {
    global param_values param_edit
    
    #puts [list family_boco_pnode_children $pnode $args]
    
    set ret $args
    foreach p [array names param_values] {
	if [string match $pnode/* $p] {
	    set lastpart [lindex [split $p /] 1]
	    set thisnode [lindex [split $lastpart ,] 0]
	    set pname [lindex [split $lastpart ,] 1]
	    if {$pname == "name"} {
		if [info exists param_edit(val,$pnode/$thisnode,name)] {
		    set got($thisnode) $param_edit(val,$pnode/$thisnode,name)
		} else {
		    set got($thisnode) $param_values($p)
		}
	    }
	}
    }
    foreach p [lsort [array names got]] {
	lappend ret $got($p)
    }
    return $ret
}

# This returns a nicely formatted list of the currently available stuff beneath
# this pnode.  It gives a list suitable for choicelist (i.e. name value name value ...
# where a "" value means nothing is selected)

proc family_boco_pnode_subtree_choicelist {pnode} {
}

proc family_boco_pnode_children_old {pnode args} {
    global param_edit
    
    set ret $args
    foreach n [$param_edit(tree) nodes $pnode] {
	set t [file tail $n]
	if [info exists param_edit(val,$n,name)] {
	    set t $param_edit(val,$n,name)
	}
	if {($t != "create_new_node") && ($t != "paste_node")} {
	    lappend ret $t
	}
    }
    return $ret
}

proc famboco_edit_getfam {params} {
    global famboco_data famboco_param_data famboco_edit famboco_info
    
    set fam $famboco_edit(curfam)
    if {$fam == ""} { return 1 }
    
    if ![famboco_check_all_entries] { return 0 }
    famboco_edit_update_icons $fam ""
    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 old_data $famboco_data($fam)
	    set famboco_data($fam) ""
	    for {set i 1} {$i < $famboco_edit(curnum)} {incr i} {
	        if [winfo exists $famboco_edit(bcwin).bc$i] {
		        set bc [famboco_edit_getnum $params $i]
	        } else {
		        if [info exists famboco_edit(active,$fam,$i)] {
		            set bc [lindex $old_data [expr $i - 1]]
		            set bc [concat $famboco_edit(active,$fam,$i) [lrange $bc 1 end]]
		        } else {
		            continue
		        }
	        }
	        if {$bc != ""} {
		        set bc1 ""
		        set j 1
		        foreach b $bc {
		            if {$b == -123456} { set b 0 }
		            lappend bc1 $b
		            if [info exists famboco_edit(argname,$i,$j)] {
			            set aname $famboco_edit(argname,$i,$j)
			            if {[info exists famboco_edit(valtype,$i,$aname)] && ($famboco_edit(valtype,$i,$aname) == "string") && ([scan $b %g dummy] == 1)} {
			                set b \"$b\"
			            }
		            }
		        }
		        lappend famboco_data($fam) $bc1
		        incr j
	        }
	    }
        foreach bc $famboco_edit(noedit) { lappend famboco_data($fam) $bc }
    }
#    famboco_edit_populate_family $fam
    return 1
}

proc famboco_check_all_entries {} {
    global famboco_info
    
    if ![info exists famboco_info(entries_to_check)] { return 1 }
    set okay 1
    foreach ent $famboco_info(entries_to_check) {
	if ![form_winmapped $ent] continue
	if {[lindex [$ent configure -state] 4] != "normal"} continue
	if ![form_entry_check $ent] { set okay 0 }
    }
    if $okay {
	return 1
    } else {
	form_warning $famboco_info(form) \
	    "Please correct the indicated fields." 2000
	return 0
    }
}

proc famboco_edit_getnum {params i} {
    global famboco_data famboco_edit famboco_info
    
    if $params {
	set bc [list $famboco_edit(type,$i)]
    } else {
	set fam $famboco_edit(curfam)
	if ![info exists famboco_edit(active,$fam,$i)] { return "" }
	set bc [list $famboco_edit(active,$fam,$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} {
	set aname $famboco_edit(argname,$i,$j)
	if $useopts {
	    set opts $famboco_edit(opts,$i,$aname)
	} else {
	    set opts ""
	}
	set val $famboco_edit(val,$i,$aname)
	if {$opts != ""} {
	    set nval ""
	    foreach o $opts {
		if {[lindex $o 0] == $val} {
		    set nval [lindex $o 1]
		    if {$nval == ""} { set nval $val }
		    break
		}
	    }
	    
	    if {$nval == ""} {
		# If we didn't find this value in the list of options then
		# maybe it came from a pnode.  Don't bother to check further
		# than that.
		if {($famboco_edit(choice_pnode,$i,$aname) == "") &&
		    !$famboco_edit(allow_others,$i,$aname)} {
		    mess "Warning: can't handle bc value $val\n" red
		    set nval [lindex [lindex $opts 0] 0]
		} else {
		    set nval $val
		}
	    }
	    set val $nval
	}
	if {[info exists famboco_info(represent_blank)] &&
	    ([string trim $val] == "") &&
	    (($famboco_edit(valtype,$i,$aname) == "float_blank") ||
	     ($famboco_edit(valtype,$i,$aname) == "int_blank"))} {
	    set val $famboco_info(represent_blank)
	} elseif {([scan $val %g dummy] == 1) && ($famboco_edit(valtype,$i,$aname) == "string")} {
	    set val \"$val\"
	} elseif {($famboco_edit(valtype,$i,$aname) == "string")} {
	    set val [string trim $val \"]
	}
	set pp $famboco_edit(argpos,$i,$aname)
	
	set is_act [form_checkstate_expr $famboco_edit(activedata,$i,$aname)]
	
	# 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 [lindex $bc 0] [list [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 aname $famboco_edit(argname,$i,$j)
	set opts $famboco_edit(opts,$i,$aname)
	set val $famboco_edit(val,$i,$aname)
	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,$famboco_edit(curfam),$num)
    $famboco_edit(tree) delete bc-$famboco_edit(curfam)-$num
    famboco_edit_update_icons "" ""
}

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
	mess "Values copied\n"
    }
}

proc famboco_edit_pastebc {} {
    global famboco_edit
    famboco_edit_newbc $famboco_edit(clipboard)
}

proc famboco_copy_param_node {pnode node} {
    global param_info param_edit
    
    set copy ""
    foreach arg $param_info(args,$pnode) {
	foreach {a v} [lrange $arg 1 end] {
	    if {$a == "name"} { set name $v }
	}
	if {$name == "name"} continue
	set val $param_edit(val,$node,$name)
	foreach opt $param_edit(opts,$node,$name) {
	    if {[lindex $opt 0] == $val} {
		set val [lindex $opt 1]
	    }
	}
	lappend copy $name $val
    }
    set param_edit(clipboard) $copy
    set param_edit(clipboard_full) 1
    mess "Values copied\n"
}

proc famboco_get_bc_types {variable tvariable dims} {
    global famboco_info
    
    upvar 1 $variable bcat
    upvar 1 $tvariable bt
    
    foreach t $famboco_info(bctypes) {
	set bcdims ""
	if [info exists famboco_info(dims,$t)] {
	    set bcdims $famboco_info(dims,$t)
	}
	#puts "check $t dims $dims bcdims $bcdims"
	set ok 0
	if {($dims == "") || ($bcdims == "") || ($bcdims == "-1") ||
	    ($bcdims == "mixed") || ($dims == "mixed")} {
	    set ok 1
	} else {
	    foreach d $dims {
		if $ok break
		foreach bd $bcdims {
		    if $ok break
		    if {(($d == "2") && [string match 2* $bd]) ||
			(($bd == "2") && [string match 2* $d])} {
			set ok 1
		    } elseif {$d == $bd} {
			set ok 1
		    }
		}
	    }
	}
	if {$ok != 1} continue
	#puts ok
	lappend bcat($famboco_info(category,$t)) $famboco_info(title,$t)
	set bt($famboco_info(title,$t)) $t
    }
    if {$dims != ""} {
	catch {unset "bcat(Meshing directives)"}
    }
}

proc famboco_edit_newbc {{vals ""} {dims ""}} {
    global famboco_edit famboco_info
    
    if {$vals != ""} {
	set bc $vals
	set type [lindex $vals 1]
    } elseif [info exists famboco_info(bctypes)] {
	famboco_get_bc_types bcat bt $dims
	
	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 ""]]
	if {$ch == ""} return
	if ![info exists bt($ch)] {
	    user_error "You didn't select a valid type."
	    return ""
	}
	set type $bt($ch)
	
	set bc [famboco_make_default_bc $type 1 $famboco_edit(curfam) 1]
	
    } 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]
    }
    
    set num [lindex [famboco_edit_onebc 0 $bc $famboco_edit(curfam) -1] 0]
    
    set title [lindex [famboco_bc_type_title_dims $bc] 1]
    set f $famboco_edit(curfam)
    
    if [info exists famboco_info(category,$type)] {
	set cat $famboco_info(category,$type)
    } else {
	set cat ""
    }
    if {$cat != ""} {
	set children [$famboco_edit(tree) nodes fam-$f]
	set parent ""
	foreach ch $children {
	    set data [$famboco_edit(tree) itemcget $ch -data]
	    if {$data == [list bccat $f $cat]} {
		set parent $ch
		break
	    }
	}
	if {$parent == ""} {
	    set cpos [expr [llength $children] - 1]
	    set parent bc-$f-cat[expr $cpos + 1]
	    $famboco_edit(tree) insert end fam-$f $parent \
		-text $cat -data [list bccat $f $cat] -open 1
	}
	set nch end
    } else {
	set parent fam-$f
	set curnodes [$famboco_edit(tree) nodes fam-$f]
	if {([llength $curnodes] > 1) && ([lindex [$famboco_edit(tree) itemcget [lindex $curnodes end-1] -data] 0] == "create_new")} {
	    set nch [expr [llength $curnodes] - 2]
	} else {
	    set nch [expr [llength $curnodes] - 1]
	}
    }
    
    set key bc-$f-$num
    catch {
       $famboco_edit(tree) insert $nch $parent $key \
	   -text $title -data [list bc $f $num] \
	   -image bc_active_image
    }
    
    return $num
}

# What use is the in_fbc_order parameter?  All callers expect that it will be 1

proc famboco_make_default_bc {type isactive famname in_fbc_order} {
    global famboco_info
    
    set bc [list $type]
    set maxpos 0
    set defp 1
    set swap 0
    foreach arg $famboco_info(args,$type) {
	set def ""; set pos ""; set deff ""; set title ""
	foreach {n v} [lrange $arg 1 end] {
	    if {$n == "default"} { set def $v }
	    if {$n == "default_func"} { set deff $v }
	    if {$in_fbc_order && ($n == "pos")} { set pos $v }
	    if {$n == "title"} { set title $v }
	}
	if {$def == "__family_name__"} { set def $famname }
	if {$deff != ""} {
	    set def [$deff $famname $type $title]
	}
	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]]
    }
    if {$isactive == -1} {
	if [info exists famboco_info(default_active,$type)] {
	    set isactive $famboco_info(default_active,$type)
	} else {
	    set isactive 0
	}
    }
    return [concat $isactive $bc]
}

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 "" -1
}

proc famboco_edit_newfam {d} {
    global famboco_data

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

proc famboco_edit_populate {d} {
    global famboco_data famboco_edit famboco_noedit
    
    eval $famboco_edit(tree) delete [$famboco_edit(tree) nodes root]
    $famboco_edit(tree) insert end root dim-3 -text "Volumes" \
	-data "dim 3" -open 0 -image bc_volume_image
    $famboco_edit(tree) insert end root dim-2x -text "Surfaces" \
	-data "" -open 0 -image bc_surface_image
    $famboco_edit(tree) insert end dim-2x dim-2s -text "One-sided" \
	-data "dim 2s" -open 0 -image bc_surface_1_image
    $famboco_edit(tree) insert end dim-2x dim-2d -text "Two-sided" \
	-data "dim 2d" -open 0 -image bc_surface_2_image
    $famboco_edit(tree) insert end dim-2x dim-2 -text "Mixed/unknown" \
	-data "dim 2" -open 0 -image bc_unknown_image
    $famboco_edit(tree) insert end root dim-1 -text "Edges" \
	-data "dim 1" -open 0 -image bc_edge_image
    $famboco_edit(tree) insert end root dim-0 -text "Nodes" \
	-data "dim 0" -open 0 -image bc_node_image
    $famboco_edit(tree) insert end root dim-mixed -text "Mixed/unknown" \
	-data "dim mixed" -open 0 -image bc_unknown_image
    
    foreach f [lsort [array names famboco_data]] {
	    if {($f == "__param__") || ($f == "ORFN")} continue
	    if {$famboco_edit(only_un) && ($famboco_data($f) != "")} continue
	    set dim [ic_boco_fam_dim $f]
	    if {$dim == "2"} {
	        set dim2 [ic_boco_fam_dim $f 1]
            if {$dim2 == "2s 2"} {
                set dim 2s
            } elseif {$dim2 == "2d 2"} {
                set dim 2d
            }
        }
	    $famboco_edit(tree) insert end dim-$dim fam-$f -text $f -data [list fam $f] -image bc_family_image
	    set famboco_edit(family_dim_$f) $dim
	    famboco_edit_populate_family $f
    }
}

proc famboco_edit_populate_family {f} {
    global famboco_edit famboco_noedit famboco_data famboco_info
    global got
    catch {unset got}
    $famboco_edit(tree) delete [$famboco_edit(tree) nodes fam-$f]
    set num 1
    foreach bc $famboco_data($f) {
	set act [lindex $bc 0]
	set bctype [lindex $bc 1]
	if [info exists famboco_info(category,$bctype)] {
	    set cat $famboco_info(category,$bctype)
	} else {
	    set cat ""
	}
	lappend got($cat) [list $bc $num $act]
	set famboco_edit(active,$f,$num) $act
	incr num
    }
    
    set cnum 0
    foreach cat [lsort [array names got]] {
	incr cnum
	if {$cat == ""} {
	    set parent fam-$f
	} else {
	    set parent bc-$f-cat$cnum
	    $famboco_edit(tree) insert end fam-$f $parent \
		-text $cat -data [list bccat $f $cat]
	}
	foreach bcxx $got($cat) {
	    set bc [lindex $bcxx 0]
	    set num [lindex $bcxx 1]
	    set act [lindex $bcxx 2]
	    set xx [famboco_bc_type_title_dims $bc]
	    if [info exists famboco_noedit([lindex $xx 0])] continue
	    
	    if {($famboco_edit(curfam) == $f) && 
		![info exists famboco_edit(active,$f,$num)]} {
		continue
	    }
	    
	    set key bc-$f-$num
	    $famboco_edit(tree) insert end $parent $key \
		-text [lindex $xx 1] -data [list bc $f $num] \
		-image bc_[expr {$act ? "active" : "inactive"}]_image
	}
    }
    if !$famboco_info(all_bcs) {
	$famboco_edit(tree) insert end fam-$f create_new_bc-$f \
	    -text "Create new" -data [list create_new $f \
					  $famboco_edit(family_dim_$f)]
	$famboco_edit(tree) insert end fam-$f paste_bc-$f \
	    -text "Paste" -data [list paste $f \
				     $famboco_edit(family_dim_$f)]
    }
}

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

# reload == 1 means if there are no params defined, load them from the .par file associated
# with this project if there is one
#
# reload == 0 means ..
#
# reload == -1 means ..
#
# reload == 2 means ..

proc famboco_edit_params {{sname ""} {reload 1}} {
    global param_info param_edit param_values solution solver_info
    global famboco_info

    if [info exists famboco_info] {
       if ![famboco_get_info $famboco_info(name)] { return 0 }
    } elseif [info exists solution(solver)] {
	if ![famboco_get_info $solution(solver)] { return 0 }
    } else {
        return 0
    }

    if ![info exists param_info] {
	mess "This solver has no parameters\n"
	return 0
    }

    if {$reload == 2} {
	set file [famboco_load_params $sname]
	if {$file == ""} {
	    mess "There is no parameter data available\n" red
	    error
	    return 0
	}
    } else {
	catch {unset param_values}
	set vals [ic_param_get_all]
	if {($vals == "") && $reload} {
	    famboco_load_params $sname
	    set vals [ic_param_get_all]
	}
	array set param_values $vals
    }

    if [info exists param_values] {
	set saved_values [array get param_values]
    } else {
	set saved_values ""
    }
    set form .param_edit
    form_kill .param_edit
    form_kill .famboco

    set buts [list [list Accept {
	if [famboco_check_all_entries] {
	    form_done .param_edit
	    set form_done_flag(.param_edit) 1
	}
    }] cancel]

    set d [form_init $form "Solver parameters" "" $buts]
    if {$d == ""} { return 0 } ;# this can happen if you double-click
    set famboco_info(form) $form
    set famboco_info(what) param

    set subs [form_paned $d.p [list "-weight 0" "-weight 1"] \
		  h {}]
    pack $d.p -fill both -expand 1
    set treefr [lindex $subs 0]
    set descfr [lindex $subs 1]
    set frame [form_scrollwin $descfr.f 500 500 {0 0 -sticky nwes}]

    set tree [form_tree $treefr.f {0 0}]

    $tree bindText <1> famboco_edit_params_select
    $tree bindText <Double-1> famboco_edit_params_doubleclick
    grid columnconfigure $treefr 0 -weight 1000
    grid rowconfigure $treefr 0 -weight 1000
    grid columnconfigure $descfr 0 -weight 1000
    grid rowconfigure $descfr 0 -weight 1000

    catch {unset param_edit}
    #set param_edit(save_file) $file
    set param_edit(all_nodes) ""
    set param_edit(cur_node) ""
    set param_edit(tree) $tree
    set param_edit(frame) $frame
    famboco_edit_params_build

    futil_setup_switch $frame param_edit(cur_node) "" "" 0

    set first [lindex $param_info(children,) 0]
    famboco_edit_params_select :$first

    if [info exists param_edit(saved_size)] {
	set s $param_edit(saved_size)
	.param_edit.main.p.f0 configure -width [lindex $s 0] -height [lindex $s 1]
	.param_edit.main.p.f1 configure -width [lindex $s 1] -height [lindex $s 2]
    }
    form_finish bot

    set ok [form_wait .param_edit]
    #set param_edit(saved_size) [list [winfo width .param_edit.main.p.f0] \
				      [winfo height .param_edit.main.p.f0] \
				      [winfo width .param_edit.main.p.f1] \
				    [winfo height .param_edit.main.p.f1]]

    if $ok {
	famboco_finish_editing
	if {$reload != -1} {
	    ic_param_set_all [array get param_values]
	    if {$reload == 2} {
		#famboco_edit_save_param_values $file
		ic_param_save $file
	    }
	}
	ic_param_set_modified
	return 1
    } else {
	array set param_values $saved_values
	return 0
    }
}

proc famboco_finish_editing {} {
    global param_info param_edit param_values solution solver_info
    global famboco_info

    set curnode [$param_edit(tree) selection get]
    if {$curnode != ""} {
	famboco_edit_params_after_edit $curnode
    }

#figure nodes deleted during param_edit, Onyx #52378, Anand
    foreach v [array names param_edit] {
	  if [string match val,* $v] {
		if [regexp ,DELETED$ $v] {
	    	set del [lindex [split $v ,] 1]
			set is_deleted($del) 1
		}
	  }
	}

    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) != "") &&
		($param_edit(choice_pnode,$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
		}
	    }
#do not set param_values for deleted param_edit nodes; unset if any, Onyx #52378, Anand
		set del [lindex [split $pname ,] 0]
		if [info exists is_deleted($del)] {
		catch {unset param_values($del)} res
		} else {
	    set param_values($pname) $pval
		}
	}
    }
}

proc famboco_load_params {sname {quiet 0}} {
    global solver_info solution project_dir famboco_info param_info
    global file_name famboco_edit file_name

    # one won't find params for generic; set some params too -JKA
    if {![info exists famboco_edit(solver)] || ($famboco_edit(solver) == "Generic")} {
	    set famboco_info(all_bcs) 0
	    set famboco_info(ignore_dims) 0
	    set famboco_info(has_icons) 0
	    set famboco_edit(solver) Generic
	    return ""
    }

    if {$sname == ""} {
	    set sname [lindex $solver_info($famboco_edit(solver)) 1]
    }
    if [info exists file_name(solver_params)] {
	    if {$file_name(solver_params) == ""} {
	        foreach p {settings domain tetin} {
		        if {[info exists file_name($p)] && ($file_name($p) != "")} {
		            set file_name(solver_params) [file rootname $file_name($p)].par
		            if !$quiet { mess "Using $file_name(solver_params) for param file name\n" }
		        }
	        }
	    }
	    set file $file_name(solver_params)
    } elseif [info exists project_dir] {
	    set file $project_dir/mesh/$sname.par
    } else {
	    set file $file_name(mesh_dir)/$sname.par
    }
    if [file exists $file] {
#	    mess "loading existing par file $file\n" blue
	    ic_param_load $file
    } elseif {$famboco_info(predefs) != ""} {
	    mess "Can't find existing par file $file\n" red
	    set names ""
	    foreach p $famboco_info(predefs) {
	        set predef([lindex $p 1]) [lindex $p 0]
	        lappend names [lindex $p 1]
	    }
	    set ch [choose_one "Select a predefined set of\nproblem parameters." $names]
	    if {$ch != ""} {
	        ic_param_load $predef($ch)
	    } else {
	        return ""
	    }
    }
    famboco_edit_initialize_param_node_defaults

    if {[info command famboco_after_load_param_callback] != ""} {
	    famboco_after_load_param_callback
    }

    if [info exists param_info] {
	    return $file
    } else {
	    return ""
    }
}

# 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
	set param_edit(maxnum,$node) $param_info(maxnum,$pnode)

	set name_arg ""
	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
		    set name_arg $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]
		set par [lindex [split [string range $pp $ll end] ,] 1]
		if ![string match *:* $ch] {
		    set inst($ch) 1
		    if {($par != "") && ($par == $name_arg)} {
			set this_name($ch) $param_values($pp)
		    }
		}
	    }
	}
	foreach ch [lsort [array names inst]] {
	    set chnode $node/$ch
	    if ![$tree exists $chnode] {
		if [info exists this_name($ch)] {
		    set chname $this_name($ch)
		} else {
		    set chname $ch
		}
		$tree insert end $node $chnode -text $chname -open 0
		lappend param_edit(all_nodes) $chnode

		if {[info exists param_info(active,$node)] &&
		    ($param_info(active,$node) == "0")} {
		    $tree itemconfigure $node -fill gray
		}
	    }
	    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
	    set paste ${node}/paste_node
	    if ![$tree exists $cnew] {
		$tree insert end $node $cnew -text "Create new" \
		    -data "create_new $node"
		$tree insert end $node $paste -text "Paste" \
		    -data "paste $node"
		set param_edit(info_node,$cnew) $pnode
		set param_edit(create_new_node,$cnew) 1
		set param_edit(info_node,$paste) $pnode
		set param_edit(paste_node,$paste) 1

		$tree itemconfigure $node -data "create_new $node"
	    }
	}

    } 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 0
		if {[info exists param_info(active,$chnode)] &&
		    ($param_info(active,$chnode) == "0")} {
		    $tree itemconfigure $chnode -fill gray
		}
		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]
	set pos [$tree index $parent/create_new_node]
    } elseif [info exists param_edit(paste_node,$crnode)] {
	set parent [$tree parent $crnode]
	set pos [$tree index $parent/create_new_node]
    } else {
	set parent $crnode
	set pos [$tree index $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 maxnum $param_edit(maxnum,$parent)
	if {$maxnum && ($maxnum <= $i)} {
	    set s [expr {($maxnum == 1) ? "" : "s"}]
	    user_error "You can create at most $maxnum node$s of this type"
	    return ""
	}

	$tree insert $pos $parent $node -text $nameval -open 0
	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_doubleclick {node} {
    global param_edit

    set data [$param_edit(tree) itemcget $node -data]
    if {[lindex $data 0] == "create_new"} {
	set node [famboco_edit_params_create_new [lindex $data 1] "" ""]
	if {$node == ""} return
	famboco_edit_params_select $node
    }
}

proc famboco_edit_params_select {node} {
    global param_info param_edit param_values

    if ![famboco_check_all_entries] { return 0 }

    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 "" ""]
	if {$node == ""} { return 0 }
    } elseif [info exists param_edit(paste_node,$node)] {
	if ![info exists param_edit(clipboard)] {
	    user_error "No data available to paste"
	    return 0
	}
	set node [famboco_edit_params_create_new $node "" \
		      $param_edit(clipboard)]
	if {$node == ""} { return 0 }
    }

    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
    }

    busy 1

    set win $frame.$node

    if {[winfo children $win] == ""} {
	if [info exists param_edit(is_multiple,$node)] {
	    if !$param_info(no_create_or_delete,$pnode) {
	    	set args [list {label title "Please select an item from the list\nin the tree below this one\nor create a new one with \"Create New\""}]
	    } else {
	    	set args [list {label title "Please select an item from the list\nin the tree below this one"}]
	    }
	    set html ""
	    set bubble ""
	    set after ""
	    set bcargs ""

	} else {
	    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 {$args == ""} {
		set args [list {label value "Please select an item from the list\nin the tree below this one"}]
	    }
	    if {$param_info(multiple,$pnode) &&
		!$param_info(no_create_or_delete,$pnode)} {
		lappend args [list arg type button_box title {Delete Copy} command \
				  [list "famboco_edit_params_delete x $node" \
				       "famboco_copy_param_node $pnode $node"]]
	    }
	    set html $param_info(html,$pnode)
	    set bubble $param_info(bubble,$pnode)
	    set after "famboco_edit_params_after_edit $node"
	}
	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 $after "" \
	    $param_info(active,$pnode)
    }
    update idle

    set pwin [winfo parent $win]
    set cw [winfo reqwidth $win]
    set ch [winfo reqheight $win]
    $pwin configure -width $cw -height $ch

    busy 0
    return 1
}

proc famboco_edit_initialize_param_node_defaults {} {
    global param_info param_values

    foreach key [array names param_info] {
	if [string match args,* $key] {
	    set pn [lindex [split $key ,] 1]
	    if $param_info(multiple,$pn) continue

	    foreach a $param_info($key) {
		foreach {n v} [lrange $a 1 end] {
		    set a_$n $v
		}
		if [info exists param_values($pn,$a_name)] continue

		set param_values($pn,$a_name) $a_default
	    }
	}
    }
}

proc famboco_edit_params_delete {dd node} {
    global param_edit param_values

    if {[info exists param_edit(tree)] && [winfo exists $param_edit(tree)] && [$param_edit(tree) exists $node]} {
	# We reverse them so that the children get deleted before the
	# parents - in the reverse order they were added to the list.
	foreach descendant [lreverse $param_edit(all_nodes)] {
	    if [string match $node:* $descendant] {
		famboco_edit_params_delete $dd $descendant
	    }
	}

	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 - 3} { set n [expr $nch - 3] }
	    if {$n >= 0} {
		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)
	}
    }
    ic_param_set_modified
}

# 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 {clear 1}} {
    global param_values param_info param_values_modified param_edit

    mess "Loading solver parameters from $file.\n"
    if $clear { 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] == "problem_type_defaults"} {
	    # do nothing here
	} elseif {[lindex $line 0] == "begin"} {
	    set nname [lindex $line 1]
	    set ninst [lindex $line 2]
	    regsub -all " " $ninst __ ninst
	    regsub -all \t $ninst __ ninst
	    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

    # If we don't do this there will be old values there and
    # ic_param_get_values will return bad stuff
    catch {unset param_edit}

    if {[trace vinfo param_values] == ""} {
	trace variable param_values w "set param_values_modified 1; list"
    }
}

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 {pats ""}} {
    global param_values param_info param_values_modified

    #parray param_values

    if {[info command famboco_before_save_param_callback] != ""} {
	famboco_before_save_param_callback
    }

    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]] {
	if {$pats != ""} {
	    set ok 0
	    foreach p $pats {
		if [string match $p* $v] { set ok 1; break }
	    }
	    if !$ok continue
	}
	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 {[info exists param_info(multiple,$thisnode1)] && $param_info(multiple,$thisnode1)} { continue }

	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 != {{}} && $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
    catch {unset param_values_modified}
}

proc famboco_annotate_family_boco {file} {
    global famboco_info env
    
    set fp [open $file r]
    set fp2 [open ${file}_new w]
    while {![eof $fp]} {
	set line [gets $fp]
	if [string match \#ARG* $line] continue
	if {($line == "") || [string match \#* $line]} {
	    puts $fp2 $line
	    continue
	}
	set this_lines [list $line]
	while {[string match *\\\\ $line] && ![eof $fp]} {
	    set line [string trimright $line \\]
	    set line2 [gets $fp]
	    lappend this_lines $line2
	    append line " [string trim $line2]"
	}
	set bctype [lindex $line 1]
	if [info exists famboco_info(args,$bctype)] {
	    set p 0
	    catch {unset atpos}
	    foreach arg $famboco_info(args,$bctype) {
		set name noname
		set pos -1
		foreach {n v} [lrange $arg 1 end] {
		    switch $n {
			bc_name { set name $v }
			pos { set pos $v }
		    }
		}
		if {$pos == -1} { set pos [incr p] }
		set atpos($pos) $name
	    }
	    set ok 0
	    set anno "\#ARGS"
	    set na [expr [llength $line] - 2]
	    for {set i 1} {$i <= $na} {incr i} {
		if [info exists atpos($i)] {
		    lappend anno $atpos($i)
		    set ok 1
		} else {
		    lappend anno noname
		}
	    }
	    if $ok {
		puts $fp2 $anno
	    }
	}
	foreach line $this_lines {
	    puts $fp2 $line
	}
    }
    close $fp
    close $fp2
    if [ic_wb2_is_running] {
        cmd_rename ${file}_new $file
    } else {
        cmd_rename $file ${file}_old
        cmd_rename ${file}_new $file
    }
}

proc famboco_wizard {prog args} {
    global env param_values param_edit
    
    if [info exists param_edit(save_file)] {
	set old_save_file $param_edit(save_file)
    }
    if {[file pathtype $prog] != "absolute"} {
	#set prog $env(ICEM_ACN)/icemcfd/output-interfaces/$prog
    }
    set file [cmd_tempfile tmp_edit .par]
    form_cancel .param_edit
    famboco_finish_editing
    famboco_edit_save_param_values $file
    form_cancel .param_edit; update
    puts "$prog $args $file"
    eval exec \"$prog\" $args \"$file\"
    famboco_edit_initialize_param_node_defaults
    ic_param_load $file
    if {[info command famboco_after_load_param_callback] != ""} {
	famboco_after_load_param_callback
    }
    if [info exists old_save_file] {
	set param_edit(save_file) $old_save_file
    }
    famboco_edit_params "" 0
}

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

# THESE SHOULD MOVE TO THE BATCH AREA ...  but for now the params are kept
# in the gui area.

if 0 {
    
## Are there params available?

proc ic_param_is_loaded {} {
    global param_values
    return [array size param_values]
}
set dont_record_command(ic_param_is_loaded) 1

## Are the params modified?

proc ic_param_is_modified {} {
    global param_values param_values_modified

    return [info exists param_values_modified]
}
set dont_record_command(ic_param_is_modified) 1

## Set the parameters modified flag.

proc ic_param_set_modified {{flag 1}} {
    global param_values_modified

    set param_values_modified $flag
}

## 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 param_values param_values_modified
    
    if {[trace vinfo param_values] == ""} {
	trace variable param_values w "set param_values_modified 1; list"
    }

    if {[info exists param_edit(tree)] && [winfo exists $param_edit(tree)]} {
	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]    
	}
    } else {
	foreach {n v} $values {
	    set param_values($parent/$name,$n) $v
	}
    }
}

## Delete the given node.

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

## List the children of the given node.  This returns values of the form
## :foo:bar:baz/baz13, unless the tail arg is 1, in which case it just
## returns baz13

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]
}
set dont_record_command(ic_param_list_children) 1

## List all the node names

proc ic_param_node_list {{pat *}} {
    global param_values
    
    foreach pv [array names param_values $pat,*] {
	set found([lindex [split $pv ,] 0]) 1
    }
    return [array names found]
}
set dont_record_command(ic_param_node_list) 1

## Does a given node exist?

proc ic_param_node_exists {node} {
    global param_values
    
    return [expr {([array names param_values $node,*] != "") || 
		  ([array names param_values $node/*] != "") || 
		  ([array names param_values $node:*] != "")}]
}
set dont_record_command(ic_param_node_exists) 1

## Make up a new name (prefix followed by a number) for a child of a node.
## If prefix is - then that means just use a number

proc ic_param_unused_child_name {node {prefix ""}} {
    foreach ch [ic_param_list_children $node] {
	set found([lindex [split $ch /] 1]) 1
    }
    if {$prefix == "-"} {
	set prefix ""
    } elseif {$prefix == ""} {
	set prefix [lindex [split $node :] end]
    }
    for {set i 1} {[info exists found($prefix$i)]} {incr i} {}
    return $prefix$i
}
set dont_record_command(ic_param_unused_child_name) 1

## 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
    
    # 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.
    foreach pp [array names param_values $node,*] {
	set t [lindex [split $pp ,] 1]
	set v $param_values($pp)
	set ret($t) $v
    }
    
    foreach pp [array names param_edit val,$node,*] {
	set t [lindex [split $pp ,] 2]
	set v $param_edit($pp)
	set ret($t) $v
    }
    return [array get ret]
}
set dont_record_command(ic_param_get_values) 1

## 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 {
	if [info exists param_edit] {
	    set param_edit(val,$node,$t) $v
	}
	set param_values($node,$t) $v
    }
    if {[trace vinfo param_values] == ""} {
	trace variable param_values w "set param_values_modified 1; list"
    }
}

## Rename a bunch of nodes.  If delete_non_matching is 1 then delete those
## which don't match.

proc ic_param_rename {oldprefix newprefix {delete_non_matching 0}} {
    global param_values
    
    set keys [concat [array names param_values $oldprefix,*] \
		  [array names param_values $oldprefix/*] \
		  [array names param_values $oldprefix:*]]
    set opl [string length $oldprefix]
    foreach k $keys {
	set k2 $newprefix[string range $k $opl end]
	#puts "$k -> $k2"
	set param_values($k2) $param_values($k)
	set newkey($k2) 1
	unset param_values($k)
    }
    if $delete_non_matching {
	foreach p [array names param_values] {
	    if ![info exists newkey($p)] { unset param_values($p) }
	}
    }
}

if {![interp exists batch_interp] && ![info exists app_name]} {
    
    proc ic_boco_fam_dim {fam} {
	global projlib uns_cur
	
	set dimen ""
	if {[info exists projlib] && ($projlib != "")} {
	    set pid [$projlib lookup_family_pid $fam]
	    if {$pid != ""} {
		foreach {type dim} {material 3 surface 2 curve 1 point 0} {
		    if {[$projlib get_objects_in_family $type $pid] != ""} {
			if {$dimen != ""} {
			    return mixed
			} else {
			    set dimen $dim
			}
		    }
		}
	    }
	}
	if {$dimen == ""} { return mixed } else { return $dimen }
    }
    proc ic_boco_clear_icons {} {}
    proc ic_boco_add_icon {args} {}
    
    proc form_paned {w ch_opts side tabdata} {
	if {$side == "h"} { set pside bottom } else { set pside left }
	PanedWindow $w -side $pside -pad 0
	set ret ""
	foreach ch $ch_opts {
	    lappend ret [eval $w add $ch]
	}
	form_dotab $w $tabdata nwes
	return $ret
    }
    proc form_scrollwin {w width height tabdata} {
	set sw [ScrolledWindow $w]
	set sf [ScrollableFrame $sw.f -width $width -height $height]
	$sw setwidget $sf
	set subf [$sf getframe]
	form_dotab $w $tabdata nwes
	return $subf
    }
    proc form_tree {w tabdata} {
	set sw [ScrolledWindow $w]
	form_dotab $sw $tabdata nwes
	set tree $sw.tree
	Tree $tree -relief flat -borderwidth 0
	$sw setwidget $tree
	return $tree
    }
}
}

if {[package versions BWidget] == ""} {
# bug 12346
#    source $guibase_dir/family_boco_old.tcl
}

# This is a bit out of place here but ...

proc get_solver_info {} {
    global solution solver_info env only_solvers
    
    #if ![info exists solver_info] just do it
    catch {unset solver_info}
    
    set dir $env(ICEM_ACN)/icemcfd/output-interfaces
    if [file exists $dir/solver_table.tcl] {
	source $dir/solver_table.tcl
	foreach ch $solver_interfaces {
	    set solver_info([lindex $ch 0]) [concat $ch [list $dir]]
	}
    } else {
	mess "Warning: no $dir/solver_table.tcl file found\n" red
    }
    if {[info exists env(HOME)] && \
	    [file exists $env(HOME)/output-interfaces/solver_table.tcl]} {
	set dir $env(HOME)/output-interfaces
	source $dir/solver_table.tcl
	foreach ch $solver_interfaces {
	    set solver_info([lindex $ch 0]) [concat $ch [list $dir]]
	}
    }
    if {[file exists ./output-interfaces/solver_table.tcl]} {
	set dir [pwd]/output-interfaces
	source $dir/solver_table.tcl
	foreach ch $solver_interfaces {
	    set solver_info([lindex $ch 0]) [concat $ch [list $dir]]
	}
    }
    if [info exists only_solvers] {
	foreach ss [array names solver_info] {
	    if ![info exists only_solvers($ss)] {
		unset solver_info($ss)
	    }
	}
    }
}

proc famboco_list_csystems {} { return "" }

proc famboco_to_schema {file} {
    global famboco_info
    
    set fp [open $file w]
    set allargs [array names famboco_info]
    
    # first the categories
    
    foreach a $allargs {
	if {[regexp {args,(\w*)} $a dummy name]} {
	    set category __nocategory__
	    if {[info exists famboco_info(category,$name)]} {
		set category $famboco_info(category,$name)
		if {$category == ""} {
		    set category __nocategory__
		}
	    }
	    
	    lappend categories($category) $name
	}
    }
    
    # Write the menu categories
    foreach a [array names categories] {
	puts $fp [list CATEGORY $a $categories($a)]
    }
	    
    foreach a $allargs {
	if {[regexp {args,(\w*)} $a dummy name]} {
	    set arglist $famboco_info($a)
	    
	    set idx 0
	    set curbc ""
	    foreach arg $arglist {
		set ordinfo [famboco_schema_order [lrange $arg 1 end] $idx]
		lappend curbc $ordinfo
		incr idx
	    }
	    set category __nocategory__
	    if {[info exists famboco(category,$a)]} {
		set category $famboco(category,$a)
	    }
	    puts $fp "\"$name\" [llength $curbc]"
	    foreach l $curbc {
		puts $fp $l
	    }
	}
    }
    close $fp
}

proc famboco_schema_order {curarg defidx} {
    foreach {n v} $curarg {
	set $n $v
    }
    
    if {![info exists type]} {
	set type string
    }
    
    if {![info exists default]} {
	set default _nodefault_
    }
    
    if {![info exists title]} {
	set title _notitle_
    }
    
    if {![info exists name]} {
	set name _noname_
    }
    
    if {![info exists pos]} {
	set pos $defidx
    }
    
    if {![info exists collapse]} {
	set collapse 0
    }

    return "$pos \"$name\" $type $collapse \"$title\" \"$default\""
}



