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

# This routine sets the variables nfamilies family_name family_number.
# These get reset in import_save unless there are no surfaces (that is,
# unput is only editing a boco file).
#
# For leo, there is an additional set of variables that gets set:
# boco_nument(domain subface edge vertex patch family), boco_numbc(*),
# boco_data(*,n)

proc boco_load {bocofile {allbc 0} {prefix ""}} {
    global boco_data boco_nfamilies boco_family_name boco_family_number
    global boco_nument boco_numbc boco_modified
    
    set mod [info exists boco_modified]
    
    if {$prefix != ""} { set prefix $prefix, }
    
    set fp [open $bocofile r]
    
    set line [gets $fp]
    if ![string match {ICEM MULCAD*} $line] {
	close $fp
	user_error "File \"$bocofile\" is not a boco file: first line\
		is \"$line\""
	return
    }
    
    set types {domain subface edge vertex patch family freesub}
    
    foreach et $types {
	set nent($et) 0
	set boco_nument($prefix$et) 0
	set boco_numbc($prefix$et) 0
    }
    
    set keep_nline 0
    while {![eof $fp]} {
	if $keep_nline {
	    set line $nline
	    set keep_nline 0
	} else {
	    set line [gets $fp]
	}
	#puts "LINE $line"
	set nline [gets $fp]
	set bad 0
	switch -glob -- $line {
	    {*NB OF DOMAIN*} {
		if {[scan $nline "%d %d" boco_nument(${prefix}domain) \
			boco_numbc(${prefix}domain)] != 2} {
		    set bad 1
		}
		set etype domain
	    }
	    {*NB OF SUB-FACE*} {
		if {[scan $nline "%d %d" boco_nument(${prefix}subface) \
			boco_numbc(${prefix}subface)] != 2} {
		    set bad 1
		}
		set etype subface
	    }
	    {*NB OF ENTIT*} {
		if {[scan $nline "%d %d" boco_nument(${prefix}edge) \
			boco_numbc(${prefix}edge)] != 2} {
		    set bad 1
		}
		set etype edge
	    }
	    {*NB OF VERTICES*} {
		if {[scan $nline "%d %d" boco_nument(${prefix}vertex) \
			boco_numbc(${prefix}vertex)] != 2} {
		    set bad 1
		}
		set etype vertex
	    }
	    {*NB OF PATCHES*} {
		if {[scan $nline "%d %d" boco_nument(${prefix}patch) \
			boco_numbc(${prefix}patch)] != 2} {
		    set bad 1
		}
		set etype patch
	    }
	    {*NB OF PATCH FAM**} {
		if {[scan $nline "%d %d" boco_nument(${prefix}family) \
			boco_numbc(${prefix}family)] != 2} {
		    set bad 1
		}
		set etype family
	    }
	    {*NB OF FREE SUB**} {
		if {[scan $nline "%d %d" boco_nument(${prefix}freesub) \
			boco_numbc(${prefix}freesub)] != 2} {
		    set bad 1
		}
		set etype freesub
	    }
	    {*POSIB*} {
		set pointers($etype) ""
		foreach n [split $nline ,] {
		    if {$n != ""} { lappend pointers($etype) $n }
		}
		while {[llength $pointers($etype)] <= \
			   $boco_nument(${prefix}$etype)} {
		    foreach n [split [gets $fp] ,] {
			if {$n != ""} { lappend pointers($etype) $n }
		    }
		}
	    }
	    {*NSIBOC*} {
		if !$boco_nument(${prefix}$etype) {
		    set numboco($etype) ""
		    set keep_nline 1
		} else {
		    set numboco($etype) ""
		    foreach n [split $nline ,] {
			if {$n != ""} { lappend numboco($etype) $n }
		    }
		    
		    while {[llength $numboco($etype)] < \
			    $boco_nument(${prefix}$etype)} {
			foreach n [split [gets $fp] ,] {
			    if {$n != ""} { lappend numboco($etype) $n }
			}
		    }
		}
	    }
	    {*TOTAL NB OF B.C*} {
		if {[scan $nline "%d %d" totalbc array_size] != 2} {
		    set bad 1
		}
	    }
	    {*CSIBOC,SIBOC*} {
		for {set num 0} {$num < $array_size} {incr num} {
		    if $num {
			set char_data($num) [gets $fp]
		    } else {
			set char_data($num) $nline
		    }
		    set float_data($num) ""
		    foreach ch [split [gets $fp] ,] {
			lappend float_data($num) [string trim $ch]
		    }
		}
	    }
	    default {
		# Just skip it.
	    }
	}
	if $bad {
	    close $fp
	    user_error "Bad line in boco file \"$bocofile\":\n\
		    $line\n$nline"
	}
    }
    close $fp
    
    if {$prefix == ""} {
	catch {unset boco_data}
    } else {
	foreach i [array names boco_data] {
	    if [string match $prefix* $i] {
		unset boco_data($i)
	    }
	}
    }
    
    if $allbc {
	set tote 0; set totb 0
	foreach et $types {
	    for {set i 1} {$i <= $boco_nument(${prefix}$et)} {incr i} {
		set boco_data(${prefix}$et,$i) ""
		set p [lindex $pointers($et) [expr {$i - 1}]]
		set n [lindex $numboco($et) [expr {$i - 1}]]
		for {set j 0} {$j < $n} {incr j} {
		    set k [expr {$p + $j - 1}]
		    if {![info exists char_data($k)] || \
			    ![info exists float_data($k)]} {
			mess "Error: boco file is missing data for\
				$et $j (position $k) -- skipping\n" red
			continue
		    }
		    lappend boco_data(${prefix}$et,$i) \
			    [concat [list $char_data($k)] $float_data($k)]
		    incr totb
		}
		incr tote
	    }
	}
	mess "Loaded $tote objects, $totb bocos.\n"
    } else {
	set boco_nfamilies 0
	catch {unset boco_family_number}
	catch {unset boco_family_name}
	set totb 0
	for {set i 0} {$i < $array_size} {incr i} {
	    if {[string index $char_data($i) 0] == "*"} {
		set fname [string range $char_data($i) 1 end]
		boco_add_family $fname
		
	    } elseif [info exists fname] {
		lappend boco_data($fname) \
			[concat [list $char_data($i)] $float_data($i)]
		incr totb
	    } else {
		puts "WARNING: skipping boco $char_data($i)\n$float_data($i)"
	    }
	}
	mess "Loaded $boco_nfamilies families, $totb bocos.\n"
    }
    if !$mod { catch {unset boco_modified} }
}

