#!./btwish
#/bin/sh
# the next line restarts using wish \
#    exec ./btwish "$0" ${1+"$@"}

# ----------------------------------------------------------------------
#  usage: 
#      The main procedure is:
#                 st_edit_curves [options]
#
#      The options are optional. When it is called with no arguments,
#      it puts up a single empty table.
#
#  options:
#      -curves  data
#          The -curves flag indicates that there is data to be read
#          in and modified.  optional. If there is data, then it 
#          should be of the form of a list of lists. Each sublist
#          contains the data for one of the stress-strain curves.
#          The first element in the list is the temperature. The 
#          rest of the elements are the x-y data associated with 
#          that temperature. It should look something like this:
#
#           { { temp0 x11 y11 x12 y12 ... x1p y1p }
#             { temp1 x21 y21 x22 y22 ... x2q y2q }
#             { ... }
#             { tempn xn1 yn1 xn2 yn2 ... xnr ynr } }
#
#       -single
#          When this flag is present, then the user only edits one 
#          curve at a time. The interface does not allow the user
#          to delete this curve or to add new curves. When the
#          -curves flag and data are present, it only reads the first
#          list of data  in the list.
#    
#       -debug
#          Prints out a bunch of stuff.
#
#  output:
#      When this returns, it sends the data to stdout. 
#      If the user has changed the data, then the output will be
#      a list of the same form as the input. There was no change,
#      or the user has cancelled the application, then the
#      procedure returns an empty string.
#
# ----------------------------------------------------------------------
#  USER DOCUMENTATION
#  (just a start)
#
#  Undelete
#      Undelete will bring back tables that were deleted. They will be 
#      placed in the window to the right of the other tables regardless
#      of their position when they were deleted.
#  
#  Moving insertion point inside a cell:
#
#      When editing a cell, Ctrl-> and CTRL-<  (that is, control 
#      key with left or right arrow) will move the insertion point 
#      left or right.
#
#  Loading data from a file:
#      The file should contain as the first element, the temperature
#      for the curve. The rest of the elements are the x and y values
#      (alternating x and y)  with the values separated by white space.
#      It should look something like this:
#            temp
#            x0   y0
#            x1   y1
#            x2   y2
#      Note: the values do not have to be on separate lines. Line
#      breaks are unimportant. Only the white space matters.
#
#   Saving data to a file:
#      Will write out the temperature first, and then the points
#      in the curve.  It will look like the format described
#      in 'Loading data from a file'
#
# ----------------------------------------------------------------------
#  RANDOM DEVELOPER STUFF
#
#  Data for each of the tables is kept in arrays that are indexed
#  by row and column.  The data in the arrays is always contiguous. 
#  However, the end of each array may contain no data.
#
#
# TODO  Do something more intelligent when all of the rows are deleted
#          from a table
# TODO  Put in checks during load from file to check the kind of
#          data that is coming in.
# TODO  Put in checks during save and save to file to see if all of the 
#          data is present
# TODO  In insert_row: make spiffier dialog box that differentiates the
#         'Cancel' button from the 'Above' and 'Below' buttons
# TODO  add_table and undelete should reset window view so
#          new table is visible. But how to do this!
# TODO  Is there a way to delete unused rows when loading from 
#       a file and the new data is shorter than the data it
#       replaces.
# ----------------------------------------------------------------------

###
###   Global variables
###

proc st_init {iargs} {
    global st_cfg st_user st_tables st_wins st_graph
    
#    if [info exists st_cfg] return
    
    #  st_cfg - an array that holds application defaults and state information
    #           The descriptions of the elements are given below
    #
    #      debug -
    #      single -
    #      num_format_str -
    #      non -
    #      button.relief -
    set st_cfg(debug) 0
    set st_cfg(single) 0
    set st_cfg(num_format_str_0) "%e"
    set st_cfg(num_format_str_1) "%e"
    set st_cfg(non) 0
    set st_cfg(button.relief)  groove
    set st_cfg(x_label) "x_label"
    set st_cfg(y_label) "y_label"
    set st_cfg(graph_title) "graph_title"
    
    foreach {n v} $iargs {
	set st_cfg($n) $v
    }

    #  st_user - This array keeps track of what the user interactions.
    #
    #      data -  The changed data when user requests save data.
    #      dirty - User data diry flag. Set when user edits table cells.
    set st_user(data) {}        
    set st_user(dirty) 0        
    
    #  st_tables -  This array keeps track of the data associated with the
    #               table widgets.
    #
    #      active - An ordered list of the different tables arrays
    #               Each of the table arrays has the form t_<temperature>
    #      deleted -
    #      browse - The last table to be browsed
    #      default_table_size - number of rows in a new table
    set st_tables(active) {};  
    set st_tables(deleted) {}; 
    set st_tables(browse) "" 
    set st_tables(default_table_size) 10 
    
    #  st_wins -   Array holding paths and state associated with window
    #                 and widgets
    #
    #      tparent - Path of parent of the tables frames
    #      scrollf - Path of scroll frame holding tables
    #      save - The path to the save button.
    #      undel - Path to undelete  button.
    set st_wins(tparent) "" 
    set st_wins(scrollf) "" 
    set st_wins(save) ""
    set st_wins(undel) ""
    set st_wins(top) .stable
    
    # st_graph - Array to hold defaults and state associated with the
    #            graph widgets.
    #
    #     xscale - Can have the value 'linear' or 'log'. Indicates whether
    #              the x axis is a linear or a log scale.
    #     yscale - Can have the value 'linear' or 'log' Indicates whether
    #              the y axis is a linear or a log scale.
    #     zoombox_coords - A list {x1 y1 x2 y2} that stores the corners of
    #              a rectangle that defines a zoom region of the graph.
    #     msg - Messages for the user.
    #     symbols - Possible symbols to use for the lines
    #              square circle diamond plus cross splus scross triangle 
    #     colors -
    #     orange -
    #     red -
    #     violet
    #     purple
    #     blue
    #     lightblue
    #     green
    #     yellow
    set st_graph(graph) ""
    set st_graph(xscale) linear
    set st_graph(yscale) linear
    set st_graph(zoombox_coords) {}
    set st_graph(msg) ""
    set st_graph(symbols_unused) {}
    set st_graph(symbols) {circle plus triangle square cross \
			       diamond splus scross}
    set st_graph(colors_unused) {}
    set st_graph(colors) {orange blue green red purple \
			      lightblue violet yellow }
    set st_graph(orange)    \#FF9933
    set st_graph(red)       \#FF0000
    set st_graph(violet)    \#CC66CC
    set st_graph(purple)    \#9933CC
    set st_graph(blue)      \#0066CC
    set st_graph(lightblue) \#33FFFF
    set st_graph(green)     \#00CC33
    set st_graph(yellow)    \#FFFF33
}

#**********************************************************************
#   Debugging procs      
#                        
#**********************************************************************
#----------------------------------------------------------------------
# st_putdebug
#
#----------------------------------------------------------------------
proc st_putdebug {text} {
    global st_cfg
    if $st_cfg(debug) {    
	set n [info level]
	puts -nonewline stderr "."
	puts  -nonewline stderr [string repeat ".   " $n]
	puts stderr $text 
    }
}

