#
# newspread.tcl - Implement a generic spreadsheet "widget" for use in 
#                   IDEAL tools (including QUANTUM).
#
# Copyright 1992, 1993 Virginia Polytechnic Institute and State University.
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#      This product includes software developed by Virginia Polytechnic
#      Institute and State University.
# 4. Neither the name of the University nor the names of its contributors
#    may be used to endorse or promote products derived from this software
#    without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $RCSfile: newspread.tcl,v $
# $Author: cstruble $
# $Date: 93/05/13 18:37:17 $
# $Locker:  $
# $Revision: 1.2 $
# $State: Exp $
#
# $Log:	newspread.tcl,v $
# Revision 1.2  93/05/13  18:37:17  cstruble
# Added copyright message.
# 
# Revision 1.1  93/05/12  13:57:01  cstruble
# Initial revision
# 
#
# Created: November 4, 1992
#
# April 7, 1993
#       Craig Struble - Added the ability to add application specific
#               bindings to the spreadsheet. Added the ability to
#               retrieve the text from a specified cell. Added various
#               convienence functions for finding current rows and
#               columns. Click anywhere in a cell!!!!
#
# March 30, 1993
#       Craig Struble - Save and Load are added. Auto scrolling is
#               checked for on every key press. May slow things down,
#               initial tests don't show any problems.
#
# March 29, 1993
#       Craig Struble - Added Mac-like command keys for editing in the text
#
# March 19, 1993
#       Craig Struble - Added selection capability, and replacement and
#               deletion of text within a selection. Any selection is cleared
#               when a key is hit or a cell is traversed to. Will probably 
#               require explicit copy, cut, and paste capabilities within
#               the spreadsheet. Not X like, but easier to implement and more
#               accessible to non-X users (Mac/Windows)
#
# March 18, 1993
#       Craig Struble - Added Shift Left, Shift Right, Shift Up, and Shift Down
#               keys to navigate through the spreadsheet. Also added some what
#               of an autoscrolling capability in the vertical direction only.
#               Added the "-rows" flag which takes the number of automatically
#               created rows as an argument.
#
# March 17, 1993
#       Craig Struble - Made Return, Delete, Left and Right work in text
#               cells to have some kind of editing capability. Tab and 
#               Shift Tab act properly.
#
# March 5, 1993
#       Craig Struble - Added auto sizing of rows for cells. Big win. Now
#               there is no more running into another cell in the 
#               spreadsheet.
#
# February xx, 1993
#       Craig Struble - Added minor text editing features. No cut and paste
#               yet.
#
# February 2, 1993
#       Craig Struble - Widened the lines in the label canvas to make them
#               easier to grab. Added a transparent box at the bottom of
#               a spreadsheet so that the horizontal scroll bar only covers
#               the visible width of the spreadsheet.
#
# December 3, 1992
#	Craig Struble - Columns can no longer be shrunk beyond their minimum
#		size. Columns and their text are resized correctly now.
#

#
# Procedure: spreadsheet
#
# Purpose:
#     To create a spreadsheet widget according to the programmer's needs
#
# Parameters:
#     w - The pathname of the spreadsheet widget.
#     args - Various configuration arguments for the spreadsheet
#
proc spreadsheet {w args} {

    initSpreadsheet $w

    if ([llength $args]!=0) then {
	getSpreadArgs $w $args
    }

    dispSpreadsheet $w

    return $w
}

#
# Procedure: getSpreadArgs
#
# Purpose:
#     Parses the arguments passed to the spreadsheet widget when it is
#     created.
#
# Parameters:
#    w - The pathname of the spreadsheet widget
#    sargs - The list of arguments passed to the spreadsheet
#
proc getSpreadArgs {w sargs} {

    global spreadc_list labelc_list spread_rows

    set spreadc $spreadc_list($w)
    set labelc $labelc_list($w)

    global spreadWidth spreadHeight setHeight setWidth

    for {set argIndex 0} {$argIndex<[llength $sargs]} {incr argIndex} {

	case [lindex $sargs $argIndex] {
	    -labels { incr argIndex
		makeLabels $w [lindex $sargs $argIndex] }

	    -hscroll { incr argIndex 
		createSpreadHscrollbar $w [lindex $sargs $argIndex] }

	    -vscroll { incr argIndex
		createSpreadVscrollbar $w  [lindex $sargs $argIndex] }

	    -width {incr argIndex
		set spreadWidth [lindex $sargs $argIndex]
		set setWidth 0}

	    -height { incr argIndex
		set spreadHeight [lindex $sargs $argIndex]
		set setHeight 0}
	    
	    -font { echo "Font" }

            -rows { incr argIndex
		set spread_rows [lindex $sargs $argIndex] }
	}
    }
}

#
# Procedure: createSpreadHscrollbar
#
# Purpose:
#      This procedure creates a horizontal scrollbar for a specified
#      spreadsheet.
#
# Parameters:
#      w - The pathname of the spreadsheet widget
#      width - The amount of visible horizontal area of the spreadsheet
#
proc createSpreadHscrollbar {w width} {
    global hasHscroll labelc_list spreadc_list hscroll_list

    set labelc $labelc_list($w)
    set spreadc $spreadc_list($w)
    set hscroll_list($w) $width

    set hasHscroll true
    set hscroll $w.hscroll

    frame $hscroll
    scrollbar $hscroll.bar -orient horiz -relief sunken -command \
	"scroll_x_2 $spreadc $labelc"

    $spreadc configure -width $width
    $labelc configure -width $width


    return $hscroll
}

#
# Procedure: createSpreadVscrollbar
#
# Purpose:
#     This procedure creates a vertical scrollbar for the specified
#     spreadsheet.
#
# Parameters:
#     w - The pathname of the spreadsheet widget
#     height - The amount of visible vertical area of the spreadsheet
#
proc createSpreadVscrollbar {w height} {
    global hasVscroll setHeight spreadHeight spreadc_list labelc_list
    global vscroll_list

    set spreadc $spreadc_list($w)
    set labelc $labelc_list($w)
    set vscroll_list($w) $height

    set hasVscroll true
    set vscroll $w.vscroll
    
    canvas $w.labelf.blank -width 19 -height 1
    pack append $w.labelf $w.labelf.blank {right filly}

    canvas $w.hscroll.blank -width 19 -height 1
    pack append $w.hscroll $w.hscroll.blank {right filly}

    scrollbar $vscroll -relief sunken -command \
	"$spreadc yview"

    $spreadc configure -height $height

    return $vscroll
}