proc boco_add_family {fname} {
    global boco_nfamilies boco_family_number boco_family_name boco_data
    global boco_modified
    
    incr boco_nfamilies
    set boco_family_name($boco_nfamilies) $fname
    set boco_family_number($fname) $boco_nfamilies
    set boco_data($fname) ""
    set boco_modified 1
    return $boco_nfamilies
}

proc boco_save {bocofile {boco boco}} {
    global ${boco}_nfamilies ${boco}_family_name ${boco}_data
    global ${boco}_modified
    
    set fams ""
    if [info exists ${boco}_nfamilies] {
	set nf [set ${boco}_nfamilies]
	for {set i 0} {$i < $nf} {incr i} {
	    lappend fams [set ${boco}_family_name([expr {$i + 1}])]
	}
    } else {
	set fams [array names ${boco}_data]
	set nf [llength $fams]
    }
    if !$nf {
	mess "Boco data not written -- no families\n"
	return
    }
    
    set line2 ""
    set line3 ""
    set pos 1
    for {set i 0} {$i < $nf} {incr i} {
	set n [lindex $fams $i]
	set nb 1
	if [info exists ${boco}_data($n)] {
	    incr nb [llength [set ${boco}_data($n)]]
	}
	lappend line2 $pos
	lappend line3 $nb
	incr pos $nb
    }
    lappend line2 0
    set line2 [boco_line_join $line2]
    set line3 [boco_line_join $line3]
    incr pos -1
    set line1 "$nf $pos"
    
    set fp [open $bocofile w]
    
    puts $fp "ICEM MULCAD - VERSION 3.1.4 - BOUNDARY CONDITION DATA"
    puts $fp "NB OF DOMAINS , NB OF B.C. :"
    puts $fp $line1
    puts $fp "POINTERS ARRAY (POSIB) :"
    puts $fp $line2
    puts $fp "NUMBER OF BOUND. COND. RELATED TO EACH DOMAIN (NSIBOC) :"
    puts $fp $line3
    puts $fp "NB OF SUB-FACES , NB OF B.C. :"
    puts $fp $line1
    puts $fp "POINTERS ARRAY (POSIB) :"
    puts $fp $line2
    puts $fp "NUMBER OF BOUND. COND. RELATED TO EACH SUB-FACE (NSIBOC) :"
    puts $fp $line3
    puts $fp "NB OF ENTITIES , NB OF B.C. :"
    puts $fp $line1
    puts $fp "POINTERS ARRAY (POSIB) :"
    puts $fp $line2
    puts $fp "NUMBER OF BOUND. COND. RELATED TO EACH ENTITY (NSIBOC) :"
    puts $fp $line3
    puts $fp "NB OF VERTICES , NB OF B.C. :"
    puts $fp $line1
    puts $fp "POINTERS ARRAY (POSIB) :"
    puts $fp $line2
    puts $fp "NUMBER OF BOUND. COND. RELATED TO EACH VERTEX (NSIBOC) :"
    puts $fp $line3
    puts $fp "NB OF PATCHES , NB OF B.C. :"
    puts $fp $line1
    puts $fp "POINTERS ARRAY (POSIB) :"
    puts $fp $line2
    puts $fp "NUMBER OF BOUND. COND. RELATED TO EACH PATCH (NSIBOC) :"
    puts $fp $line3
    puts $fp "NB OF PATCH FAMILIES , NB OF B.C. :"
    puts $fp $line1
    puts $fp "POINTERS ARRAY (POSIB) :"
    puts $fp $line2
    puts $fp "NUMBER OF BOUND. COND. RELATED TO EACH PATCH FAMILY (NSIBOC) :"
    puts $fp $line3
    
    puts $fp "NB OF FREE SUBFACES , NB OF B.C. :"
    puts $fp "0 0"
    puts $fp "POINTERS ARRAY (POSIB) :"
    puts $fp "0"
    puts $fp "NUMBER OF BOUND. COND. RELATED TO EACH FREE SUBFACE (NSIBOC) :"
    
    puts $fp "TOTAL NB OF B.C. , SIBOC ARRAY SIZE :"
    puts $fp "$pos $pos"
    puts $fp "DATA FOR BOUNDARY CONDITIONS (CSIBOC,SIBOC) :"
    for {set i 0} {$i < $nf} {incr i} {
	set n [lindex $fams $i]
	puts $fp "*$n"
	puts $fp "0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000"
	if [info exists ${boco}_data($n)] {
	    foreach bc [set ${boco}_data($n)] {
		puts $fp [lindex $bc 0]
		set floats ""
		foreach fl [lrange $bc 1 10] {
		    if {$fl == ""} {
			lappend floats 0.0
		    } else {
			lappend floats $fl
		    }
		}
		while {[llength $floats] < 10} { lappend floats 0.0 }
		puts $fp [join $floats ,]
	    }
	}
    }
    puts $fp "NB OF ANALYSIS DATA SETS :"
    puts $fp "0"
    puts $fp "DATA FOR ANALYSIS (CANDAT,ANDAT) :"

    close $fp
    catch {unset ${boco}_modified}
}