#----------------------------------------------------------------------
# st_parraydebug
#----------------------------------------------------------------------
proc st_parraydebug {arrayname} {
    global st_cfg
    global $arrayname
    if $st_cfg(debug) {parray $arrayname}
}

#**********************************************************************
#
#    Graphing procs     
#                       
#**********************************************************************

#----------------------------------------------------------------------
#  st_drawbox
#
#  Params:
# 
# Draw a box on the graph when user clicks on graph
#----------------------------------------------------------------------
proc st_drawbox {g x1 y1 x2 y2} {

    set coords {
	$x1 $y1 $x2 $y1 $x2 $y2 $x1 $y2 $x1 $y1
    }
    if [$g marker exists "outline"] {
	$g marker configure "outline" -coords $coords
    } else {
	$g marker create line -coords $coords -name "outline" \
	    -dashes { 4 2 } -outline grey
    }
    $g marker before "outline"
}

#----------------------------------------------------------------------
#  st_check_limits 
#  
#  params:
#    g - Path of graph widget.
#    x y  - A point in graph axis coordinates.
#
#  Returns:
#      Returns 1 if the point is within limits, and 0 otherwise.
#
#  Checks to see if this point is inside the graph.
#----------------------------------------------------------------------

proc st_check_limits {g x y} {
    global st_graph
    
    set tol .05
    
    scan [$g xaxis limits] "%s %s" xmin xmax
    scan [$g yaxis limits] "%s %s" ymin ymax

    set xfudge [expr [expr $xmax - $xmin] * $tol]
    set yfudge [expr [expr $ymax - $ymin] * $tol]
    set xmax [expr $xmax + $xfudge]
    set xmin [expr $xmin - $xfudge]
    set ymax [expr $ymax + $yfudge]
    set ymin [expr $ymin - $yfudge]
    
    set legal [expr $x <= $xmax  &&  $x >= $xmin \
		   && $y <= $ymax && $y >= $ymin ]
    return $legal
}

#----------------------------------------------------------------------
# st_save_coords
#
# Params:
#    g - Path of the graph widget.
#    sx - Screen x coordinate
#    xy - Screen y coordinate
#
# Returns:
#     Returns the list of all points that have been saved.
#
# Saves the given screen coordinatess as graph coordinates.  
# It only saves rectangles, so after it has two points, it 
# clears itself and starts again. Also, if the user clicks outside of
# the actual graph, then all points are cleared
#
#----------------------------------------------------------------------
proc st_save_coords {g sx sy} {
    global st_graph
    
    st_putdebug ">> st_save_coords"
    
    set coords [$g invtransform $sx $sy]
    set x [lindex $coords 0]
    set y [lindex $coords 1]
    
    set legal [st_check_limits $g $x $y]

    if $legal {
	# Check how many points have been saved
	if {[llength $st_graph(zoombox_coords)] > 3} {

	    # If there are already two points that have been 
	    # saved,then clear these points
	    set st_graph(zoombox_coords) {}
	}
	
	lappend st_graph(zoombox_coords) $x $y

    } else {
	
	# if not inside the graph, then clear all points 
	set st_graph(zoombox_coords) {}

    }
    st_putdebug "<< st_save_coords returns $st_graph(zoombox_coords)"

    return $st_graph(zoombox_coords)
}

#----------------------------------------------------------------------
#  st_zoom_graph
#
#  Params:
#      g - Path of graph widget.
#
# Zooms to the new limits in st_graph(zoombox_coordinates)
#----------------------------------------------------------------------

proc st_zoom_graph {g} {
    global st_graph

    set cc $st_graph(zoombox_coords)
    set x1 [lindex $cc 0]
    set y1 [lindex $cc 1]
    set x2 [lindex $cc 2]
    set y2 [lindex $cc 3]

    # The first and last corners of the zoom region don't have to be
    # selected in ascending order.  So consider their relative positions
    # when setting min and max axis limits.

    if { $x1 > $x2 } { 
	$g xaxis configure -min $x2 -max $x1 
    } elseif { $x1 < $x2 } {
	$g xaxis configure -min $x1 -max $x2
    } 
    if { $y1 > $y2 } { 
	$g yaxis configure -min $y2 -max $y1
    } elseif { $y1 < $y2 } {
	$g yaxis configure -min $y1 -max $y2
    } 

    # Delete the outline
    $g marker delete "outline"
    
    # Call "update" explicitly here after the graph is made busy.
    # This prevents the user from inadvertantly selecting another zoom
    # region when the graph is recalculating and redrawing itself.
    blt::busy hold $g
    update
    blt::busy release $g
}

#----------------------------------------------------------------------
#  st_motion_draws 
#
#  params:
#      g  - Path of graph widget.
#
#      motion - One of the two event patterns Button1-Motion or Motion.
#
#      draw - Boolean indicates if the mouse motion should be bound to 
#          this function. When 1, then a box is drawn. When 0, 
#          mouse motion is not bound.
#
#  When one point is defined for the "zoombox_coords" then this draws
#  a box from that point to the current mouse pointer position.
#----------------------------------------------------------------------
proc st_motion_draws {g motion draw} {
    global st_graph

    st_putdebug ">> st_motion_draws $g $motion $draw"

    if $draw {
	
	# Bind mouse motion to drag out the zoom rectangle
	bind $g <$motion> {

	    # These are the coordinates of the first point
	    set x0 [lindex $st_graph(zoombox_coords) 0]
	    set y0 [lindex $st_graph(zoombox_coords) 1]
	    
	    # The current mouse coordinates
	    set coords [%W invtransform %x %y]
	    set x1 [lindex $coords 0]
	    set y1 [lindex $coords 1]

	    # Only draw the box if the pointer is actually inside
	    # the graph.
	    
	    if [st_check_limits %W $x1 $y1] {
		st_drawbox %W $x0 $y0 $x1 $y1
	    } else {
		%W marker delete "outline"
	    }

	}
    } else {
	# Unbind mouse motion
	bind $g <$motion> {}
    }
    st_putdebug "<< st_motion_draws"
}


#----------------------------------------------------------------------
#  st_clickdrag_zooms
#
#  Params:
#      g - Path of graph widget.
#
# Binds Button1 to drag out the zoom rectangle. Button down defines
# The first point of the rectangle diagonal, and button up marks the
# second point of the rectangle diagonal.
#----------------------------------------------------------------------
proc st_clickdrag_zooms {g} {
    st_putdebug ">> st_clickdrag_zooms $g"

    bind $g <ButtonPress-1> {

	set coords [st_save_coords %W %x %y]

	# Start drawing the zoom rectangle only if there
	# if the first point actually got saved
	if {[llength $coords] == 2} {
	    st_motion_draws %W Button1-Motion 1
	    st_graph_message "Drag zoom rectangle"
	}	
    }
    
    bind $g <ButtonRelease-1> {
	st_graph_message ""
	
	# Check to see if there is one point saved already
	if {[llength $coords] == 2} {
	    
	    # Save this point as the second point for the
	    # rectangle
	    set coords [st_save_coords %W %x %y]

	    # Now check to see that there are 2 points saved 
	    if {[llength $coords] == 4} {
		st_zoom_graph %W
		st_graph_message "Click right button to reset view."
	    } else {
		st_graph_message ""
	    }
	}
	
	# And turn off drawing rectangles
	st_motion_draws %W Button1-Motion 0
    }
    st_putdebug "<< st_clickdrag_zooms"
}