#
# Procedure: start_spread_drag
#
# Purpose:
#	This procedure initializes the gray drag lines, and the current
#	x and y coordinates to support column line dragging. This procedure
#	is called when button one (usually the left button) is pressed on a
#	column line in the label canvas. 
#
# Parameters:
#       w - The spreadsheet we are resizing a column in.
#	x - The absolute x coordinate where the mouse click occured
#	y - The absolute y coordinate where the mouse click occured
#
proc start_spread_drag {w x y} {
	global lastX lastY grayline origX origY spreadc_list labelc_list
	set c1 $labelc_list($w)
	set c2 $spreadc_list($w)

	set lastX [$c1 canvasx $x]
	set lastY [$c1 canvasy $y]
	set origX [expr $lastX-2]
	set origY [expr $lastY-2]
	set maxY(labelc) [lindex [$c1 bbox all] 3]
	set maxY(spreadc) [lindex [$c2 bbox all] 3]

	set grayline(labelc) [$c1 create line $lastX 0 $lastX $maxY(labelc) \
			-fill "dark slate gray"]
	set grayline(spreadc) [$c2 create line $lastX 0 $lastX $maxY(spreadc) \
			-fill "dark slate gray"]
}

#
# Procedure: spread_drag
#
# Purpose:
#	This procedure moves the gray drag lines in the label canvas and the
#	spreadsheet canvas as the user holds button one and moves the mouse
#	after initiating a column line drag.
#
# Parameters:
#       w - The spread sheet we are resizing a column in
#	x - The x coordinate where the current motion event took place
#	y - The y coordinate where the current motion event took place
#
proc spread_drag {w x y} {
	global lastX lastY grayline spreadc_list labelc_list
	set c1 $labelc_list($w)
	set c2 $spreadc_list($w)

	set x [$c1 canvasx $x]
	set y [$c1 canvasy $y]
	#
	# don't move in the y coordinate
	#
	$c1 move $grayline(labelc) [expr $x-$lastX] 0
	$c2 move $grayline(spreadc) [expr $x-$lastX] 0
	set lastX $x
	set lastY $y
}	

#
# Procedure: stop_spread_drag
#
# Purpose:
#	This procedure stops the current column line drag. As a result,
#	the gray lines are removed from both the label and spreadsheet
#	canvases, and the columns are shifted to reflect where the user
#	moved the column line to. This procedure is called when the user
#	releases button one after initiating a column drag, and 
#	possibly executing a column drag.
#
# Parameters:
#       w - The spreadsheet widget we are dragging
#	x - The x coordinate where the mouse button was released.
#	y - The y coordinate where the mouse button was released.
#
proc stop_spread_drag {w x y} {
	global grayline origX origY min_widths spreadc_list labelc_list

	set c1 $labelc_list($w)
	set c2 $spreadc_list($w)

	$c1 delete $grayline(labelc)
	$c2 delete $grayline(spreadc)
	if ("[$c1 find withtag current]"=="") then \
		{ set current_canv $c2} \
	else \
		{ set current_canv $c1}

	set line_tags [lindex [$current_canv itemconfigure current -tags] 4]
	set moved_column [lindex $line_tags [lsearch $line_tags "col\[0-9\]*"]]

	set x [$c1 canvasx $x]
	set min_x [keylget min_widths($w) $moved_column]
	if ($x<$min_x) {set x $min_x}

	set move_x [expr $x-[lindex [$current_canv coords current] 0]]

	set bottomY [lindex [$c1 coords [$c1 find withtag bottom]] 1]
	$c1 coords [$c1 find withtag bottom] 0 $bottomY 0 $bottomY
	
	set lastX [lindex [$c1 bbox all] 2]
	set labelY [lindex [$c1 bbox all] 3]
	set spreadY [lindex [$c2 bbox all] 3]

	$c1 addtag moveit enclosed [expr $origX-3] 0 $lastX $labelY
	$c2 addtag moveit enclosed [expr $origX-3] 0 $lastX $spreadY

	$c1 move moveit $move_x 0
	$c2 move moveit $move_x 0

	resizeTextColumns $c1 $moved_column $move_x
	resizeTextColumns $c2 $moved_column $move_x

	$c1 dtag moveit
	$c2 dtag moveit

	set lastX [lindex [$c1 bbox all] 2]
	lappend scrollr 0 0 $lastX $labelY
	$c1 configure -scrollregion $scrollr
	
	$c1 coords [$c1 find withtag bottom] 0 $bottomY $lastX $bottomY

	unset scrollr
	lappend scrollr 0 0 $lastX [expr $spreadY+20]
	$c2 configure -scrollregion $scrollr
	resizeRowLines $c2 $lastX
}

#
# Procedure: resizeTextColumns
#
# Purpose:
#     Set the widths of the text items in a resized column to the width of
#     the column.
#
# Parameters:
#     c - The spreadsheet canvas that we resized a column on
#     column - The column tag of the column that was resized
#     width_change - The amount in pixels that the column's size changed
#

proc resizeTextColumns {c column width_change} {
	set columnItems [$c find withtag $column]

	foreach item $columnItems {
		if ("[$c type $item]"=="text") then {
			set currentwidth [lindex \
				 [$c itemconfigure $item -width] 4]
			set newwidth [expr $currentwidth+$width_change]
			
			$c itemconfigure $item -width $newwidth
		}
	}
}