proc boco_line_join {stuff} {
    set line ""
    set llen 0
    set nstuff [llength $stuff]
    foreach s $stuff {
	append line $s
	incr llen
	if {$llen < $nstuff} {
	    if {!($llen % 10)} {
		append line \n
	    } else {
		append line ,
	    }
	}
    }
    return $line
}

proc boco_save_full {bocofile {prefix ""}} {
    global boco_nument boco_numbc boco_data boco_dirty

    if [boco_save_by_families $bocofile $prefix] {
	mess "Wrote a family-style boco file.\n"
	return
    }
    
    if {$prefix != ""} { set prefix $prefix, }
    
    set fp [open $bocofile w]
    puts $fp "ICEM MULCAD - VERSION 3.1.4 - BOUNDARY CONDITION DATA"
    
    set ptr 1
    set array ""
    foreach xxx {
	{domain  DOMAINS DOMAIN}
	{subface SUB-FACES SUB-FACE}
	{edge    ENTITIES ENTITY}
	{vertex  VERTICES VERTEX}
	{patch   PATCHES PATCH}
	{family  {PATCH FAMILIES} {PATCH FAMILY}}
    } {
	set t [lindex $xxx 0]
	set pointers ""
	set nums ""

	set nume boco_nument(${prefix}$t)
	if ![info exists $nume] { set $nume 0 }
	set numb boco_numbc(${prefix}$t)
	if ![info exists $numb] { set $numb 0 }
	
	for {set i 1} {$i <= [set $nume]} {incr i} {
	    set n [llength $boco_data(${prefix}$t,$i)]
	    if $n {
		append pointers $ptr
		append nums $n
		incr ptr $n
	    } else {
		append pointers 0
		append nums 0
	    }
	    if {$i < $boco_nument(${prefix}$t)} {
		if {$i % 16} {
		    append pointers ,
		    append nums ,
		} else {
		    append pointers \n
		    append nums \n
		}
	    }
	    foreach boco $boco_data(${prefix}$t,$i) {
		append array [lindex $boco 0]\n
		set floats [lrange $boco 1 10]
		while {[llength $floats] < 10} { lappend floats 0.000000 }
		append array [join $floats ,]\n
	    }
	}
	if {$i > 1} {
	    append pointers ,0
	} else {
	    set pointers 0
	}
	puts $fp "NB OF [lindex $xxx 1] , NB OF B.C. :"
	puts $fp "[set $nume] [set $numb]"
	puts $fp "POINTERS ARRAY (POSIB) :"
	puts $fp $pointers
	puts $fp "NUMBER OF BOUND. COND. RELATED TO EACH [lindex $xxx 2] (NSIBOC) :"
	if {$nums != ""} { puts $fp $nums }
    }
    
    append array \n0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
    
    puts $fp "NB OF FREE SUBFACES , NB OF B.C. :"
    puts $fp "0 0"
    puts $fp "POINTERS ARRAY (POSIB) :"
    puts $fp "0"
    puts $fp "NUMBER OF BOUND. COND. RELATED TO EACH FREE SUBFACE (NSIBOC) :"
    
    puts $fp "TOTAL NB OF B.C. , SIBOC ARRAY SIZE :"
    puts $fp "[expr {$ptr - 1}] $ptr"
    puts $fp "DATA FOR BOUNDARY CONDITIONS (CSIBOC,SIBOC) :"
    
    puts $fp $array
    
    puts $fp "NB OF ANALYSIS DATA SETS :"
    puts $fp "0"
    puts $fp "DATA FOR ANALYSIS (CANDAT,ANDAT) :"

    close $fp
    catch {unset boco_dirty($prefix)}
}