#----------------------------------------------------------------------
#  st_2_clicks_zooms
#
#  params:
#     g - Path of graph widget.
#
#  Configures the graph interaction so that the zoom rectangle
#  is defined by clicking in the graph twice with mouse 1.
#----------------------------------------------------------------------
proc st_2_clicks_zooms {g} {

    bind $g <ButtonPress-1> {

	set coords [st_save_coords %W %x %y]
	if {[llength $coords] == 0} {
	    
	    # If there are no points saved, then stop drawing the
	    # outline of the zoom rectangle
	    st_motion_draws %W Motion 0
	    %W marker delete "outline"
	    
	} elseif {[llength $coords] ==  2} {
	    
	    # If there is exactly one point saved, then start drawing 
	    # the zoom rectangle
	    st_motion_draws %W Motion 1
	    
	} else {
	    
	    # Assume that there are two points saved, then zoom
	    # to the rectangle defined by these two points
	    st_zoom_graph %W

	    # And turn off drawing rectangles
	    st_motion_draws %W Motion 0
	}
    }
}

#----------------------------------------------------------------------
#  st_button3_resents
#
#  params:
#     g  -  Path of the graph window.
#     fr -  main frame
#
#  Binds the mouse 3 button to reset view on the graph.
#----------------------------------------------------------------------
proc st_button3_resets {g fr} {
    global st_graph

    bind $g <ButtonPress-3> {
	# Reset the view
	if [string equal $st_graph(xscale) linear] {
	    %W axis configure x -min {} -max {}
	} else {
	    %W axis configure x -min {} -max {}
	}
	if [string equal $st_graph(yscale) linear] {
	    %W axis configure y -min {} -max {}
	} else {
	    %W axis configure y -min {} -max {}
	}
	
	st_graph_message ""

	# Erase the last rectangle that was drawn
	%W marker delete "outline"
    }
}

#----------------------------------------------------------------------
# st_next_color
#
# Return an appropriate color
#----------------------------------------------------------------------
proc st_next_color {} {
    global st_graph
    set col white
    set colors $st_graph(colors_unused)
    if [llength $colors] {
	set col [lindex $colors 0]
	set st_graph(colors_unused) [lrange $colors 1 end]
    }
    return $col
}

#----------------------------------------------------------------------
# st_next_symbol
#
# Return the next available symbol
#----------------------------------------------------------------------
proc st_next_symbol {} {
    global st_graph
    set s [lindex $st_graph(symbols_unused) 0]
    set st_graph(symbols_unused) \
	[concat [lrange $st_graph(symbols_unused) 1 end] $s]
    return $s
}

#----------------------------------------------------------------------
# st_add_element
#
# Params: 
# g - Path of graph widget
# t - name of table
#----------------------------------------------------------------------
proc st_add_graph_element {g t} {
    upvar \#0 $t table
    st_putdebug ">> add_graph_element g=$g t=$t" 
    # extract the data 
    upvar \#0 $table(data) data
    set x {}
    set y {}
    for {set r 1} {$r < $table(rows)} {incr r} {
	lappend x $data($r,1)
	lappend y $data($r,2)
    }
    
    # make the element name the temperature
    # no ...set elname $table(temp)
    set elname elem[llength [$g element names]]
    
    set lcolor [st_next_color]
    $g element create $elname -xdata $x -ydata $y
    $g element configure $elname -color $lcolor -pixels 5
    $g element configure $elname -symbol [st_next_symbol]

    st_putdebug "<< st_add_graph_element"
}

#----------------------------------------------------------------------
#  st_graph_message
#
#  Prints the string in parameter str to an appropriate window for 
#  user's information
#----------------------------------------------------------------------
proc st_graph_message {str} {
    global st_graph
    set st_graph(msg) $str
}

#----------------------------------------------------------------------
#  st_scale_axis
#
#  Params:
#      g - Path of the graph widget that will be scaled.
#      b - Path to button that invoked this proc. 
#      axis - Which axis to change. either x or y.
#----------------------------------------------------------------------
proc st_scale_axis {g b axis} {
    global st_graph
    set ok 1
    st_graph_message ""
    if [string equal $st_graph(${axis}scale) linear] {

	# Linear scale
	set val "no"
	# rename the axis
	$g axis configure $axis -title "$axis"

    } else {
	
	# Log scale
	set val "yes"
	scan [$g axis limits $axis] "%s %s" amin amax

	
	# Check to see if there are coordinates with negative values
	if {$amin < 0} {
	    if {$amax < 0} {
		# When all the  values are negative, then
		# do not scale axes. Let user know that we cannot 
		# change axes.
		st_graph_message "Cannot plot negative values on log scale."
		$b flash
		$b toggle
		set ok 0
	    } else {
		# When some but not all of the data is negative, clip
		# the data that is negative 
		st_graph_message "Warning: Adjusting view to clip data\
                         with negative $axis values."
		$g axis configure $axis -min 1.0


	    }
	}
	# rename the axis
	$g axis configure $axis -title "log($axis)"
    }
    if $ok {
	# scale the axis
	$g axis configure $axis -logscale $val

    }
}

#----------------------------------------------------------------------
#  st_graph_curves
#
#  Graph all of the curves. Puts up a new dialog box displaying
#  the graph.
#----------------------------------------------------------------------
proc st_graph_curves {} {
    global st_tables st_graph st_cfg
    
    # reset the colors and the symbols
    set st_graph(colors_unused) $st_graph(colors)
    set st_graph(symbols_unused) $st_graph(symbols)

    # Make a new top level window
    # Make the diaolog window
    # -modal local
    set dlg [toplevel .graphdlg]
    wm geometry $dlg +300+300
    wm title $dlg "Curves"
    
    # Make the graph widget and its frame
    set fr [frame $dlg.gfr -relief sunken -bd 1]
    pack $fr -side top -fill x -padx 2 
    set gwin [blt::graph $fr.g -plotbackground black]
    set st_graph(graph) $gwin
    #$gwin configure -title "Stress strain curves"
    $gwin configure -title $st_cfg(graph_title)

    # Configure the axes
    #$gwin axis configure y -title "y"
    #$gwin axis configure x -title "x"
    $gwin axis configure y -title $st_cfg(y_label)
    $gwin axis configure x -title $st_cfg(x_label)
    $gwin axis configure x -limitsformat [list "%6.4g"]
    $gwin axis configure y -limitsformat [list "%6.4g"]
    $gwin axis configure x y -subdivisions 4

    # Configure the legend
    $gwin legend configure -relief groove
    $gwin legend configure -background black
    $gwin legend configure -foreground white

    # Create the elements for this graph
    foreach t $st_tables(active) {
	st_add_graph_element $gwin $t
    }
    
    pack $gwin -side top -fill both -expand yes -pady 8 -padx 8
    
    # A frame and text box for user messages
    set fr [frame $dlg.msgfr -relief sunken -bd 1]
    set msgbox [label $fr.msg -anchor w -padx 4 \
		    -textvariable st_graph(msg)]
    pack $fr -side top -fill x -padx 2 -pady 4 -anchor n
    pack $msgbox -side top -fill x -anchor n
    
    # Set up the button bindings for the zooming interaction.
    # Button-1 click-drag-release defins the zoom rectangle.
    # Button 3 reset the graph to the original view
    st_clickdrag_zooms $gwin
    st_button3_resets $gwin $fr
    
    # The controls for the graph

    # Frame to hold the buttons
    set fr [frame $dlg.fr -relief raised -bd 1]    
    
    # The 'done' button
    set okwin [button $fr.done -text "Dismiss" \
		   -relief groove \
		   -command "destroy $dlg"]

    pack $okwin -side left -expand yes -pady 4 -padx 20

    # Controls changing scale of graph axes
    foreach axis {x y} {
	
	# set axis to be linear to start out
	set st_graph(${axis}scale)  linear
	
	set logbutton \
	    [checkbutton $fr.${axis}log -text "$axis log scale" \
		 -anchor w \
		 -variable st_graph(${axis}scale) \
		 -offvalue linear -onvalue log \
		 -command [list st_scale_axis $gwin $fr.${axis}log $axis]]
       
	pack $logbutton -side left  -anchor w -ipadx 8
    }

    pack $fr -side top -fill both -padx 2 
}