#			
# Procedure: makeLabels
# 
# Purpose:
#     This procedure makes labels for a specified spreadsheet
#
# Parameters:
#     w - The pathname of the spreadsheet widget
#     labels - A list of the labels and column widths for the spreadsheet.
#          This parameter has the form:
#              {{"Title1" width1} {"Title2" width2} ... }
#
proc makeLabels {w labels} {

    global spreadWidth setWidth min_widths all_cols labelc_list spreadc_list
    global labels_list

    set labelc $labelc_list($w)
    set spreadc $spreadc_list($w)
    set labels_list($w) $labels

    set current_x 0
    set current_col 0
    set line_tags dummy

    for {set argIndex 0} {$argIndex<[llength $labels]} {incr argIndex} {
	set currentLabel [lindex $labels $argIndex]
	set labelText [lindex $currentLabel 0]
	set columnWidth [lindex $currentLabel 1]
	set current_tag [format "col%d" $current_col]

	incr current_x 5
	set cur_text [$labelc create text $current_x 0 -text $labelText -width\
            $columnWidth -anchor nw -tags $current_tag]

        set min_width [lindex [$labelc itemconfigure $cur_text -width] 4]
	set current_x [expr $current_x+$min_width+15]

	unset line_tags
	lappend line_tags col $current_tag 
	set current_line [$labelc create line $current_x 0  $current_x 1 \
			  -tags $line_tags -width 3]
	set current_line [$spreadc create line $current_x 0 $current_x 1 \
			  -tags $line_tags]
	keylset min_widths($w) $current_tag $current_x
	lappend all_cols($w) $current_tag

	makeLineBindings $w
	incr current_col
    }
    set max_x [lindex [$labelc bbox all] 2]
    set max_y [lindex [$labelc bbox all] 3]
    lappend scrollr 0 0 $max_x $max_y
    $labelc configure -scrollregion $scrollr -height $max_y
    $labelc create line 0 [expr $max_y-1] $max_x [expr $max_y-1] -tags bottom
    resizeLines $labelc $max_y

    if ($setWidth!=0) then {
	set spreadWidth $max_x
    }
}

#
# Procedure: makeLineBindings
#
# Purpose:
#    Make the bindings to the column lines so that columns can be resized.
#
# Parameters:
#    w - The label canvas that is having its lines updated
#

proc makeLineBindings {w} {
    global spreadc_list labelc_list

    set labelc $labelc_list($w)
    
    $labelc bind col <B1-Motion> \
	"spread_drag $w %x %y"
    $labelc bind col <1> \
	"start_spread_drag $w %x %y"
    $labelc bind col <ButtonRelease-1> \
	"stop_spread_drag $w %x %y"
}

#
# Procedure: resizeLines
#
# Purpose:
#     Resizes the height of vertical lines in a specified canvas. All
#     horizontal lines in the canvas need to have the tag "col" associated
#     with them.
#
# Parameters:
#    c - The canvas whose lines we are resizing
#    height - The new height of the lines
#
proc resizeLines {c height} {
    set allItems [$c find withtag col]

    foreach item $allItems {

	if {"[$c type $item]"=="line"} then {
	    set currCoords [$c coords $item]
	    set x0 [lindex $currCoords 0]
	    set y0 [lindex $currCoords 1]
	    set x1 [lindex $currCoords 2]
	    set y1 $height
	    $c coords $item $x0 $y0 $x1 $y1
	}
    }
}

#
# Procedure: resizeRowLines
#
# Purpose:
#     Resizes the width of horizontal lines in a specified canvas. All
#     horizontal lines in the canvas need to have the tag "row" associated
#     with them.
#
# Parameters:
#    c - The canvas whose lines we are resizing
#    width - The width of the new lines
#
proc resizeRowLines {c width} {
    set allItems [$c find withtag row]

    foreach item $allItems {

	if {"[$c type $item]"=="line"} then {
	    set currCoords [$c coords $item]
	    set x0 [lindex $currCoords 0]
	    set y0 [lindex $currCoords 1]
	    set x1 $width
	    set y1 [lindex $currCoords 3]
	    $c coords $item $x0 $y0 $x1 $y1
	}
    }
}

#
# Procedure: initSpreadsheet
#
# Purpose:
#     Initializes some global variables for creation of a new spreadsheet
#     widget.
#
# Parameters:
#     w - The pathname of the new spreadsheet
#
proc initSpreadsheet {w} {
    global hasHscroll hasVscroll spreadWidth spreadHeight setWidth setHeight
    global current_row labelc_list spreadc_list all_rows all_cols min_widths
    global spread_rows spread_select

    set hasHscroll false
    set hasVscroll false
    set setWidth 1
    set setHeight 1
    set spreadHeight 0
    set spread_rows 0

    if {[info vars spread_select] == ""} {set spread_select ""}

    set spreadc_list($w) $w.spreadc
    set labelc_list($w) $w.labelf.labelc
    set current_row($w) 0
    set all_rows($w) {}
    set all_cols($w) {}
    set min_widths($w) {}

    frame $w
    frame $w.labelf
    canvas $spreadc_list($w)
    canvas $labelc_list($w)

    bind $spreadc_list($w) <1> "spreadsheetScanForCell $w %x %y"
}

#
# Procedure: checkForScroll
#
# Purpose:
#     Checks to see what kind of scrollbars a spreadsheet has, and displays
#     them, or sizes the spreadsheet correctly depending on the existence
#     of scrollbars.
#
# Parameters:
#     w - The pathname of the spreadsheet widget
#
proc checkForScroll {w} {
    global hasHscroll hasVscroll spreadWidth spreadHeight spreadc_list
    global labelc_list

    set spreadc $spreadc_list($w)
    set labelc $labelc_list($w)
    set hscroll $w.hscroll
    set vscroll $w.vscroll

    if ("$hasHscroll"=="true") then {
	pack append $hscroll $hscroll.bar {frame nw fillx}
	pack append $w $hscroll {bottom fillx}
	$spreadc configure -xscroll "$hscroll.bar set"
	$labelc configure -xscroll "$hscroll.bar set"
    } else {
	$spreadc configure -width $spreadWidth
	$labelc configure -width $spreadWidth
    }
    
    if ("$hasVscroll"=="true") then {
	pack append $w $vscroll {right filly}
	$spreadc configure -yscroll "$vscroll set"
    } else {
	$spreadc configure -height $spreadHeight
    }
}