# This just copies the data from the old boco variables to the new ones

proc boco_save_famboco {bocofile {boco boco}} {
    global ${boco}_nfamilies ${boco}_family_name ${boco}_data
    global ${boco}_modified famboco_data
    
    set fams ""
    if [info exists ${boco}_nfamilies] {
	set nf [set ${boco}_nfamilies]
	for {set i 0} {$i < $nf} {incr i} {
	    lappend fams [set ${boco}_family_name([expr {$i + 1}])]
	}
    } else {
	set fams [array names ${boco}_data]
	set nf [llength $fams]
    }
    if !$nf {
	mess "Boco data not written -- no families\n"
	return
    }
    
    catch {unset famboco_data}
    foreach n $fams {
	set bc ""
	if [info exists ${boco}_data($n)] {
	    foreach b [set ${boco}_data($n)] { lappend bc [concat 1 $b] }
	}
	set famboco_data($n) $bc
    }
    famboco_save $bocofile
    catch {unset ${boco}_modified}
}

proc boco_collapse {} {
    global boco_data boco_nument boco_numbc
    
    foreach t {domain subface edge vertex patch family} {
	set boco_numbc($t) 0
	for {set i 1} {$i <= $boco_nument($t)} {incr i} {
	    foreach d $boco_data($t,$i) {
		set found($d) 1
	    }
	    set boco_data($t,$i) [lsort [array names found]]
	    incr boco_numbc($t) [llength $boco_data($t,$i)]
	    catch {unset found}
	}
    }
}

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

# This saves multiblock bocofiles in the new style.
# It depends on the boco_sol_convert routine in boco_solver.tcl