#**********************************************************************
#
#    Tables procs     
#                       
#**********************************************************************

#----------------------------------------------------------------------
# st_get_non
#
# Params: none
#
# Get a unique number to use in creating unique id's
#----------------------------------------------------------------------
proc st_get_non {} {
    global st_cfg
    set val $st_cfg(non)
    incr st_cfg(non)
    return $val
}

#----------------------------------------------------------------------
# st_push_delete_stack <tname>
#
# Pushes this table onto the delete stack
#----------------------------------------------------------------------
proc st_push_delete_stack {tname} {
    global st_tables
    set st_tables(deleted) [concat [list $tname] $st_tables(deleted)]
}

#----------------------------------------------------------------------
# st_pop_delete_stack
#
# Pops tables from the delete stack
#----------------------------------------------------------------------
proc st_pop_delete_stack {} {
    global st_tables
    set val [lindex $st_tables(deleted) 0]
    set st_tables(deleted) [lrange $st_tables(deleted) 1 end]
    return $val
}

#----------------------------------------------------------------------    
# st_new_table_info <list>
#
# Creates a new array that holds information about a table.
#
# param: 
#     newdata - A list of numbers where the first number is 
#         a temperature, and the rest are the x and y data. 
#         There should be be an even number of these
# returns:
#       The name of the new table.
#
# side effects:
#       Adds the new table name to the st_tables(active) global array.
#       Creates a new array variable.
#----------------------------------------------------------------------
proc st_new_table_info {newdata} {
    
    global st_wins st_cfg st_tables

    st_putdebug ">> st_new_table_info temp:[lindex $newdata 0]"

    # get the temperature
    set temp [lindex $newdata 0]
    
    # name the table after the temperature
    # and make it a global variable
    set n [st_get_non]
    set tarray_name st_${n}_$temp

    st_putdebug "    table array name: $tarray_name"

    upvar \#0 $tarray_name tarray
    
    set tarray(name) $tarray_name ;# name of this array
    
    # keep track of the temperature
    set tarray(temp) $temp
    
    # name the table variable and make it a global variable
    set datavar_name ${tarray_name}data
    
    st_putdebug "    data variable name $datavar_name"
    
    upvar \#0 $datavar_name data    

    # put the data into the table variable
    set r 1
    foreach {x y} [lrange $newdata 1 end] {
	set data($r,0) $r
	if {$st_cfg(num_format_str_0) == "%d"} {
	   set x [expr int($x)]
	} else {
	   set x [format $st_cfg(num_format_str_0) $x]
	}
	set data($r,1) $x
	if {$st_cfg(num_format_str_1) == "%d"} {
	   set y [expr int($y)]
	} else {
	   set y [format $st_cfg(num_format_str_1) $y]
	}
	set data($r,2) $y
	incr r
    }
    
    # put in the title row
    set data(0,0) \#
    #set data(0,1) x
    #set data(0,2) y
    set data(0,1) $st_cfg(x_label)
    set data(0,2) $st_cfg(y_label)

    # Name some paths associated with this table
    # The table widget is inside of a  frame widget which is
    # inside of a frame, st_wins(tparent) that holds all of the tables.
    set frame $st_wins(tparent).$tarray_name
    set tarray(frame) $frame  ;# frame that holds the table with its widgets
    set tarray(table) $frame.t ;# table widget path
    set tarray(scrollbar) $frame.sy ;# scrollbar widget path 
    set tarray(temp_entry) $frame.te ;# LabelEntry for temperature
    set tarray(del) $frame.del
    set tarray(save) $frame.save
    set tarray(load) $frame.load
    set tarray(delrow) $frame.delrow
    set tarray(insrow) $frame.insrow
    
    # configuration values for table widget
    set tarray(rows) $r
    set tarray(cols) 3
    set tarray(roworigin) 0
    set tarray(data) $datavar_name
    set tarray(titlerows) 1
    set tarray(titlecols) 1

    # Background color of the table
    set tarray(bg) ivory
    
    # add it to the tables list
    lappend st_tables(active) $tarray_name

    st_putdebug ">> st_new_table info returns: $tarray_name"

    return $tarray_name
}

#----------------------------------------------------------------------
#  st_enable_undelete_button 
#
#  params:
#      setting - 1 to enable the button
#                0 to disable the button
#
#  Sets the state of the top level undelete button
#----------------------------------------------------------------------
proc st_enable_undelete_button {setting} {
    global st_wins
    if $setting {
    	$st_wins(undel) config -state normal	
    } else {
	$st_wins(undel) config -state disabled
    }
}

#----------------------------------------------------------------------
#  st_add_table
#
#  params:
#       t - Optional param defaults to 0. This is the temperature 
#           for the new table. 
#
# Adds a new table with the temperature t
#----------------------------------------------------------------------
proc st_add_table {{temp 0}} {
    global st_tables st_wins
    
    st_putdebug ">> st_add_table temp=$temp"

    # default data for the new table
    for {set i 0} {$i < 21} {incr i} {
	lappend newdata 0
    }
    
    # create the table info array
    set newtable [st_new_table_info $newdata]

    # create the table widget 
    # t is the path to the table widget itself
    set t [st_create_table_panel $newtable]

    # show the table
    set fr [st_display_table $newtable]
    
    # Some attempts at showing the new table, but so far none
    # of these are working.
    st_putdebug "   xview: [$st_wins(scrollf) xview]"
    #    $st_wins(scrollf) see $st_wins(tparent) top right
    $st_wins(scrollf) see [$st_wins(scrollf) getframe] top right
    
    # Adding a table changes the data
    st_set_user_changed_curve
    
    st_putdebug "<< st_add_table"
}