#
# Procedure: dispSpreadsheet
#
# Purpose:
#     Displays the spreadsheet in all its wonderful glory.
#
# Parameters:
#     w - The pathname of the spreadsheet widget.
#
proc dispSpreadsheet {w} {

    global spreadWidth spreadHeight setWidth setHeight spreadc_list labelc_list
    global spread_rows

    set spreadc $spreadc_list($w)
    set labelc $labelc_list($w)
 
    lappend scrollr 0 0 $spreadWidth $spreadHeight
    $spreadc configure -scrollregion $scrollr
    $labelc configure -scrollregion $scrollr
    pack append $w.labelf $labelc {frame nw fillx}
    resizeLines $spreadc $spreadHeight
    pack append $w $w.labelf {top fillx}
    
    checkForScroll $w

    pack append $w $spreadc {expand fill}
    for {set row 0} {$row < $spread_rows} {incr row} {
	addRow $w
    }
}

#
# Procedure: makeTextItemBindings
#
# Purpose:
#    Make bindings to the text items for the spreadsheet. These bindings are
#    as follows:
#       button 1 - Place cursor in the selected cell
#       button 1 motion - Select text in the current cell
#       shift and button 1 - Extend the text selection
#       return - Add a row within a cell
#       delete - delete a character
#       left - move left within the cell
#       right - move right within the cell
#       tab - move to the next cell add a row if moving past the last cell
#       shift and tab - move to the previous cell if possible
#       shift right - same as tab
#       shift left - same as shift tab
#       shift up - move up one cell
#       shift down - move down one cell, add a row if in the last row
#       Control c - copy text selection to the buffer
#       Control v - paste text selection at the current insertion point
#       Control x - delete selection and copy it to cut buffer
#       Mod1 c - same as control c
#       Mod1 v - same as control v
#       Mod1 x - same as control x
#       Mod2 c - same as control c
#       Mod2 v - same as control v
#       Mod2 x - same as control x
#       Any other keypress - insert character as current insertion point
#
# Parameters:
#    w - The spreadsheet that we are adding the bindings to
#    row - The current row tag that we are currently adding the bindings to
#

proc makeTextItemBindings {w row} {
   set spreadc [spreadsheetFindCanvas $w]

   set rowItems [$spreadc find withtag $row]
 
   foreach item $rowItems {
      if ("[$spreadc type $item]"=="text") then {
         $spreadc bind $item <1> "spreadsheetB1Press $w %x %y"
	 $spreadc bind $item <B1-Motion> "textB1Move $spreadc %x %y"
         $spreadc bind $item <Shift-1> "$spreadc select adjust ctext @%x,%y"
         $spreadc bind $item <KeyPress> "spreadsheetTextKeyPress $w $row %A"
         $spreadc bind $item <Return> "spreadsheetTextKeyPress $w $row \\n"
         $spreadc bind $item <Shift-KeyPress> \
           "spreadsheetTextKeyPress $w $row %A"
	 $spreadc bind $item <Delete> "textBs $spreadc"
	 $spreadc bind $item <Left> "textLeft $spreadc"
	 $spreadc bind $item <Right> "textRight $spreadc"
	 $spreadc bind $item <Tab> "spreadsheetForwardCell $w"
	 $spreadc bind $item <Shift-Tab> "spreadsheetBackwardCell $w"
	 $spreadc bind $item <Shift-Right> "spreadsheetForwardCell $w"
	 $spreadc bind $item <Shift-Left> "spreadsheetBackwardCell $w"
	 $spreadc bind $item <Shift-Up> "spreadsheetUpCell $w"
	 $spreadc bind $item <Shift-Down> "spreadsheetDownCell $w"
	 $spreadc bind $item <Control-c> "spreadsheetCopySelection $w"
	 $spreadc bind $item <Control-v> "spreadsheetPasteSelection $w"
	 $spreadc bind $item <Control-x> "spreadsheetCutSelection $w"
	 $spreadc bind $item <Mod1-c> "spreadsheetCopySelection $w"
	 $spreadc bind $item <Mod1-v> "spreadsheetPasteSelection $w"
	 $spreadc bind $item <Mod1-x> "spreadsheetCutSelection $w"
	 $spreadc bind $item <Mod2-c> "spreadsheetCopySelection $w"
	 $spreadc bind $item <Mod2-v> "spreadsheetPasteSelection $w"
	 $spreadc bind $item <Mod2-x> "spreadsheetCutSelection $w"
      }
   }
}

#
# Procedure: addRow
#
# Purpose:
#   Add a row to the end specified spreadsheet
#
# Parameters:
#   w - the pathname of the spreadsheet to add the row to
#

proc addRow {w} {
    global all_cols current_row labelc_list spreadc_list last_row
    global all_rows row_heights

    set labelc $labelc_list($w)
    set spreadc $spreadc_list($w)
    set current_row_tag [format "row%d" $current_row($w)]

    if ($current_row($w)>0) then {
	set current_y [expr [lindex \
			     [$spreadc bbox $last_row($w)] 3]+5]
    } else {
	set current_y 5
    }

    foreach current_column $all_cols($w) {
	set current_coords [$labelc bbox $current_column]
	set current_x [lindex $current_coords 0]
	set width [expr [lindex $current_coords 2]-$current_x-5]
	$spreadc create text [expr $current_x] $current_y \
	    -tags [list text $current_row_tag $current_column] \
	    -anchor nw -width $width
    }
    set current_y [lindex [$spreadc bbox $current_row_tag] 3]
    $spreadc create line 0 $current_y [expr $current_x+$width] \
	$current_y -tags [list row $current_row_tag]
    set row_heights($spreadc,$current_row_tag) \
	[lindex [$spreadc bbox $current_row_tag] 3]

    set last_x [lindex [$labelc coords bottom] 2]
    lappend scrollr 0 0 $last_x [expr $current_y+20]
    $spreadc configure -scrollregion $scrollr
    resizeLines $spreadc $current_y
    resizeRowLines $spreadc $last_x
    makeTextItemBindings $w $current_row_tag
    set last_row($w) $current_row_tag
    incr current_row($w)
    lappend all_rows($w) $current_row_tag
}

#
# Procedure: spreadsheetTextKeyPress
#
# Purpose:
#   Inserts the currently pressed key into the current cell
#
# Paramters:
#   w - The spreadsheet that we are putting the character into
#   row - The row tag for the row that the current cell is in
#   char - The character we are inserting
#