proc boco_save_by_families {bocofile {prefix ""}} {
    global boco_famdata boco_fam_to_ent boco_ent_to_fam boco_nument boco_data
    global boco_fam_pointer boco_fam_number
    
    if {$prefix != ""} { set prefix $prefix, }
    
    if {![info exists boco_nument(${prefix}family)] || \
	    !$boco_nument(${prefix}family)} {
	mess "Can't save by families -- there are no families\n"
	return 0
    }
    set nfam $boco_nument(${prefix}family)
    
    if [catch {boco_sol_convert $prefix 1 0}] {
	mess "Can't save by families -- some members have different data\n"
	return 0
    }
    
    set curpt 1
    set output_data ""
    set family_names ""
    for {set i 1} {$i <= $nfam} {incr i} {
	set famstr [lindex [lindex $boco_data(${prefix}family,$i) 0] 0]
	if {[string index $famstr 0] == "*"} {
	    set fam [string range $famstr 1 end]
	} elseif {[string range $famstr 5 8] == "name"} {
	    set fam [string trim [string range $famstr 0 4]]
	} else {
	    mess "Can't save by families -- a family doesn't begin with * or end with name\n"
	    return 0
	}
	
	set boco_fam_pointer($fam) $curpt
	set boco_fam_number($fam) 0
	append output_data $famstr\n
	append output_data "0,0,0,0,0,0,0,0,0,0\n"
	incr boco_fam_number($fam)
	incr curpt
	if [info exists boco_famdata($fam)] {
	    foreach data $boco_famdata($fam) {
		set str [format %-5s%-5s [lindex $data 0] [lindex $data 1]]
		append output_data $str\n
		append output_data [join [lrange $data 2 11] ,]\n
		incr boco_fam_number($fam)
		incr curpt
	    }
	}
	lappend family_names $fam
    }
    append output_data "\n0,0,0,0,0,0,0,0,0,0"
    set nboco [expr {$curpt - 1}]

    set fp [open $bocofile w]
    puts $fp "ICEM MULCAD - VERSION 3.1.4 - BOUNDARY CONDITION DATA"
    
    foreach xxx {
	{domain  DOMAINS DOMAIN}
	{subface SUB-FACES SUB-FACE}
	{edge    ENTITIES ENTITY}
	{vertex  VERTICES VERTEX}
    } {
	boco_fam_putone $fp $boco_nument(${prefix}[lindex $xxx 0]) \
	    [lindex $xxx 0] [lindex $xxx 1] [lindex $xxx 2] ""
    }
    
    boco_fam_putone $fp 0 "" PATCHES PATCH ""
    
    boco_fam_putone $fp $nfam "" "PATCH FAMILIES" "PATCH FAMILY" $family_names
    
    boco_fam_putone $fp 0 "" "FREE SUBFACES" "FREE SUBFACE" ""
    
    puts $fp "TOTAL NB OF B.C. , SIBOC ARRAY SIZE :"
    puts $fp "$nboco $curpt"
    puts $fp "DATA FOR BOUNDARY CONDITIONS (CSIBOC,SIBOC) :"
    
    puts $fp $output_data
    
    puts $fp "NB OF ANALYSIS DATA SETS :"
    puts $fp "0"
    puts $fp "DATA FOR ANALYSIS (CANDAT,ANDAT) :"

    close $fp
    catch {unset boco_dirty($prefix)}
    return 1
}

proc boco_fam_putone {fp ne t name1 name2 fam_names} {
    global boco_fam_pointer boco_fam_number boco_ent_to_fam
    
    set pointers ""
    set nums ""
    
    set nb 0
    for {set i 1} {$i <= $ne} {incr i} {
	if {$fam_names == ""} {
	    set nn "boco_ent_to_fam($t $i)"
	    if [info exists $nn] {
		set f [set $nn]
	    } else {
		set f ""
	    }
	} else {
	    set f [lindex $fam_names [expr {$i - 1}]]
	}
	if {$f != ""} {
	    append pointers $boco_fam_pointer($f)
	    append nums $boco_fam_number($f)
	    incr nb $boco_fam_number($f)
	} else {
	    append pointers 0
	    append nums 0
	}
	if {$i < $ne} {
	    if {$i % 16} {
		append pointers ,
		append nums ,
	    } else {
		append pointers \n
		append nums \n
	    }
	}
    }
    if {$i > 1} {
	append pointers ,0
    } else {
	set pointers 0
    }
    puts $fp "NB OF $name1 , NB OF B.C. :"
    puts $fp "$ne $nb"
    puts $fp "POINTERS ARRAY (POSIB) :"
    puts $fp $pointers
    puts $fp "NUMBER OF BOUND. COND. RELATED TO EACH $name2 (NSIBOC) :"
    if {$nums != ""} { puts $fp $nums }
}

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

proc boco_edit {} {
    global boco_data boco_nfamilies boco_family_number
    global boco_scroller_win boco_current_boco form_done_flag
    global boco_only_none
    
    foreach b [array names boco_data] { set saved_data($b) $boco_data($b) }
    
    set boco_current_boco ""
    
    form_kill .bocos
    set d [form_init .bocos "Boundary condition data" bocos "done cancel"]
    
    form_label $d.l1 "Bocos for family:" {0 0}
    form_label $d.l2 "" {0 1}
    $d.l2 configure -textvariable boco_current_boco -anchor w
    set boco_scroller_win [form_scroller $d.bcwin 350 {1 0 -cols 2} 1]
    form_checkbutton $d.copyto "Copy to next family" boco_copyto {2 1}
    form_button $d.addboco "Add boco" boco_edit_add {2 0}
    form_listbox $d.bocolist "Available families" "" \
	    "boco_edit_setboco $d.bocolist" "" 8x12 {0 3 -rows 2}
    form_checkbutton $d.only "Only unassigned" boco_only_none {2 3}
    $d.only configure -command "boco_fill_listbox $d.bocolist"
    form_finish bot
    boco_fill_listbox $d.bocolist
    
    tkwait variable form_done_flag(.bocos)
    
    boco_edit_setdata
    if !$form_done_flag(.bocos) {
	foreach b [array names boco_data] { set boco_data($b) $saved_data($b) }
	return 0
    } else {
	return 1
    }
}