#----------------------------------------------------------------------
# st_pack_tables_parent <path>
#
# params:
#     path - The path of the frame that holds the table and its
#            associated widgets
#
# Pack a table into the parent frame. 
#----------------------------------------------------------------------
proc st_pack_tables_parent {path} {
    global st_wins
    pack $path -in $st_wins(tparent) \
	-side left \
	-fill y -expand yes \
	-anchor center
}

#----------------------------------------------------------------------
# st_undelete
#
# undoes the last delete table
#----------------------------------------------------------------------
proc st_undelete {} {
    global st_tables st_wins
    
    # get the table from the deleted list
    set utable [st_pop_delete_stack]

    # Put it back into the active tables list
    lappend st_tables(active) $utable
    
    # And then display it. 
    # Note that this alwyas puts it at the end of the row of tables
    upvar \#0 $utable tarray
    st_putdebug ">> st_undelete $utable"
    st_putdebug "\t delete list length: [llength $st_tables(deleted)]"

    # display table
    st_pack_tables_parent $tarray(frame)
    $st_wins(scrollf) see $st_wins(tparent) top right
    
    # If there are no tables left in the delete stack
    # then disable this button
    if ![llength $st_tables(deleted)] {
	st_enable_undelete_button 0
    }

    # user changed something
    st_set_user_changed_curve
    
    st_putdebug "<< st_undelete"
}


#----------------------------------------------------------------------
#  st_save_to_file
#
#  Params:
#      name - Name of the table array holding the curve data.
#
#  Opens a dialog box prompting the user for the name of a file. 
#  Saves data from a table into the file.
#
#----------------------------------------------------------------------
proc st_save_to_file {name} {
    upvar \#0 $name table
    st_putdebug ">> st_save_to_file $name" 

    # default name for the new file
    regsub st_ $name curve defname
    
    set fname [tk_getSaveFile -title "Save curve to file" \
		  -initialfile $defname.tbl]
    if {[catch {
	set f [open $fname w+]
	set xy [st_get_xy $table(data) $table(rows)]
	# First write the temperature
	puts $f "$table(temp)\t"

	# write x and y data in two columns
	foreach {x y} $xy {
	    puts $f "$x\t$y"
	}
	close $f
    } result]} then {
	st_putdebug "CATCH $result"
	catch {close $f}
    }
    st_putdebug "<< st_save_to_file $fname"
}

#----------------------------------------------------------------------
#  st_load_from_file <name>
#
#  Params:
#      name - Name of curve/table to change.
#
#  Opens a dialog box for the user to specify a file. Loads the
#  data in the file into the selected table. 
#
#----------------------------------------------------------------------
proc st_load_from_file {name} {
    global st_cfg
    upvar \#0 $name table 

    st_putdebug ">> st_load_from_file table=$name"    

    set fname [tk_getOpenFile -title "Load curve from file"]
    st_putdebug "   open file: $fname"

    upvar \#0 $table(data) data

    if {[catch {
	set f [open $fname r]

	# read file
 	set newdata [read $f]
	close $f 
	
	# get the temperature
	set temp [lindex $newdata 0]
	set table(temp) $temp
	
	# get the rest of the data
	set newdata [lrange $newdata 1 end]
	set table(rows) [expr [llength $newdata] / 2 + 1]
	$table(table) configure -rows $table(rows)
	st_putdebug "    temp=$temp row=$table(rows)"

	# Clear the old array 
	unset data
	array set data {}
	
	# Put the data into the table variable
	set r 1
	foreach {x y} $newdata {
	    set data($r,0) $r
             if {$st_cfg(num_format_str_0) == "%d"} {
                set x [expr int($x)]
             } else {
	       set x [format $st_cfg(num_format_str_0) $x]
             }
	    set data($r,1) $x
             if {$st_cfg(num_format_str_1) == "%d"} {
                set y [expr int($y)]
             } else {
	       set y [format $st_cfg(num_format_str_1) $y]
             }
	    set data($r,2) $y
	    incr r
	}
	set data(0,0) \#
	#set data(0,1) x
	#set data(0,2) y
         set data(0,1) $st_cfg(x_label)
         set data(0,2) $st_cfg(y_label)
	
	# force a table refresh
	$table(table) configure -padx [$table(table) cget -padx]
	
	# Change state because user edited curve
	st_set_user_changed_curve

    } result]} then {
	st_putdebug "CATCH $result"
	catch {close $f}
    }
    st_putdebug "<< st_load_from_file $fname"


}

#----------------------------------------------------------------------
#  st_get_xy
#
#  Args:
#     datavar - The name of table data variable
#     r - The number of rows in the data
#
#  Returns a list which is the x y values of a curve.
#----------------------------------------------------------------------
proc st_get_xy {datavar r} {
    global st_cfg
    upvar \#0 $datavar data
    st_putdebug ">> st_get_xy data=$datavar rows=$r"
    set val {}
    for {set i 1} {$i < $r} {incr i} {
	set x $data($i,1)
	set y  $data($i,2)
	if {$i == 1} {
	    st_putdebug "   row 1: x=$x ; space=[string is space $x]"
	}
	if {![string is double $x] || [string is space $x] } {
	    st_putdebug "   bad value at ($i,1)=$x"
	    set data($i,1) [format $st_cfg(num_format_str_0) 0]
	}
	if ![string is double $y] {
	    st_putdebug "   bad value at ($i,2)=$y"
	    set data($i,2) [format $st_cfg(num_format_str_1) 0]
	}

	lappend val $x $y
    }
    st_putdebug "<< st_get_xy "
    return $val
}

#----------------------------------------------------------------------
# st_save
#
# Saves the table data so that it can be returned later.
#----------------------------------------------------------------------
proc st_save {} {
    global st_tables st_user st_wins

    set output {}
    st_putdebug ">> st_save"
    st_putdebug "   active tables: $st_tables(active)"
    # Put data from the tables into the user data list
    
    foreach t $st_tables(active) {
	upvar \#0 $t table
	st_putdebug "   saving $t"
	# The first element of each sublist is the temperature
	# The rest are the x y vals
	set l [concat $table(temp) [st_get_xy $table(data) $table(rows)]]
	lappend output $l
    }
    set st_user(data) [list $output]

    # disable the save window
#    $st_wins(save) configure -state disabled
    
    # unset the dirty flat
    st_set_user_data_dirty 0
    
    st_putdebug "<< st_save"
}

#----------------------------------------------------------------------
#  st_exit_tables
#
#  params:
#     cancel - 1 if the user has hit the cancel button.
#     Returns from this script. Before it does so, it sends the table data
#     (if there is any) to standard out
#----------------------------------------------------------------------
proc st_exit_tables {{cancel 0}} {
    global st_user
    st_putdebug ">> st_exit_tables cancel=$cancel"
    
    if $cancel {
	# If the user hits the cancel button
	puts ""
	flush stdout
	exit
    } elseif $st_user(dirty) {
	# if exit is selected, but there is unsaved data,
	# ask if wants to save before exit
	set msg "You have unsaved changes.\n"
	append msg "How do you want to proceed?"
	set ans [MessageDlg .dlg -parent .\
		     -message $msg \
		     -aspect 1000 \
		     -type    user \
		     -icon    question \
		     -buttons {"Exit" "Save and Exit" "Cancel"}]
	destroy .dlg
	if {$ans == 0} {
	    st_putdebug "Exit, discarding changes"
	    exit
	} elseif { $ans == 1 } {
	    st_putdebug "Save and exit"
	    st_save
	    exit
	} elseif {$ans == -1} {
	    # The use just killed the window
	    return
	} else {
	    st_putdebug "Cancel"
	    return
	}
    } else {
	puts $st_user(data)
	flush stdout
	exit
    }
}