proc spreadsheetTextKeyPress {w row char} {
    global row_heights 
    set spreadc [spreadsheetFindCanvas $w]
    
    #
    # Make sure we don't erase stuff on controls and modifiers
    #
    
    if {"$char" == ""} return

    #
    # Replace current selection with the character if there is a selection
    #
    
    catch {$spreadc dchars ctext sel.first sel.last}
    $spreadc insert ctext insert $char
    set current_height [lindex [$spreadc bbox $row] 3]

    #
    # If the size of the row increases, change the size of the spreadsheet
    # row and move all other rows down
    #
    
    if {"$current_height" > "$row_heights($spreadc,$row)" } then {
	moveSpreadRows $w $row \
	    [expr $current_height-$row_heights($spreadc,$row)]
    }
    set current_tags [lindex [$spreadc itemconfigure ctext -tags] 4]
    set col [lindex $current_tags [lsearch $current_tags "col\[0-9\]*"]]
    spreadsheetAutoScrollText $w [spreadsheetRowNum $w $row] \
       [spreadsheetColNum $w $col]
}

#
# Procedure: moveSpreadRows
#
# Purpose:
#   Move the spreadsheet rows down a specified number of pixels
#
# Parameters:
#   w - The spreadsheet we are affecting
#   row - The current row that is being extended
#

proc moveSpreadRows {w row height_diff} {
   global row_heights spreadc_list all_rows

   set spreadc $spreadc_list($w)
   set allItems [$spreadc find withtag $row]

   #
   # Update the lines surrounding the current row
   #

   foreach item $allItems {

       if {"[$spreadc type $item]"=="line"} then {
	   set currCoords [$spreadc coords $item]
	   set x0 [lindex $currCoords 0]
	   set y0 [lindex $currCoords 1]
	   set x1 [lindex $currCoords 2]
	   set y1 [lindex $currCoords 3]
       }
   }

   # 
   # Now move all the rows after the current row
   #

   set max_x [lindex [$spreadc bbox all] 2]
   set max_y [lindex [$spreadc bbox all] 3]
   
   $spreadc addtag moveit enclosed -5 [expr $y0-3] $max_x $max_y
   
   $spreadc move moveit 0 $height_diff
   $spreadc dtag moveit
   
   set max_y [lindex [$spreadc bbox all] 3]
   resizeLines $spreadc $max_y
   foreach cur_row $all_rows($w) {
       set row_heights($spreadc,$cur_row) [lindex [$spreadc bbox $cur_row] 3]
   }
   lappend scrollr 0 0 $max_x [expr $max_y+20]
   $spreadc configure -scrollregion $scrollr
}

#
# Procedure: spreadsheetForwardCell
#
# Purpose:
#    Moves focus to the next cell. Performs wrapping around the spreadsheet,
#    and will add a row if trying to go past the last cell in the spreadsheet
#
# Parameters:
#    w - The spreadsheet we are moving around in
#

proc spreadsheetForwardCell {w} {
   global spreadc_list all_rows all_cols

   set spreadc $spreadc_list($w)
   set current_cell [$spreadc find withtag ctext]
   set current_tags [lindex [$spreadc itemconfigure $current_cell -tags] 4]
   set current_row [lindex $current_tags [lsearch $current_tags "row\[0-9\]*"]]
   set current_col [lindex $current_tags [lsearch $current_tags "col\[0-9\]*"]]

   if {"$current_cell" == ""} {return}

   $spreadc select clear
   set row_num [lsearch $all_rows($w) $current_row]
   set col_num [lsearch $all_cols($w) $current_col]
   
   if {$col_num == [expr [llength $all_cols($w)]-1]} then {
	set col_num 0
        if {$row_num == [expr [llength $all_rows($w)]-1]} then {
           addRow $w
        }
        incr row_num
   } else {
        incr col_num
   }

   $spreadc dtag ctext
   set new_cell [spreadsheetFindCell $w $row_num $col_num]

   $spreadc addtag ctext withtag $new_cell
   $spreadc focus ctext
   $spreadc icursor ctext end
   spreadsheetAutoScrollText $w $row_num $col_num
}

#
# Procedure: spreadsheetBackwardCell
#
# Purpose:
#    Moves to the previous cell in the spreadsheet. Performs wrapping. Does
#    nothing if trying to move before the first cell.
#
# Parameters:
#    w - The spreadsheet we are moving around in
#

proc spreadsheetBackwardCell {w} {
   global spreadc_list all_rows all_cols

   set spreadc $spreadc_list($w)
   set current_cell [$spreadc find withtag ctext]
   set current_tags [lindex [$spreadc itemconfigure $current_cell -tags] 4]
   set current_row [lindex $current_tags [lsearch $current_tags "row\[0-9\]*"]]
   set current_col [lindex $current_tags [lsearch $current_tags "col\[0-9\]*"]]

   if {"$current_cell" == ""} {return}

   $spreadc select clear
   set row_num [lsearch $all_rows($w) $current_row]
   set col_num [lsearch $all_cols($w) $current_col]
   
   if {$col_num == 0} then {
	set col_num [expr [llength $all_cols($w)]-1]
        if {$row_num == 0} then {
                return
        } else {
		incr row_num -1
        }
   } else {
        incr col_num -1
   }

   $spreadc dtag ctext
   set new_cell [spreadsheetFindCell $w $row_num $col_num]

   $spreadc addtag ctext withtag $new_cell
   $spreadc focus ctext
   $spreadc icursor ctext end
   spreadsheetAutoScrollText $w $row_num $col_num
}

#
# Procedure: spreadsheetUpCell
#
# Purpose:
#    Moves to the cell above the current cell.
#
# Parameters:
#    w - The pathname of the spreadsheet that we are moving around in
#

proc spreadsheetUpCell {w} {
   global spreadc_list all_rows all_cols
   set spreadc $spreadc_list($w)
   set current_cell [$spreadc find withtag ctext]
   set current_tags [lindex [$spreadc itemconfigure $current_cell -tags] 4]
   set current_row [lindex $current_tags [lsearch $current_tags "row\[0-9\]*"]]
   set current_col [lindex $current_tags [lsearch $current_tags "col\[0-9\]*"]]

   if {"$current_cell" == ""} {return}

   set row_num [lsearch $all_rows($w) $current_row]
   set col_num [lsearch $all_cols($w) $current_col]
   
   $spreadc select clear

   if {$row_num == 0} then {
      return
   } else {
      incr row_num -1
   }

   $spreadc dtag ctext
   set new_cell [spreadsheetFindCell $w $row_num $col_num]

   $spreadc addtag ctext withtag $new_cell
   $spreadc focus ctext
   $spreadc icursor ctext end
   spreadsheetAutoScrollText $w $row_num $col_num
}