proc boco_fill_listbox {w} {
    global boco_only_none boco_data

    set bnames [lsort [array names boco_data]]
    if $boco_only_none {
	set bn ""
	foreach b $bnames {
	    if {$boco_data($b) == ""} {
		lappend bn $b
	    }
	}
	form_listbox_set $w $bn
    } else {
	form_listbox_set $w $bnames
    }
}

proc boco_edit_setboco {listb fname} {
    global boco_scroller_win boco_current_boco boco_deleted
    global boco_copyto boco_temp_data boco_data boco_only_none
    
    boco_edit_setdata
    
    set bsw $boco_scroller_win
    
    foreach w [winfo children $bsw] {
	destroy $w
    }
    
    if {$boco_copyto && ($boco_current_boco != "")} {
	set boco_data($fname) $boco_data($boco_current_boco)
    }
    set boco_current_boco $fname
    catch {unset boco_temp_data}
    catch {unset boco_deleted}
    set nd [llength $boco_data($fname)]
    
    for {set i 0} {$i < $nd} {incr i} {
	set data [lindex $boco_data($boco_current_boco) $i]
	
	set boco_temp_data($i,s0) [string range [lindex $data 0] 0 4]
	set boco_temp_data($i,s1) [string range [lindex $data 0] 5 9]
	#set boco_temp_data($i,i0) [lindex $data 1]
	for {set j 0} {$j < 9} {incr j} {
	    set d [lindex $data [expr {$j + 1}]]
	    if {$d == ""} { set d 0.0 }
	    set boco_temp_data($i,f$j) $d
	}
	set boco_temp_data($i,f0) [expr int($boco_temp_data($i,f0))]
	boco_edit_putone $i
    }
    
    if $boco_only_none {
	boco_fill_listbox $listb
	form_listbox_select $listb $fname
    }
}

proc boco_edit_putone {i} {
    global boco_scroller_win boco_current_boco boco_data
    global boco_temp_data
    
    set bsw $boco_scroller_win
    
    form_frame $bsw.b$i ridge 2 "$i 0"
    form_label $bsw.b$i.ls0 "string 1" {0 0}
    form_label $bsw.b$i.ls1 "string 2" {0 2}
    #form_label $bsw.b$i.li0 I0 {0 4}
    form_entry $bsw.b$i.s0 boco_temp_data($i,s0) string {0 1}
    form_entry $bsw.b$i.s1 boco_temp_data($i,s1) string {0 3}
    #form_entry $bsw.b$i.i0 boco_temp_data($i,i0) int {0 5}
    form_button $bsw.b$i.del Delete "boco_edit_delete $bsw.b$i $i" {0 5}
    $bsw.b$i.s0 configure -width 5
    $bsw.b$i.s1 configure -width 5
    for {set j 0} {[info exists boco_temp_data($i,f$j)]} {incr j} {
	set r [expr {1 + $j / 3}]
	set c [expr {($j % 3) * 2}]
	if !$j {
	    form_label $bsw.b$i.lf$j "integer" "$r $c"
	} else {
	    form_label $bsw.b$i.lf$j "real $j" "$r $c"
	}
	incr c
	form_entry $bsw.b$i.f$j boco_temp_data($i,f$j) float "$r $c"
    }
}

proc boco_edit_setdata {} {
    global boco_current_boco boco_data boco_temp_data boco_deleted
    
    if {$boco_current_boco == ""} return
    
    set boco_data($boco_current_boco) ""
    for {set i 0} {[info exists boco_temp_data($i,s0)]} {incr i} {
	if [info exists boco_deleted($i)] continue
	set data ""
	lappend data [format %-5.5s%-5.5s $boco_temp_data($i,s0) \
		$boco_temp_data($i,s1)]
	#lappend data $boco_temp_data($i,i0)
	for {set j 0} {[info exists boco_temp_data($i,f$j)]} {incr j} {
	    set d $boco_temp_data($i,f$j)
	    if ![scan $d %g d] { set d 0.0 }
	    lappend data $d
	}
	lappend boco_data($boco_current_boco) $data
    }
}

proc boco_edit_add {} {
    global boco_temp_data boco_current_boco
    
    if {$boco_current_boco == ""} {
	mess "No family selected.\n"
	return
    }
    for {set i 0} {[info exists boco_temp_data($i,s0)]} {incr i} {}
    set boco_temp_data($i,s0) ""
    set boco_temp_data($i,s1) ""
    #set boco_temp_data($i,i0) 0
    for {set j 0} {$j < 10} {incr j} { set boco_temp_data($i,f$j) 0.0 }
    boco_edit_putone $i
}