#----------------------------------------------------------------------
# st_delete_table <name>
#
# params:
#     name    The name of the table array which refers to the table
#             to delete.
#----------------------------------------------------------------------
proc st_delete_table {name} {

    global st_tables

    st_putdebug ">> st_delete_table name=$name"
    
    # get the table array with this name
    upvar \#0 $name table
    
    # Find the frame that holds the table and all it's widgets
    set top  $table(frame)
    st_putdebug "\t top level frame is $top"
    
    # Unpack the window so it doesn't show
    pack forget $top
    
    # put the table in the deleted table list
    st_push_delete_stack $name

    # remove the table from the table list
    st_putdebug "   Current active tables list=$st_tables(active)"    
    set temp_tables {}
    foreach t $st_tables(active) {
	st_putdebug "\t t=$t"
	if {$name != $t} {
	    lappend temp_tables $t
	    puts "."
	}
    }
    set st_tables(active) $temp_tables
    st_putdebug "   New active tables list=$st_tables(active)"
    
    # indicate that the data changed
    st_set_user_changed_curve

    # Activate the Undelete button
    st_enable_undelete_button 1
    
    st_putdebug "<< st_delete_table"
}

#----------------------------------------------------------------------
# st_delete_row 
#
# Params:
# name - The name of the table.
#
# Deletes the selected rows in the table named in the parameter list.
# Note that when data is deleted,the cells at the end of the array have
# no data in them.
#----------------------------------------------------------------------
proc st_delete_row {name} {
    upvar \#0 $name table

    set tpath $table(table)
    
    st_putdebug "<< st_delete_row $name"
    #set anchor [$tpath index active row]

    # Find the selected rows
    set r [lsort -integer [$tpath tag row sel]]

    if {[llength $r] == 0} {
	# if there are no selected rows, perhaps there is
	# an active row
	set r [$tpath index active row]
    }
    set begin [lindex $r 0]
    set count [llength $r]
    st_putdebug "\t delete start $begin num $count"

    if [llength $r] {
	$tpath delete rows -keeptitles -- $begin $count 
	
	# Adjust the number of rows in the table
	set table(rows) [expr $table(rows) - $count]
	st_putdebug "\tstart row=$begin num=$count"
	st_putdebug "\tremaining rows=$table(rows)"
	
	# Note: deletion clears the selection
	# So, after deleting, make the active row the selected row
	$tpath selection set active
	
	# Set the dirty flag since data was changed
	st_set_user_changed_curve
    } 
    st_putdebug "<< st_delete_row $name"
}


#----------------------------------------------------------------------
#  st_set_user_data_dirty
#
#  Params:
#      val - Optional boolean parameter. Says whether to set diry
#            on or off. Default is on.
#----------------------------------------------------------------------
proc st_set_user_data_dirty {{val 1}} {
    global st_user
    set st_user(dirty) $val
}

#----------------------------------------------------------------------
#  st_set_user_changed_curve
#
#  Params:
#     none
#
#  Sets the UI and the state when the user changes the data
#  via UI.
#----------------------------------------------------------------------
proc st_set_user_changed_curve {} {
    global  st_wins

    # turn on the save button
#    $st_wins(save) config -state normal
    
    # set the dirty bit
    st_set_user_data_dirty
}


#----------------------------------------------------------------------
#  st_insert_row
#
#  Params:
#      name - The name of the table.
#
#  Add a row beneath the currently selected row in the table named 
#  in the parameter list.
#----------------------------------------------------------------------
proc st_insert_row {name} {
    upvar \#0 $name table
    global debug st_cfg

    st_putdebug "st_insert_row $name >>"
    
    set tpath $table(table)
    
    if {$table(rows) == 1} {
	# user has deleted all rows
	set num 1
	set r 1
	set ans 1
    } else {
	# Next line will throw an error if no row is active
	set r [$tpath index active row]
	st_putdebug "    active row $r of $table(rows)"
	st_putdebug "    title rows [$tpath tag row title]"
	
	# If this is a title row, then can't insert a row
	if {$r == 0} {
	    # disable the button
	    $table(insrow) config -state disabled
	    return
	}
	
	# Put up a dialog box to determine if should insert above
	#or below current row.
	
	set ans [tk_dialog .insert_question "Insert Row" "Insert New Row Where?" "" -1 "Above Current" "Below Current" Cancel]
	
	# Where to insert
	if {$ans == 0} {
	    set num -1
	} elseif {$ans == 1} {
	    set num 1
	} else {
	    # If user selected cancel, then just return
	    return
	}
    }
    
    # insert the row
    puts "$tpath insert rows -keeptitles -- $r $num"
    $tpath insert rows -keeptitles -- $r $num
    
    # Change the number of rows in the table
    incr table(rows)
    
    # Put data into new row in the table's data variable
    upvar \#0 $table(data) data
    set r [expr $r + $ans]
    st_putdebug "    Add data to new row: $r"
    set data($r,1) [format $st_cfg(num_format_str_0) 0]
    set data($r,2) [format $st_cfg(num_format_str_1) 0]
    
    # Make the next row active
    $tpath activate $r,1
    
    # Number the new row that is added at the end
    set data([expr $table(rows) - 1],0) [expr $table(rows) -1]
    
    # Change state because user changed something
    st_set_user_changed_curve
    
    st_putdebug "st_insert_row <<"
}

# st_browse
#
# Params:
#     name - The name of the table.
#     last - last cell index
#     new - new cell index
#
# Gets called each time the active cell changes
proc st_browse {name last new} {
    global st_tables
    upvar \#0 $name table
    st_putdebug ">> st_browse $name last=$last new=$new"
    
    # get the row of the newly browsed cell
    set r [lindex [split $new ,] 0]

    if {[lsearch -exact [$table(table) tag row title] $r] != -1} {
	# if this was a title row,then turn off the row
	# command buttons
	set newstate disabled
    } else {
	# For all other rows  Activate the delete_row and 
	# insert_row buttons.for the current table
	set newstate normal
    }
    $table(insrow) configure -state $newstate
    $table(delrow) configure -state $newstate

    # Record that this table got browed
    set $st_tables(browse) $name
    st_putdebug "<< st_browse"
}

#----------------------------------------------------------------------
#  st_temp_changed
#
#  Invoked as the command for a temperature entry widget.
#----------------------------------------------------------------------
proc st_temp_changed {} {
    global st_wins st_user
    st_putdebug ">> st_temp_changed"
    st_set_user_changed_curve
    st_putdebug "<< st_temp_changed"
}