#
# Procedure: spreadsheetDownCell
#
# Purpose:
#     Move to the cell directly below the current cell. If there are no rows
#     past the current one, add a new row.
#
# Parameters:
#     w - The pathname of the spreadsheet we are moving around in.
#

proc spreadsheetDownCell {w} {
   global spreadc_list all_rows all_cols
   set spreadc $spreadc_list($w)
   set current_cell [$spreadc find withtag ctext]
   set current_tags [lindex [$spreadc itemconfigure $current_cell -tags] 4]
   set current_row [lindex $current_tags [lsearch $current_tags "row\[0-9\]*"]]
   set current_col [lindex $current_tags [lsearch $current_tags "col\[0-9\]*"]]

   if {"$current_cell" == ""} {return}

   $spreadc select clear

   set row_num [lsearch $all_rows($w) $current_row]
   set col_num [lsearch $all_cols($w) $current_col]

   if {$row_num == [expr [llength $all_rows($w)]-1]} then {
      addRow $w
   } 
  
   incr row_num
     
   $spreadc dtag ctext
   set new_cell [spreadsheetFindCell $w $row_num $col_num]

   $spreadc addtag ctext withtag $new_cell
   $spreadc focus ctext
   $spreadc icursor ctext end
   spreadsheetAutoScrollText $w $row_num $col_num
}

#
# Procedure: spreadsheetFindCell
#
# Purpose:
#     Find and return the ID of a cell given the row and column number or
#     the keyword "current". Returns -1 if no such cell exists.
#
# Parameters:
#     w - The pathname of the spreadsheet that we want to find the cell in.
#     row - The row number of the cell we are looking for or "current" if
#           we want the current cell
#     column - The column number of the cell we are looking for. Defaults
#              to -1 if using the current keyword.
#

proc spreadsheetFindCell {w row {column -1}} {
   global spreadc_list all_rows all_cols

   set spreadc $spreadc_list($w)

   if {"$row" == "current"} {
      return [$spreadc find withtag ctext]
   }

   set current_col [lindex $all_cols($w) $column]
   set current_row [lindex $all_rows($w) $row]
   set col_cells [$spreadc find withtag $current_col]

   foreach cell $col_cells {
     set current_tags [lindex [$spreadc itemconfigure $cell -tags] 4]
     set row_tag [lsearch $current_tags $current_row]

     if {$row_tag >= 0} {return $cell}
   }
   return -1
}


#
# Procedure: spreadsheetAutoScrollText
#
# Purpose:
#     Automatically scroll the spreadsheet to keep the active area in the
#     window.
#
# Parameters:
#     w - The pathname of the spreadsheet
#     row - The row number of the row that we are currently in.
#     col - The column number of the column we are currently in.
#
proc spreadsheetAutoScrollText {w row col} {
   global all_rows

   set spreadc [spreadsheetFindCanvas $w]
   set labelc [spreadsheetFindLabels $w]
   set current_row [lindex $all_rows($w) $row]

   set current_item [spreadsheetFindCell $w $row $col]
   set vert_scroll [lindex [$spreadc configure -scrollincrement] 4]
   set horiz_scroll [lindex [$spreadc configure -scrollincrement] 3]
   set lx_coord [lindex [$spreadc coords $current_item] 0]
   set rx_coord [lindex [$spreadc bbox $current_item] 2]
   set ty_coord [lindex [$spreadc coords $current_item] 1]
   set by_coord [lindex [$spreadc bbox $current_row] 3]
   set top [$spreadc canvasy 0]
   set left [$spreadc canvasx 0]
   set right [expr {$left + [winfo width $spreadc]}]
   set bottom [expr {$top + [winfo height $spreadc]}]

   set unit [expr {$lx_coord / $horiz_scroll - 1}]
   if {$lx_coord < $left} then {
       $spreadc xview [expr $unit-10]
       $labelc xview [expr $unit-10]
   } else {
      if {$rx_coord > $right} then {
          $spreadc xview [expr $unit-10]
          $labelc xview [expr $unit-10]
      }
   }

   set unit [expr {$ty_coord / $vert_scroll - 1}]
   if {$ty_coord < $top} then {
       $spreadc yview [expr $unit-10]
       return
   }
   if {$by_coord > $bottom-30} then {
       $spreadc yview [expr $unit-10]
   }
}

#
# Procedure: spreadsheetB1Press
#
# Purpose:
#     Handles the button 1 press in the spreadsheet. Puts the cursor into
#     the selected cell, and autoscrolls if necessary.
#
# Parameters:
#     w - The pathname of the spreadsheet we are currently looking at
#     x - The x coordinate where the mouse button was pressed.
#     y - The y coordinate where the mouse button was pressed.
#

proc spreadsheetB1Press {w x y} {
   global spreadc_list all_rows all_cols

   set spreadc $spreadc_list($w)
   textB1Press $spreadc $x $y

   set current_cell [$spreadc find withtag ctext]
   set current_tags [lindex [$spreadc itemconfigure $current_cell -tags] 4]
   set current_row [lindex $current_tags [lsearch $current_tags "row\[0-9\]*"]]
   set current_col [lindex $current_tags [lsearch $current_tags "col\[0-9\]*"]]

   if {"$current_cell" == ""} {return}

   set row_num [lsearch $all_rows($w) $current_row]
   set col_num [lsearch $all_cols($w) $current_col]
 
   spreadsheetAutoScrollText $w $row_num $col_num
}

#
# Procedure: spreadsheetInsertText
#
# Purpose:
#     Insert some text at the current insertion point in the selected cell.
#
# Parameters:
#     w - The pathname of the spreadsheet we are using
#     itext - The text we want to insert into the cell
#     row - The row number of the cell we want to insert text into, or
#        current if we want it in the current cell.
#     col - The column number of the cell we want to insert text into.
#