proc boco_edit_delete {win n} {
    global boco_deleted
    
    blt_table forget $win
    set boco_deleted($n) 1
}

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

proc boco_edit_full {{prefix ""}} {
    global boco_dirty boco_editf_list boco_editf_type boco_editf_scwin
    global boco_editf_prefix boco_dirty boco_data boco_editf_tmp
    global boco_numbc boco_nument
    
    set d [form_init .bocof "Boundary condition data" bocos "done cancel"]
    
    if {$d != ""} {
	set boco_editf_type domain
	trace variable boco_editf_type w "boco_editf_seltype; list"
	
	form_frame $d.type flat 0 {0 0}
	form_radiobutton $d.type.d Domains  boco_editf_type domain {0 0}
	form_radiobutton $d.type.s Subfaces boco_editf_type subface {1 0}
	form_radiobutton $d.type.e Edges    boco_editf_type edge {2 0}
	form_radiobutton $d.type.v Vertices boco_editf_type vertex {3 0}
	form_radiobutton $d.type.p Patches  boco_editf_type patch {4 0}
	form_radiobutton $d.type.f Families boco_editf_type family {5 0}
	
	form_listbox $d.ents "" "" boco_editf_selone "" 6x7 {0 1}
	set boco_editf_list $d.ents
	
	form_rowheight $d 1 4
	
	form_frame $d.fr flat 0 {2 0 -cols 2}
	form_label $d.fr.numl "Number of B.C.s" {0 0}
	form_entry $d.fr.num boco_editf_curcount readonly {0 1}
	$d.fr.num configure -width 4
	form_button $d.fr.add Add boco_editf_add {0 2}
	form_button $d.fr.delete Delete boco_editf_delete {0 3}
	form_label $d.lab \
	    " Names can be up to 10 characters; values can be up\n\
             to 10 floating point numbers, separated by spaces.\n\
             Use the middle button to scroll the value entries." {3 0 -cols 3}
	$d.lab configure -justify left
	set boco_editf_scwin [form_scroller $d.bcwin 150 {4 0 -cols 3} 1]
	$boco_editf_scwin configure -relief sunken -borderwidth 2
	form_frame $d.ops flat 0 {3 0 -cols -}
	form_button $d.ops.rdups "Remove duplicates" boco_edit_rdups {0 0}
	form_button $d.ops.rpat "Remove pattern" boco_edit_rpat {1 0}
	form_finish -0-0
    }
    
    array set save [array get boco_data]
    
    set boco_editf_prefix $prefix
    
    trace variable boco_editf_tmp w error
    #catch {unset boco_editf_tmp}
    boco_editf_seltype
    
    if ![form_wait .bocof] {
	unset boco_data
	array set boco_data [array get save]
    } else {
	boco_editf_get
	
	# Recompute boco_numbc
	set p $prefix; if {$p != ""} { set p $p, }
	foreach t {domain subface edge vertex patch family} {
	    set boco_numbc($p$t) 0
	    for {set i 1} {$i <= $boco_nument($p$t)} {incr i} {
		incr boco_numbc($p$t) [llength $boco_data($p$t,$i)]
	    }
	}
	
	set boco_dirty($prefix) 1
    }
    if ![catch {blt_table_slaves $boco_editf_scwin} slaves] {
	foreach s $slaves { destroy $s }
    }
}

proc boco_edit_rdups {} {
    global boco_data
    
    set num 0
    foreach d [array names boco_data] {
	set stuff $boco_data($d)
	catch {unset found}
	set nstuff ""
	foreach s $stuff {
	    if ![info exists found($s)] {
		lappend nstuff $s
		set found($s) 1
	    } else {
		incr num
	    }
	}
	set boco_data($d) $nstuff
    }
    boco_editf_populate
    mess "$num duplicates deleted from bc's\n"
}

proc boco_edit_rpat {} {
    global boco_data
    
    set p [question_box .del_pat "Pattern" "Enter the pattern to delete." ""]
    if {$p == ""} return
    set num 0
    foreach d [array names boco_data] {
	set stuff $boco_data($d)
	set nstuff ""
	foreach s $stuff {
	    if [string match $p $s] { incr num; continue }
	    lappend nstuff $s
	}
	set boco_data($d) $nstuff
    }
    boco_editf_populate
    mess "$num duplicates deleted from bc's\n"
}