#----------------------------------------------------------------------
# st_validate_cell
#
# params:
#     cur - Current value of the cell being edited
#     new - The potential value for the active cell.
#
# Validate the contents of an edited cell
#----------------------------------------------------------------------
proc st_validate_cell { cur new } {
    global st_wins st_user
    set isgood 0

    # make sure this is a number      
    if {[string is double $new] || [string match {[-+.]} $new] \
	    || [string is space $new] || [string is double ${new}0]} {

	set isgood 1

	# If it is number then set the dirty bit
	# Note: This sets the dirty bit even when the value of the cell 
	# ultimately doesn't get changed, as in the case when the user
	# hits the escape button while editing a cell. But, it's 
	# a reasonable approximation.
	st_set_user_changed_curve
    }
    st_putdebug "<< st_validate_cell isgood=$isgood"
    
    return $isgood
}

#----------------------------------------------------------------------
#  st_create_curve_table
#
#  Params:
#      name - The name of the table.
#
#  Return value:
#      Returns the path to this table widget.
#
#  Creates the table widget.
#----------------------------------------------------------------------
proc st_create_curve_table {name} {
    global st_graph

    st_putdebug ">> st_create_curve_table name=$name"
    
    upvar \#0 $name tname
    
    # Make the table widget
    set tpath $tname(table)
    set sypath $tname(scrollbar)
    st_putdebug "sypath $sypath"
    table $tpath \
	-anchor center \
	-rows $tname(rows) \
	-cols $tname(cols) \
	-variable $tname(data) \
	-titlerows $tname(titlerows) \
	-titlecols $tname(titlecols) \
	-roworigin $tname(roworigin) \
	-yscrollcommand [list $sypath  set ] \
	-rowstretchmode none -colstretch unset \
	-bg $tname(bg) \
	-selectmode extended \
	-resizeborders col \
	-sparsearray 0 \
	-multiline 0 \
	-validate 1 \
	-autoclear 1 \
	-colwidth 16 \
	-validatecommand [list st_validate_cell %s %S] \
	-browsecommand [list st_browse $name %s %S]
    
    # Make the title column smaller
    $tpath width 0 5

    # configure the standard tags
    #     select
    $tpath tag config sel -fg black -bg $st_graph(lightblue) \
	-relief raised
    #     active
    $tpath tag config active -fg black -bg $st_graph(lightblue) \
	-relief raised
    #     title
    $tpath tag config title -relief raised -fg black -bg bisque3

    st_putdebug "<< st_create_curve_table returns=$tpath"    

    return $tpath
}

#----------------------------------------------------------------------
# st_create_table_panel <name>
#
# Create a new table widget. <name> is the name of the array
# holding the table info. It also creates other widgets associated with 
# the table widget, such as the scrollbar and other buttons
# Returns the path.
#----------------------------------------------------------------------
proc st_create_table_panel {name} {
    global st_cfg st_graph
    
    st_putdebug ">> st_create_table_panel $name"
    upvar \#0 $name tname

    # make the frame for this table
    frame $tname(frame) -relief sunken \
	-height 80 -width 20 -borderwidth 2

    # Make the table widget
    set tpath [st_create_curve_table $name]

    # Make scrollbar for this table
    set sypath $tname(scrollbar)
    scrollbar $sypath -command [list $tpath yview]
    
    # A temperature entry LabelEntry
    if !$st_cfg(single) {
	set bt $tname(temp_entry)
	LabelEntry $bt \
	    -label "Temperature: " \
	    -side left \
	    -text $tname(temp) \
	    -textvariable ${name}(temp) \
	    -entrybg ivory \
	    -command st_temp_changed
    }

    # A delete table button
    set bt $tname(del)
    button $bt -text "Delete Curve" \
	-anchor center -relief $st_cfg(button.relief) \
	-command [list st_delete_table $name] 
    
    # A load table button
    set bt $tname(load)
    button $bt -text "Load from File ..." \
	-anchor center -relief $st_cfg(button.relief) \
	-command [list st_load_from_file $name]
    
    # A save table button
    set bt $tname(save)
    button $bt -text "Save to File ..." \
	-anchor center -relief $st_cfg(button.relief) \
	-command [list st_save_to_file $name] 

    # A delete row button
    set bt $tname(delrow)
    button $bt -text "Delete Row(s)" \
	-anchor center -relief $st_cfg(button.relief) \
	-state disabled \
	-command [list st_delete_row $name] 

 
   # An add row button
    set bt $tname(insrow)
    button $bt -text "Insert Row..." \
	-anchor center -relief $st_cfg(button.relief) \
	-state disabled \
	-command [list st_insert_row $name] 

    # bind the return key for each table
    bind $tpath <Return> {
	set r [%W index active row]
	set c [%W index active col]
	%W selection clear $r,$c
	%W activate [incr r],1
	%W see active
	break
    }
    
    # bind the tab key for each table
    bind $tpath <Tab> {
	set r [%W index active row]
	set c [%W index active col]

	%W selection clear $r,$c
	if {$c == 1} {
	    %W activate $r,2
	} else {
	    %W activate [incr r],1
	}
	%W see active
	break
    }
    
    return $tpath
}

#----------------------------------------------------------------------
#  st_display_table 
# 
#  Params: 
#  name - the name of the table to display
#
#  Returns the path to the table.
#
#  Creates the window structure and geometry for a table that hasn't
#  been displayed before. 
#----------------------------------------------------------------------
proc st_display_table {name} {
    global st_wins st_cfg

    st_putdebug ">> st_display_table $name "
    upvar \#0 $name table

    set f $table(frame)
    set t $table(table)
    set sy $table(scrollbar)

    # pack the top frame first
    st_pack_tables_parent $f
    
    # Put widgets into two vertical sections. Buttons on top
    # and the table on the bottom
    set top [frame $f.top]
    $top config -relief groove -bd 1 
    set bottom [frame $f.botton]

    pack $top -in $f \
	-side top -fill both -anchor n 
    pack $bottom -in $f -side top -anchor nw \
	-fill both -expand yes
    
    # pack all of the buttons in the top frame
    grid $table(load)       $table(save)   -in $top -sticky ew 
    if {!$st_cfg(single)} {
	grid $table(del)                   -in $top -sticky ew  
	grid $table(temp_entry) -          -in $top -sticky ew
    }
    grid $table(delrow)     $table(insrow) -in $top -sticky ew

    
    # Have to do this because buttons were created first
    if {!$st_cfg(single)} {
	raise $table(temp_entry)
    }
    raise $table(del)
    raise $table(load)
    raise $table(save)
    raise $table(delrow)
    raise $table(insrow)
    
     # pack the table and scrollbar inside the bottom frame
    pack $sy  -in $bottom -side right -anchor n -fill y
    pack  $t -in $bottom -side right -fill both \
	-ipadx 2 -ipady 5 \
 	-anchor n -expand 1
    raise $sy
    raise $t
    update idle
    pack propagate $f 0 ;# without this the table grows by 4 pixels every time we add a row
    st_putdebug "<< st_display_table"

    return $f
}


######################################################################
#
# Main script
##