proc spreadsheetInsertText {w itext {row -1} {col -1}} {
   global spreadc_list row_heights all_rows

   set spreadc $spreadc_list($w)
   
   if {"$row" == "current"} then {
      set current_cell [$spreadc find withtag ctext]
      if {"$current_cell" == ""} {return}
      $spreadc insert ctext insert $itext
      return
   }

   if {"$row" == "-1" || "$col" == "-1"} then {
      echo "spreadsheetInsertText spreadsheet text row column"
      echo "or spreadsheetInsertText spreadsheet text current"
      return
   }

   $spreadc insert [spreadsheetFindCell $w $row $col] insert $itext

   set row [lindex $all_rows($w) $row]

   set current_height [lindex [$spreadc bbox $row] 3]
   
   if ("$current_height">"$row_heights($spreadc,$row)") then {
       moveSpreadRows $w $row \
	   [expr $current_height-$row_heights($spreadc,$row)]
   }
}

#
# Procedure: spreadsheetGetText
#
# Purpose:
#    Returns the text that is in the specified cell
#
# Parameters:
#    w - The pathname of the spreadsheet we are currently looking at
#    row - The row number of the cell we want the text from, or current if
#       we want text from the current cell.
#    col - The column number of the cell we want the text from.
#

proc spreadsheetGetText {w {row -1} {col -1}} {
   global spreadc_list row_heights all_rows

   set spreadc $spreadc_list($w)
   
   if {"$row" == "current"} then {
      set current_cell [$spreadc find withtag ctext]
      if {"$current_cell" == ""} {return}
      $spreadc insert ctext insert $itext
      return
   }

   if {"$row" == "-1" || "$col" == "-1"} then {
      echo "spreadsheetGetText spreadsheet row column"
      echo "or spreadsheetGetText spreadsheet current"
      return
   }

   return [lindex [$spreadc itemconfigure [spreadsheetFindCell $w $row $col] \
           -text] 4]
}


#
# Procedure: spreadsheetFindCanvas
#
# Purpose:
#     Returns the pathname of the spreadsheet canvas for the given spreadsheet
#      widget.
#
# Parameters:
#     w - The pathname of the spreadsheet widget we are looking at.
#

proc spreadsheetFindCanvas {w} {
   global spreadc_list

   return $spreadc_list($w)
}

#
# Procedure: spreadsheetFindLabels
#
# Purpose:
#     Returns the pathname of the label canvas for the given spreadsheet
#     widget.
#
# Parameters:
#     w - the pathname of the spreadsheet widget we are looking at.
#

proc spreadsheetFindLabels {w} {
   global labelc_list

   return $labelc_list($w)
}

#
# Procedure: spreadsheetCopySelection
#
# Purpose:
#    To copy the current selection into a private spreadsheet buffer.
#
# Parameters:
#    w - The pathname of the spreadsheet widget containing the selection.
#        NOT USED.
#

proc spreadsheetCopySelection {w} {
   global spread_select

   set spread_select [selection get]
}

#
# Procedure: spreadsheetClearSelection
#
# Purpose:
#    Deletes the current selection from the specified spreadsheet
#
# Parameters:
#    w - The pathname of the spreadsheet widget that has the selection.
#

proc spreadsheetClearSelection {w} {
   catch {[spreadsheetFindCanvas $w] dchars ctext sel.first sel.last}
}

#
# Procedure: spreadsheetCutSelection
#
# Purpose:
#    To copy the current selection into a private spreadsheet buffer, and then
#    to delete it from the cell containing the selection.
#
# Parameters:
#    w - The pathname of the spreadsheet widget that we are cutting text in.
#

proc spreadsheetCutSelection {w} {
   global spread_select

   spreadsheetCopySelection $w
   spreadsheetClearSelection $w
}

#
# Procedure: spreadsheetPasteSelection
#
# Purpose:
#    Pastes the information of the current spreadsheet buffer into the
#    current cell at the current insertion point. Clears out any current
#    selection to actually replace the text.
#
# Parameters:
#    w - The pathname of the spreadsheet widget that we are pasting the
#        selection into.
#

proc spreadsheetPasteSelection {w} {
   global spread_select

   set spreadc [spreadsheetFindCanvas $w]

   spreadsheetClearSelection $w

  $spreadc insert ctext insert $spread_select
}
	
#
# Procedure: spreadsheetSave
#
# Purpose:
#    Save the current information in the spreadsheet out to a file.
# 
# Parameters:
#    w - The pathname of the spreadsheet widget to save.
#    filename - The name of the file to save it to.
#
# Notes:
#    The file specified by filename will be overwritten.
#    The size of the spreadsheet columns will go back to there initial state,
#    not to their current state, when loaded back in.
#

proc spreadsheetSave {w filename} {
   global all_rows all_cols min_widths labels_list hscroll_list vscroll_list

   set spreadc [spreadsheetFindCanvas $w]
   set labelc [spreadsheetFindLabels $w]

   set output_file [open $filename w]

   puts $output_file "spreadsheet \$w " nonewline

   if {[info exists labels_list($w)]} {
       set current_text [format " -labels \{%s\}" $labels_list($w)]
       puts $output_file $current_text nonewline
   }
   if {[info exists hscroll_list($w)]} then {
       puts $output_file [format " -hscroll %s" $hscroll_list($w)] nonewline
   }
   if {[info exists vscroll_list($w)]} then {
       puts $output_file [format " -vscroll %s" $vscroll_list($w)] nonewline
   }
   puts $output_file "\n"

   puts $output_file "set cell_text \{ " nonewline
   for {set current_row 0} {$current_row < [llength $all_rows($w)]} \
       {incr current_row} {
	   puts $output_file "\{" nonewline
	   for {set current_col 0} {$current_col < [llength $all_cols($w)]} \
	       {incr current_col} {
		   set current_cell [spreadsheetFindCell $w \
				     $current_row $current_col]

		   set current_text [lindex [$spreadc itemconfigure\
					     $current_cell -text] 4 ]
		   regsub -all "\n" $current_text "\\n" current_text

		   puts $output_file [format "\"%s\" " $current_text] nonewline
	       }
	   puts $output_file "\} " nonewline
       }
   puts $output_file "\}"
   close $output_file
}