proc boco_editf_seltype {} {
    global boco_nument boco_editf_list boco_editf_type boco_editf_prefix
    global boco_data
    
    set p $boco_editf_prefix; if {$p != ""} { set p $p, }
    set t $boco_editf_type
    set stuff ""
    for {set i 1} {$i <= $boco_nument($p$t)} {incr i} {
	lappend stuff "$t.$i ([llength $boco_data($p$t,$i)])"
    }
    form_listbox_set $boco_editf_list $stuff
    form_listbox_select_index $boco_editf_list 0
    boco_editf_selone [lindex $stuff 0]
}

proc boco_editf_selone {stuff} {
    global boco_nument boco_editf_list boco_editf_type boco_editf_prefix
    global boco_data boco_editf_num
    
    if {$stuff == ""} return
    if ![scan $stuff $boco_editf_type.%d num] { user_error "can't parse $stuff" }
    boco_editf_get
    set boco_editf_num $num
    boco_editf_populate
}

proc boco_editf_add {} {
    global boco_editf_curcount
    
    incr boco_editf_curcount
    boco_editf_putrow $boco_editf_curcount "" ""
}

proc boco_editf_delete {} {
    global boco_editf_curcount boco_editf_tmp boco_editf_scwin
    
    set ff [focus]
    if {[scan $ff $boco_editf_scwin.b%d num] != 1} {
	tk_dialog .bocof_err "Delete boco" \
	    " To delete a boundary condition, click in one of the entry\n\
	      windows for the desired one and then select Delete." "" 0 Okay
	return
    }
    for {set i $num} {$i < $boco_editf_curcount} {incr i} {
	set boco_editf_tmp($i,string) $boco_editf_tmp([expr $i + 1],string)
	set boco_editf_tmp($i,float) $boco_editf_tmp([expr $i + 1],float)
    }
    boco_editf_delrow $boco_editf_curcount
    incr boco_editf_curcount -1
}

proc boco_editf_populate {} {
    global boco_editf_scwin boco_nument boco_editf_type boco_editf_prefix
    global boco_data boco_editf_num
    global boco_editf_tmp boco_editf_curtype boco_editf_curnum
    global boco_editf_curcount
    
    set p $boco_editf_prefix; if {$p != ""} { set p $p, }
    set data $boco_data($p$boco_editf_type,$boco_editf_num)
    
    if ![catch {blt_table_slaves $boco_editf_scwin} slaves] {
	foreach s $slaves { destroy $s }
    }
    catch {unset boco_editf_tmp}
    set c 1
    foreach d $data {
	boco_editf_putrow $c [lindex $d 0] [lrange $d 1 end]
	incr c
    }
    set boco_editf_curcount [expr $c - 1]
    set boco_editf_curtype $boco_editf_type
    set boco_editf_curnum $boco_editf_num
}

proc boco_editf_putrow {c string floats} {
    global boco_editf_scwin boco_editf_tmp
    
    set boco_editf_tmp($c,string) $string
    set boco_editf_tmp($c,float) $floats
    set w $boco_editf_scwin.b$c
    label ${w}_l1 -text "Name $c:"
    label ${w}_l2 -text "Values $c:"
    entry ${w}_e1 -textvariable boco_editf_tmp($c,string) -width 12 \
	-background white
    entry ${w}_e2 -textvariable boco_editf_tmp($c,float) -width 30 \
	-background white
    blt_table $boco_editf_scwin ${w}_l1 [expr {$c * 2}],0
    blt_table $boco_editf_scwin ${w}_l2 [expr {$c * 2 + 1}],0
    blt_table $boco_editf_scwin ${w}_e1 [expr {$c * 2}],1
    blt_table $boco_editf_scwin ${w}_e2 [expr {$c * 2 + 1}],1 -columnspan 2
}

proc boco_editf_delrow {c} {
    global boco_editf_scwin boco_editf_tmp
    
    set w $boco_editf_scwin.b$c
    foreach s {l1 l2 e1 e2} { destroy ${w}_$s }
    
    unset boco_editf_tmp($c,string)
    unset boco_editf_tmp($c,float)
}

proc boco_editf_get {} {
    global boco_editf_prefix
    global boco_data
    global boco_editf_tmp boco_editf_curtype boco_editf_curnum
    global boco_editf_curcount
    
    if [info exists boco_editf_tmp] {
	set p $boco_editf_prefix; if {$p != ""} { set p $p, }
	set stuff ""
	for {set c 1} {$c <= $boco_editf_curcount} {incr c} {
	    lappend stuff [concat [list $boco_editf_tmp($c,string)] \
			       $boco_editf_tmp($c,float)]
	}
	set boco_data($p$boco_editf_curtype,$boco_editf_curnum) $stuff
    }
}