#----------------------------------------------------------------------
#  st_edit_curves
#
#  For documentation, see top of file.
#----------------------------------------------------------------------
proc st_edit_curves {args} {
    global st_wins st_tables st_cfg st_graph

    set data ""
    set toplev ""
    set iargs ""
    
    # Process args
    set argc [llength $args]
    for {set i 0} {$i < $argc} {incr i} {
	switch -exact -- [lindex $args $i] {
	    -curves {
		set data [lindex $args [expr $i + 1]]
		incr i
	    }
	    -single {
		lappend iargs single 1
	    }
	    -debug {
		lappend iargs debug 1
	    }
	    -nofloatx {
		lappend iargs num_format_str_0 %s
	    }
	    -nofloaty {
		lappend iargs num_format_str_1 %s
	    }
	    -intx {
		lappend iargs num_format_str_0 %d
	    }
	    -inty {
		lappend iargs num_format_str_1 %d
	    }
	    -xlabel {
                   set val ""
		set val [lindex $args [expr $i + 1]]
                   lappend iargs x_label $val
		incr i
	    }
	    -ylabel {
                   set val ""
		set val [lindex $args [expr $i + 1]]
                   lappend iargs y_label $val
		incr i
	    }
	    -title {
                   set val ""
		set val [lindex $args [expr $i + 1]]
                   lappend iargs graph_title $val
		incr i
	    }
	    -toplevel {
		set toplev [lindex $args [expr $i + 1]]
		incr i
	    }
	}
    }
    
    st_init $iargs
    
    st_putdebug ">> st_edit_curves args=$args"
    
    if {$toplev == ""} {
	toplevel $st_wins(top)
    } else {
	set st_wins(top) $toplev
    }

    st_putdebug "   st_cfg(single)=$st_cfg(single)"
    
    # If the single flag was set and there is data, then 
    # change the data to be only the first curve
    if { $st_cfg(single) && $data != "" } {
	set data [list [lindex $data 0]]
    }
    
    # Using Bwidgets
    package require BWidget

    # Some colors - not for the embedded version
    # tk_setPalette bisque

    # Button frame at the top of window to hold the buttons
    set bf [frame $st_wins(top).bf -relief raised -bd 1]
    
    # Widgets at the top of the frame
    
    set rf flat
    if 0 {
	set exitw [button $bf.exit -text "Exit" \
		       -relief $rf -command st_exit_tables]
	set cancelw [button $bf.cancel -text "Cancel" -relief $rf \
			 -command [list st_exit_tables 1]]
	set st_wins(save) [button $bf.save \
			       -text "Save changes" -relief $rf \
			       -state disabled -command st_save]
    }
    set exitw [button $bf.exit -text "Accept" -relief $rf \
		   -command "set st_cfg(finish) 1"]
    set cancelw [button $bf.cancel -text "Cancel" -relief $rf \
		     -command "set st_cfg(finish) 0"]
    
    set st_wins(undel) [button $bf.undel -text "Undelete curve" \
		    -relief $rf -state disabled \
		    -command st_undelete]
    set addw [button $bf.add -text "New curve" \
		  -relief $rf -command st_add_table]
    set graphw [button $bf.graph -text "Graph..." \
		    -relief $rf -command st_graph_curves]

    # Make a frame to hold the tables and put it in the scrolled window
    set sw [ScrolledWindow $st_wins(top).sw \
		-relief groove -bd 1 -scrollbar horizontal]
    set st_wins(scrollf) [ScrollableFrame $sw.f \
			      -bg bisque4 -constrainedheight 1]
    $sw setwidget $st_wins(scrollf)
    set st_wins(tparent) [$st_wins(scrollf) getframe]

    # Make and configure the table widgets
    if { $data == {} } {
	# if there is no input then put up an empty table
	# default data for the new table
	for {set i 0} {$i < 21} {incr i} {
	    lappend newdata 0
	}
	# create the table info array
	set newtable [st_new_table_info $newdata]

	#create the table widget
	st_create_table_panel $newtable
    } else {
	foreach indata $data {
	    # Load data into the table variable and at the same time create
	    # a table array that will hold the table's configuration info
	    set t [st_new_table_info $indata] 
	    upvar \#0 $t table
	    
	    st_putdebug "   New table $t"
	    st_putdebug "   st_tables(active)  $st_tables(active)"
	    st_putdebug "   name of data is $table(data)"
	    st_parraydebug $t
	    
	    # create all the widgets associated with this table. This 
	    # is not only the table widget itself, but also its label
	    # frame, scrollbars, buttons, etc.
	    st_create_table_panel $t
	}
    }    

    # Arrange all the widgets ----------
    
    # Button all on the first row
    pack $st_wins(top).bf -side top  -fill x -anchor n
    if $st_cfg(single) {
	#set buttons [list $exitw $cancelw $st_wins(save) $graphw]
	set buttons [list $exitw $cancelw $graphw]
    } else {
	# $st_wins(save)
	set buttons [list $exitw $cancelw \
			 $st_wins(undel) $addw $graphw]
    }
    foreach b $buttons {
	pack $b -side left
    }

    # window containing the tables on second row
    pack $sw -side top -expand yes -fill both -anchor n
    foreach t $st_tables(active) {
	st_display_table $t
    }
    st_putdebug "<< st_edit_curves"
    
    if {$toplev == ""} {
	wm geometry $st_wins(top) +300+300
	wm title $st_wins(top) "Curve editor"
    }
    vwait st_cfg(finish)
    destroy $st_wins(top)
    
    # Get rid of the graph if any
    catch {destroy .graphdlg}
    
    if !$st_cfg(finish) { return "" }
    
    set ret ""
    foreach n $st_tables(active) {
	upvar \#0 $n lvar1
	set t $lvar1(temp)
	set r $lvar1(rows)
	upvar \#0 $lvar1(data) lvar2
	set ret1 [list $t]
	for {set i 1} {$i < $r} {incr i} {
	    #lappend ret1 [string trim $lvar2($i,1)] [string trim $lvar2($i,2)]
             if {$st_cfg(num_format_str_0) == "%d"} {
                set x [expr int($lvar2($i,1))]
             } else {
	       set x $lvar2($i,1)
             }
             if {$st_cfg(num_format_str_1) == "%d"} {
                set y [expr int($lvar2($i,2))]
             } else {
	       set y $lvar2($i,2)
             }
             lappend ret1 [string trim $x] [string trim $y]
	}
	lappend ret $ret1
    }
    return $ret
}

######################################################################
#
# Stuff for testing
#
#
proc st_edit_curves_test {} {
    st_init
    set d {}                               
    foreach t {1000 1788 1888 1250} {
	set l {}                           
	set i 0
	set yi 5
	set xi 10
	set x 1.0              
	set y0 [expr $t / 3]
	set y $y0
	for {set r 0} {$r < 50} {incr r} { 
	    set y [expr $y0 + 100 * $i + [expr 2 * [expr $r * $yi]]]
	    set x [expr $t + [expr $r * $xi] - 1500]
	    lappend l $x $y
	}                                  
	lappend d [concat $t $l]
	set i [expr $i + 23]
    }                                      

    st_edit_curves -curves $d ;# -debug 
}