#
# Procedure: spreadsheetLoad
#
# Purpose:
#   To load in a spreadsheet that was previously saved.
#
# Parameters:
#   w - The name of the spreadsheet widget that will be created and filled
#       with the loaded spreadsheet.
#   filename - The name of the file to load the spreadsheet from.
#

proc spreadsheetLoad {w filename} {

    set input_file [open $filename r]

    gets $input_file command
    eval $command
    gets $input_file
    gets $input_file command
    eval $command
    
    close $input_file

    set curr_row 0

    foreach row $cell_text {
	addRow $w
	set curr_col 0
	foreach col $row {
	    spreadsheetInsertText $w $col $curr_row $curr_col
	    incr curr_col
	}
	incr curr_row
    }
    return $w
}

#
# Procedure: spreadsheetColNum
#
# Purpose:
#    Returns the column number given a column tag.
#
# Parameters:
#    w - The pathname of the spreadsheet widget that we are looking at.
#    col - The column tag that we want the column number for.

proc spreadsheetColNum {w col} {
   global all_cols

   if {"$col" == "current"} {
     set col [spreadsheetCol $w current]
   }
   return [lsearch $all_cols($w) $col]
}

#
# Procedure: spreadsheetRowNum
#
# Purpose:
#    Returns the row number given a row tag.
#
# Parameters:
#    w - The pathname of the spreadsheet widget we are looking at.
#    row - The row tag that we want the row number for.
#

proc spreadsheetRowNum {w row} {
   global all_rows

   if {"$row" == "current"} {
     set row [spreadsheetRow $w current]
   }
   return [lsearch $all_rows($w) $row]
}

#
# Procedure: spreadsheetCol
#
# Purpose:
#    Returns the column tag given a column number
#
# Parameters:
#    w - The pathname of the spreadsheet widget we are looking at.
#    col - The column number that we want the column tag for.
#

proc spreadsheetCol {w col} {
   global all_cols

   if {"$col" == "current"} {
      set spreadc [spreadsheetFindCanvas $w]
      set tags [lindex [$spreadc itemconfigure ctext -tags] 4]
      return [lindex $tags [lsearch $tags "col\[0-9\]*"]]
   }
   return [lindex $all_cols($w) $col]
}

#
# Procedure: spreadsheetRow
#
# Purpose:
#    Returns the row tag given a row number.
#
# Parameters:
#    w - The pathname of the spreadsheet widget we are looking at.
#    row - The row number that we want the row tag for.
#

proc spreadsheetRow {w row} {
   global all_rows

   if {"$row" == "current"} {
      set spreadc [spreadsheetFindCanvas $w]
      set tags [lindex [$spreadc itemconfigure ctext -tags] 4]
      return [lindex $tags [lsearch $tags "row\[0-9\]*"]]
   }

   return [lindex $all_rows($w) $row]
}

#
# Procedure: spreadsheetBind 
#
# Purpose:
#    To allow programmers to add their own specific bindings to cells, 
#    rows, and/or columns.
#
# Paramters:
#    w - The pathname of the spreadsheet that we are updating the bindings for.
#    row - The row number that we want to bind to, or all for the entire row.
#    col - The column number that we want to bind to, or all for the entire
#          column.
#    event - The event that we want to bind.
#    command - The command to execute when the specified event happens.
#

proc spreadsheetBind {w row col event command} {
    set spreadc [spreadsheetFindCanvas $w]

    if {"$row" != "all" && "$col" != "all"} {
       $spreadc bind [spreadsheetFindCell $w $row $col] $event "$command"
       return
    }

    if {"$row" != "all"} {
       $spreadc bind [spreadsheetRow $w $row] $event "$command"
       return
    }

    if {"$col" != "all"} {
       $spreadc bind [spreadsheetCol $w $col] $event "$command"
       return
    }

    $spreadc bind all $event "$command"
}

#
# Procedure: spreadsheetScanForCell
#
# Purpose:
#    Find which cell was chosen. This is used when a used clicks directly on
#    the canvas. Since text items don't necessarily reach across the cell
#    we need to fake the user into believing they clicked in the text box,
#    event if they didn't.
#
# Parameters:
#    w - The pathname of the spreadsheet widget we are looking at.
#    x - The x coordinate where the mouse click took place.
#    y - The y coordinate where the mouse click took place.
#

proc spreadsheetScanForCell {w x y} {
   global all_rows all_cols

   set spreadc [spreadsheetFindCanvas $w]

   set x [$spreadc canvasx $x]
   set y [$spreadc canvasy $y]

   for {set leftx $x} {$leftx > 0} {incr leftx -1} {
       set item [$spreadc find overlapping $leftx $y $x $y]
       if {$item != {}} {if {[$spreadc type $item] == "text"} break}
   }
   if {$leftx == 0} return

    $spreadc icursor $item @$leftx,$y
    $spreadc focus $item
    focus $spreadc
    $spreadc dtag ctext
    $spreadc select clear
    $spreadc select from $item @$leftx,$y
    $spreadc addtag ctext withtag $item
    set current_tags [lindex [$spreadc itemconfigure $item -tags] 4]
    set current_row [lindex $current_tags [lsearch $current_tags "row\[0-9\]*"]]
    set current_col [lindex $current_tags [lsearch $current_tags "col\[0-9\]*"]]

    set row_num [lsearch $all_rows($w) $current_row]
    set col_num [lsearch $all_cols($w) $current_col]
 
    spreadsheetAutoScrollText $w $row_num $col_num
}

#
# Procedure: spreadsheetSetCurrentCell
#
# Purpose:
#    Set the current cell of the canvas
#
# Parameters:
#    w - The pathname of the spreadsheet widget we are setting the cell in.
#    row - The row number of the cell we want to set.
#    col - The column number of the cell we want to set.

proc spreadsheetSetCurrentCell {w row col} {
    set spreadc [spreadsheetFindCanvas $w]
    
    set item [spreadsheetFindCell $w $row $col]
 
    $spreadc icursor $item end
    $spreadc focus $item
    focus $spreadc
    $spreadc select clear
    $spreadc dtag ctext
    $spreadc select from $item end
    $spreadc addtag ctext withtag $item
    
    spreadsheetAutoScrollText $w $row $col
}
