OSDN Git Service

Update tkTable to version 2.7:
authorkseitz <kseitz>
Mon, 13 Aug 2001 17:53:52 +0000 (17:53 +0000)
committerkseitz <kseitz>
Mon, 13 Aug 2001 17:53:52 +0000 (17:53 +0000)
* src/tkTableCmds.c, src/tkTable.tcl.h, src/tkTableCellSort.c,
src/tkTableEdit.c, src/tkTableInitScript.h, src/tkTablePs.c,
src/tkTableUtil.c, doc/tkTable.html: New files.
* src/tkTable.c, src/tkTable.h, src/TkTable.tcl, src/tkTableCell.c,
src/tkTableTag.c, src/tkTableWin.c, src/tkTable_version.in: Update to
version 2.7.
* configure.in: If compiling with cygwin, we need to have
WIN32 defined to build tkTable modules.
* configure: Regenerate.
* src/Makefile.am: Add new tkTable files and update build rules
for new version of tkTable.
* src/Makefile.in: Regenerate.

22 files changed:
libgui/ChangeLog
libgui/configure
libgui/configure.in
libgui/doc/tkTable.html [new file with mode: 0644]
libgui/doc/tkTable.n
libgui/library/Makefile.in
libgui/src/Makefile.am
libgui/src/Makefile.in
libgui/src/tkTable.c
libgui/src/tkTable.h
libgui/src/tkTable.tcl
libgui/src/tkTable.tcl.h [new file with mode: 0644]
libgui/src/tkTableCell.c
libgui/src/tkTableCellSort.c [new file with mode: 0644]
libgui/src/tkTableCmds.c [new file with mode: 0644]
libgui/src/tkTableEdit.c [new file with mode: 0644]
libgui/src/tkTableInitScript.h [new file with mode: 0644]
libgui/src/tkTablePs.c [new file with mode: 0644]
libgui/src/tkTableTag.c
libgui/src/tkTableUtil.c [new file with mode: 0644]
libgui/src/tkTableWin.c
libgui/src/tkTable_version.in

index fcb00a2..ca6d716 100644 (file)
@@ -1,3 +1,19 @@
+2001-08-12  Keith Seitz  <keiths@redhat.com>
+
+       Update tkTable to version 2.7:
+       * src/tkTableCmds.c, src/tkTable.tcl.h, src/tkTableCellSort.c,
+       src/tkTableEdit.c, src/tkTableInitScript.h, src/tkTablePs.c,
+       src/tkTableUtil.c, doc/tkTable.html: New files.
+       * src/tkTable.c, src/tkTable.h, src/TkTable.tcl, src/tkTableCell.c,
+       src/tkTableTag.c, src/tkTableWin.c, src/tkTable_version.in: Update to
+       version 2.7.
+       * configure.in: If compiling with cygwin, we need to have
+       WIN32 defined to build tkTable modules.
+       * configure: Regenerate.
+       * src/Makefile.am: Add new tkTable files and update build rules
+       for new version of tkTable.
+       * src/Makefile.in: Regenerate.
+
 2001-08-06  Mo DeJong  <mdejong@redhat.com>
 
        * Makefile.in: Regen.
index 8a93629..2b9ddae 100755 (executable)
@@ -1919,6 +1919,10 @@ if test x$ide_cv_os_cygwin32 = xyes; then
   ac_win_build="yes"
 fi
 
+if test x"$ac_win_build" = xyes; then
+  LIBGUI_CFLAGS="-DWIN32 $LIBGUI_CFLAGS"
+fi
+
 tmp="`cd $srcdir/library; pwd`"
 if test x"$ac_cv_prog_CC" = xcl ; then
   tmp2="`cygpath --windows $tmp`"
@@ -1931,7 +1935,7 @@ fi
 # Find the init.tcl file.
 
 echo $ac_n "checking for init.tcl""... $ac_c" 1>&6
-echo "configure:1935: checking for init.tcl" >&5
+echo "configure:1939: checking for init.tcl" >&5
 if eval "test \"`echo '$''{'ac_cv_c_tcl_libdir'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -1966,7 +1970,7 @@ if test "${with_tclconfig+set}" = set; then
 fi
 
     echo $ac_n "checking for Tcl configuration script""... $ac_c" 1>&6
-echo "configure:1970: checking for Tcl configuration script" >&5
+echo "configure:1974: checking for Tcl configuration script" >&5
     if eval "test \"`echo '$''{'ac_cv_c_tclconfig'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2058,7 +2062,7 @@ if test "${with_tkconfig+set}" = set; then
 fi
 
     echo $ac_n "checking for Tk configuration script""... $ac_c" 1>&6
-echo "configure:2062: checking for Tk configuration script" >&5
+echo "configure:2066: checking for Tk configuration script" >&5
     if eval "test \"`echo '$''{'ac_cv_c_tkconfig'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2138,7 +2142,7 @@ fi
 dirlist=".. ../../ ../../../ ../../../../ ../../../../../ ../../../../../../ ../../../../../../.. ../../../../../../../.. ../../../../../../../../.. ../../../../../../../../../.."
 no_tcl=true
 echo $ac_n "checking for Tcl headers in the source tree""... $ac_c" 1>&6
-echo "configure:2142: checking for Tcl headers in the source tree" >&5
+echo "configure:2146: checking for Tcl headers in the source tree" >&5
 # Check whether --with-tclinclude or --without-tclinclude was given.
 if test "${with_tclinclude+set}" = set; then
   withval="$with_tclinclude"
@@ -2195,17 +2199,17 @@ if test x"${ac_cv_c_tclh}" = x ; then
    echo "$ac_t""none" 1>&6
    ac_safe=`echo "tcl.h" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for tcl.h""... $ac_c" 1>&6
-echo "configure:2199: checking for tcl.h" >&5
+echo "configure:2203: checking for tcl.h" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2204 "configure"
+#line 2208 "configure"
 #include "confdefs.h"
 #include <tcl.h>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:2209: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:2213: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -2269,7 +2273,7 @@ fi
 dirlist=".. ../../ ../../../ ../../../../ ../../../../../ ../../../../../../ ../../../../../../.. ../../../../../../../.. ../../../../../../../../.. ../../../../../../../../../.."
 no_tk=true
 echo $ac_n "checking for Tk headers in the source tree""... $ac_c" 1>&6
-echo "configure:2273: checking for Tk headers in the source tree" >&5
+echo "configure:2277: checking for Tk headers in the source tree" >&5
 # Check whether --with-tkinclude or --without-tkinclude was given.
 if test "${with_tkinclude+set}" = set; then
   withval="$with_tkinclude"
@@ -2362,7 +2366,7 @@ if test "${with_itclconfig+set}" = set; then
 fi
 
   echo $ac_n "checking for Itcl configuration""... $ac_c" 1>&6
-echo "configure:2366: checking for Itcl configuration" >&5
+echo "configure:2370: checking for Itcl configuration" >&5
   if eval "test \"`echo '$''{'ac_cv_c_itclconfig'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
index ba4b9b4..afe79d2 100644 (file)
@@ -59,6 +59,10 @@ if test x$ide_cv_os_cygwin32 = xyes; then
   ac_win_build="yes"
 fi
 
+if test x"$ac_win_build" = xyes; then
+  LIBGUI_CFLAGS="-DWIN32 $LIBGUI_CFLAGS"
+fi
+
 tmp="`cd $srcdir/library; pwd`"
 if test x"$ac_cv_prog_CC" = xcl ; then
   tmp2="`cygpath --windows $tmp`"
diff --git a/libgui/doc/tkTable.html b/libgui/doc/tkTable.html
new file mode 100644 (file)
index 0000000..e7c6597
--- /dev/null
@@ -0,0 +1,1907 @@
+<!-- manual page source format generated by PolyglotMan v3.0.7, -->
+<!-- available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z -->
+
+<HTML>
+<HEAD>
+<TITLE>man page(1)</TITLE>
+</HEAD>
+<BODY bgcolor=white>
+<A HREF="#toc">Table of Contents</A><P>
+_________________________________________________________________
+
+<H2><A NAME="sect0" HREF="#toc0"><B>Name</B></A></H2>
+
+<P>
+table - Create and manipulate tables
+
+<H2><A NAME="sect1" HREF="#toc1"><B>Synopsis</B></A></H2>
+
+<P>
+<B>table</B> <I>pathName</I> ?<I>options</I>?
+
+<H2><A NAME="sect2" HREF="#toc2"><B>Standard</B> <B>Options</B></A></H2>
+
+
+<DL>
+
+<DT><B>-anchor</B> </DT></DT>
+<DD>        <B>-background</B>    <B>-cursor</B>
+</DD>
+
+<DT><B>-exportselection</B> </DT></DT>
+<DD>              <B>-font</B>           <B>-foreground</B>
+</DD>
+
+<DT><B>-highlightbackground</B> </DT></DT>
+<DD>          <B>-highlightcolor</B> <B>-highlightthickness</B>
+</DD>
+
+<DT><B>-insertbackground</B> </DT></DT>
+<DD>             <B>-insertborderwidth-insertofftime</B>
+<B>-insertontime</B> <B>-insertwidth</B> <B>-invertselected</B>
+</DD>
+
+<DT><B>-relief</B> </DT></DT>
+<DD>        <B>-takefocus</B>     <B>-xscrollcommand</B>
+<B>-yscrollcommand</B>
+</DD>
+</DL>
+<P>
+See the <B>options</B> manual entry for details on the standard
+options.
+
+<H2><A NAME="sect3" HREF="#toc3"><B>Widget-specific</B> <B>Options</B></A></H2>
+
+<P>
+Command-Line Name:<B>-autoclear</B><BR>
+
+Database Name: <B>autoClear</B><BR>
+
+Database Class: <B>AutoClear</B>
+<P>
+A boolean value which specifies whether the first
+keypress in a cell will delete whatever text was
+previously there. Defaults to 0.
+<P>
+Command-Line Name:<B>-bordercursor</B><BR>
+
+Database Name: <B>borderCursor</B><BR>
+
+Database Class: <B>Cursor</B>
+<P>
+Specifies the name of the cursor to show when over
+borders, a visual indication that interactive
+resizing is allowed (it is thus affect by the value
+of -resizeborders). Defaults to <I>crosshair</I>.
+<P>
+Command-Line Name:<B>-borderwidth</B> <B>or</B> <B>-bd</B><BR>
+
+Database Name: <B>borderWidth</B><BR>
+
+Database Class: <B>BorderWidth</B>
+<P>
+Specifies a non-negative pixel value or list of
+values indicating the width of the 3-D border to
+draw on interior table cells (if such a border is
+being drawn; the <B>relief</B> option typically determines
+this). If one value is specified, a rectangle of
+this width will be drawn. If two values are specified,
+then only the left and right edges of the
+cell will have borders. If four values are specified,
+then the values correspond to the {left right
+top bottom} edges. This can be overridden by the a
+tag's borderwidth option. It can also be affected
+by the defined <B>-drawmode</B> for the table. Each value
+in the list must have one of the forms acceptable
+to <B>Tk_GetPixels</B>.
+<P>
+Command-Line Name:<B>-browsecommand</B> <B>or</B> <B>-browsecmd</B>
+Database Name: <B>browseCommand</B><BR>
+
+Database Class: <B>BrowseCommand</B>
+<P>
+Specifies a command which will be evaluated anytime
+the active cell changes. It uses the %-substition
+model described in COMMAND SUBSTITUTION below.
+<P>
+Command-Line Name:<B>-cache</B><BR>
+
+Database Name: <B>cache</B><BR>
+
+Database Class: <B>Cache</B>
+<P>
+A boolean value that specifies whether an internal
+cache of the table contents should be kept. This
+greatly enhances speed performance when used with
+<B>-command</B> but uses extra memory. Can maintain state
+when both <B>-command</B> and <B>-variable</B> are empty. The
+cache is automatically flushed whenever the value
+of <B>-cache</B> or <B>-variable</B> changes, otherwise you have
+to explicitly call <B>clear</B> on it. Defaults to off.
+<P>
+Command-Line Name:<B>-colorigin</B><BR>
+
+Database Name: <B>colOrigin</B><BR>
+
+Database Class: <B>Origin</B>
+<P>
+Specifies what column index to interpret as the
+leftmost column in the table. This value is used
+for user indices in the table. Defaults to 0.
+<P>
+Command-Line Name:<B>-cols</B><BR>
+
+Database Name: <B>cols</B><BR>
+
+Database Class: <B>Cols</B>
+<P>
+Number of cols in the table. Defaults to 10.
+<P>
+Command-Line Name:<B>-colseparator</B><BR>
+
+Database Name: <B>colSeparator</B><BR>
+
+Database Class: <B>Separator</B>
+<P>
+Specifies a separator character that will be interpreted
+as the column separator when cutting or
+pasting data in a table. By default, columns are
+separated as elements of a tcl list.
+<P>
+Command-Line Name:<B>-colstretchmode</B><BR>
+
+Database Name: <B>colStretchMode</B><BR>
+
+Database Class: <B>StretchMode</B>
+<P>
+Specifies one of the following stretch modes for
+columns to fill extra allocated window space:
+<B>none</B> Columns will not stretch to fill the
+assigned window space. If the columns are
+too narrow, there will be a blank space at
+the right of the table. This is the
+default.
+<P>
+<B>unset</B> Only columns that do not have a specific
+width set will be stretched.
+
+<DL>
+
+<DT><B>all</B> </DT></DT>
+<DD>   All columns will be stretched by the same
+number of pixels to fill the window space
+allocated to the table. This mode can
+interfere with interactive border resizing
+which tries to force column width.
+</DD>
+</DL>
+<P>
+<B>last</B> The last column will be stretched to fill
+the window space allocated to the table.
+<P>
+<B>fill</B> (only valid for <B>-rowstretch</B> currently)
+The table will get more or less columns
+according to the window space allocated to
+the table. This mode has numerous quirks
+and may disappear in the future.
+<P>
+Command-Line Name:<B>-coltagcommand</B><BR>
+
+Database Name: <B>colTagCommand</B><BR>
+
+Database Class: <B>TagCommand</B>
+<P>
+Provides the name of a procedure that will be evaluated
+by the widget to determine the tag to be used
+for a given column. When displaying a cell, the
+table widget will first check to see if a tag has
+been defined using the <B>tag</B> <B>col</B> widget method. If
+no tag is found, it will evaluate the named procedure
+passing the column number in question as the
+sole argument. The procedure is expected to return
+the name of a tag to use, or a null string. Errors
+occuring during the evaluation of the procedure, or
+the return of an invalid tag name are silently
+ignored.
+<P>
+Command-Line Name:<B>-colwidth</B><BR>
+
+Database Name: <B>colWidth</B><BR>
+
+Database Class: <B>ColWidth</B>
+<P>
+Default column width, interpreted as characters in
+the default font when the number is positive, or
+pixels if it is negative. Defaults to 10.
+<P>
+Command-Line Name:<B>-command</B><BR>
+
+Database Name: <B>command</B><BR>
+
+Database Class: <B>Command</B>
+<P>
+Specified a command to use as a procedural
+interface to cell values. If <B>-usecommand</B> is true,
+this command will be used instead of any reference
+to the <B>-variable</B> array. When retrieving cell values,
+the return value of the command is used as the
+value for the cell. It uses the %-substition model
+described in COMMAND SUBSTITUTION below.
+<P>
+Command-Line Name:<B>-drawmode</B><BR>
+
+Database Name: <B>drawMode</B><BR>
+
+Database Class: <B>DrawMode</B>
+<P>
+Sets the table drawing mode to one of the following
+options:
+<P>
+<B>slow</B> The table is drawn to an offscreen pixmap
+using the Tk bordering functions (doublebuffering).
+This means there will be no
+flashing, but this mode is slow for larger
+tables.
+<P>
+<B>compatible</B><BR>
+
+The table is drawn directly to the screen
+using the Tk border functions. It is
+faster, but the screen may flash on update.
+This is the default.
+<P>
+<B>fast</B> The table is drawn directly to the screen
+and the borders are done with fast X calls,
+so they are always one pixel wide only. As
+a side effect, it restricts <B>-borderwidth</B> to
+a range of 0 or 1. This mode provides best
+performance for large tables, but can flash
+on redraw and is not 100% Tk compatible on
+the border mode.
+<P>
+<B>single</B> The table is drawn to the screen as in fast
+mode, but only single pixel lines are drawn
+(not square borders).
+<P>
+Command-Line Name:<B>-flashmode</B><BR>
+
+Database Name: <B>flashMode</B><BR>
+
+Database Class: <B>FlashMode</B>
+<P>
+A boolean value which specifies whether cells
+should flash when their value changes. The table
+tag <B>flash</B> will be applied to these cells for the
+duration specified by <B>-flashtime</B>. Defaults to 0.
+<P>
+Command-Line Name:<B>-flashtime</B><BR>
+
+Database Name: <B>flashTime</B><BR>
+
+Database Class: <B>FlashTime</B>
+<P>
+The amount of time, in 1/4 second increments, for
+which a cell should flash when its value has
+changed. <B>-flashmode</B> must be on. Defaults to 2.
+<P>
+Command-Line Name:<B>-height</B><BR>
+
+Database Name: <B>height</B><BR>
+
+Database Class: <B>Height</B>
+<P>
+Specifies the desired height for the window, in
+rows. If zero or less, then the desired height for
+the window is made just large enough to hold all
+the rows in the table. The height can be further
+limited by <B>-maxheight</B>.
+<P>
+Command-Line Name:<B>-invertselected</B><BR>
+
+Database Name: <B>invertSelected</B><BR>
+
+Database Class: <B>InvertSelected</B>
+<P>
+Specifies whether the foreground and background of
+an item should simply have their values swapped
+instead of merging the <I>sel</I> tag options when the
+cell is selected. Defaults to 0 (merge <I>sel</I> tag).
+<P>
+Command-Line Name:<B>-ipadx</B><BR>
+
+Database Name: <B>ipadX</B><BR>
+
+Database Class: <B>Pad</B>
+<P>
+A pixel value specifying the internal offset X
+padding for text in a cell. This value does not
+grow the size of the cell, it just causes the text
+to be drawn further from the cell border. It only
+affects one side (depending on anchor). Defaults
+to 0. See <B>-padx</B> for an alternate padding style.
+<P>
+Command-Line Name:<B>-ipady</B><BR>
+
+Database Name: <B>ipadY</B><BR>
+
+Database Class: <B>Pad</B>
+<P>
+A pixel value specifying the internal offset Y
+padding for text in a cell. This value does not
+grow the size of the cell, it just causes the text
+to be drawn further from the cell border. It only
+affects one side (depending on anchor). Defaults
+to 0. See <B>-pady</B> for an alternate padding style.
+<P>
+Command-Line Name:<B>-maxheight</B><BR>
+
+Database Name: <B>maxHeight</B><BR>
+
+Database Class: <B>MaxHeight</B>
+<P>
+The max height in pixels that the window will
+request. Defaults to 600.
+<P>
+Command-Line Name:<B>-maxwidth</B><BR>
+
+Database Name: <B>maxWidth</B><BR>
+
+Database Class: <B>MaxWidth</B>
+<P>
+The max width in pixels that the window will
+request. Defaults to 800.
+<P>
+Command-Line Name:<B>-multiline</B><BR>
+
+Database Name: <B>multiline</B><BR>
+
+Database Class: <B>Multiline</B>
+<P>
+Specifies the default setting for the multiline tag
+option. Defaults to 1.
+<P>
+Command-Line Name:<B>-padx</B><BR>
+
+Database Name: <B>padX</B><BR>
+
+Database Class: <B>Pad</B>
+<P>
+A pixel value specifying the offset X padding for a
+cell. This value causes the default size of the
+cell to increase by two times the value (one for
+each side), unless a specific pixel size is chosen
+for the cell with the <B>width</B> command. This will
+force an empty area on the left and right of each
+cell edge. This padding affects all types of data
+in the cell. Defaults to 0. See <B>-ipadx</B> for an
+alternate padding style.
+<P>
+Command-Line Name:<B>-pady</B><BR>
+
+Database Name: <B>padY</B><BR>
+
+Database Class: <B>Pad</B>
+<P>
+A pixel value specifying the offset Y padding for a
+cell. This value causes the default size of the
+cell to increase by two times the value (one for
+each side), unless a specific pixel size is chosen
+for the cell with the <B>height</B> command. This will
+force an empty area on the top and bottom of each
+cell edge. This padding affects all types of data
+in the cell. Defaults to 0. See <B>-ipadx</B> for an
+alternate padding style.
+<P>
+Command-Line Name:<B>-resizeborders</B><BR>
+
+Database Name: <B>resizeBorders</B><BR>
+
+Database Class: <B>ResizeBorders</B>
+<P>
+Specifies what kind of interactive border resizing
+to allow, must be one of row, col, both (default)
+or none.
+<P>
+Command-Line Name:<B>-rowheight</B><BR>
+
+Database Name: <B>rowHeight</B><BR>
+
+Database Class: <B>RowHeight</B>
+<P>
+Default row height, interpreted as lines in the
+default font when the number is positive, or pixels
+if it is negative. Defaults to 1.
+<P>
+Command-Line Name:<B>-roworigin</B><BR>
+
+Database Name: <B>rowOrigin</B><BR>
+
+Database Class: <B>Origin</B>
+<P>
+Specifies what row index to interpret as the topmost
+row in the table. This value is used for user
+indices in the table. Defaults to 0.
+<P>
+Command-Line Name:<B>-rows</B><BR>
+
+Database Name: <B>rows</B><BR>
+
+Database Class: <B>Rows</B>
+<P>
+Number of rows in the table. Defaults to 10.
+<P>
+Command-Line Name:<B>-rowseparator</B><BR>
+
+Database Name: <B>rowSeparator</B><BR>
+
+Database Class: <B>Separator</B>
+<P>
+Specifies a separator character that will be interpreted
+as the row separator when cutting or pasting
+data in a table. By default, rows are separated as
+tcl lists.
+<P>
+Command-Line Name:<B>-rowstretchmode</B><BR>
+
+Database Name: <B>rowStretchMode</B><BR>
+
+Database Class: <B>StretchMode</B>
+<P>
+Specifies the stretch modes for rows to fill extra
+allocated window space. See <B>-colstretchmode</B> for
+valid options.
+<P>
+Command-Line Name:<B>-rowtagcommand</B><BR>
+
+Database Name: <B>rowTagCommand</B><BR>
+
+Database Class: <B>TagCommand</B>
+<P>
+Provides the name of a procedure that can evaluated
+by the widget to determine the tag to be used for a
+given row. The procedure must be defined by the
+user to accept a single argument (the row number),
+and return a tag name or null string. This operates
+in a similar manner as <B>-coltagcommand</B>, except
+that it applies to row tags.
+<P>
+Command-Line Name:<B>-selectioncommand</B> <B>or</B> <B>-selcmd</B>
+Database Name: <B>selectionCommand</B><BR>
+
+Database Class: <B>SelectionCommand</B>
+<P>
+Specifies a command to evaluate when the selection
+is retrieved from a table via the selection mechanism
+(ie: evaluating &laquo;<B>selection</B> <B>get</B>"). The return
+value from this command will become the string
+passed on by the selection mechanism. It uses the
+%-substition model described in COMMAND SUBSTITUTION
+below. If an error occurs, a Tcl background
+error is generated and nothing is returned.
+<P>
+Command-Line Name:<B>-selectmode</B><BR>
+
+Database Name: <B>selectMode</B><BR>
+
+Database Class: <B>SelectMode</B>
+<P>
+Specifies one of several styles for manipulating
+the selection. The value of the option may be
+arbitrary, but the default bindings expect it to be
+either <B>single</B>, <B>browse</B>, <B>multiple</B>, or <B>extended</B>; the
+default value is <B>browse</B>. These styles are like
+those for the Tk listbox, except expanded for 2
+dimensions.
+<P>
+Command-Line Name:<B>-selecttitle</B><BR>
+
+Database Name: <B>selectTitles</B><BR>
+
+Database Class: <B>SelectTitles</B>
+<P>
+Specifies whether title cells should be allowed in
+the selection. Defaults to 0 (disallowed).
+<P>
+Command-Line Name:<B>-selecttype</B><BR>
+
+Database Name: <B>selectType</B><BR>
+
+Database Class: <B>SelectType</B>
+<P>
+Specifies one of several types of selection for the
+table. The value of the option may be one of <B>row</B>,
+<B>col</B>, <B>cell</B>, or <B>both</B> (meaning <B>row</B> <B>&amp;&amp;</B> <B>col</B>); the
+default value is <B>cell</B>. These types define whether
+an entire row/col is affected when a cell's selection
+is changed (set or clear).
+<P>
+Command-Line Name:<B>-sparsearray</B><BR>
+
+Database Name: <B>sparseArray</B><BR>
+
+Database Class: <B>SparseArray</B>
+<P>
+A boolean value that specifies whether an associated
+Tcl array should be kept as a sparse array (1,
+the default) or as a full array (0). If true, then
+cell values that are empty will be deleted from the
+array (taking less memory). If false, then all
+values in the array will be maintained.
+<P>
+Command-Line Name:<B>-state</B><BR>
+
+Database Name: <B>state</B><BR>
+
+Database Class: <B>State</B>
+
+<DL>
+
+<DT>Specifies one of two states for the entry: </DT></DT>
+<DD><B>normal</B>
+or <B>disabled</B>. If the table is disabled then the
+value may not be changed using widget commands and
+no insertion cursor will be displayed, even if the
+input focus is in the widget. Also, all insert or
+delete methods will be ignored. Defaults to <B>normal</B>.
+</DD>
+</DL>
+<P>
+Command-Line Name:<B>-titlecols</B><BR>
+
+Database Name: <B>titleCols</B><BR>
+
+Database Class: <B>TitleCols</B>
+<P>
+Number of columns to use as a title area. Defaults
+to 0.
+<P>
+Command-Line Name:<B>-titlerows</B><BR>
+
+Database Name: <B>titleRows</B><BR>
+
+Database Class: <B>TitleRows</B>
+<P>
+Number of rows to use as a title area. Defaults to
+0.
+<P>
+Command-Line Name:<B>-usecommand</B><BR>
+
+Database Name: <B>useCommand</B><BR>
+
+Database Class: <B>UseCommand</B>
+<P>
+A boolean value which specifies whether to use the
+<B>command</B> option. This value sets itself to zero if
+<B>command</B> is used and returns an error. Defaults to
+1 (will use <B>command</B> if specified).
+<P>
+Command-Line Name:<B>-validate</B><BR>
+
+Database Name: <B>validate</B><BR>
+
+Database Class: <B>Validate</B>
+<P>
+A boolean specifying whether validation should
+occur for the active buffer. Defaults to 0.
+<P>
+Command-Line Name:<B>-validatecommand</B> <B>or</B> <B>-vcmd</B>
+Database Name: <B>validateCommand</B><BR>
+
+Database Class: <B>ValidateCommand</B>
+<P>
+Specifies a command to execute when the active cell
+is edited. This command is expected to return a
+Tcl boolean. If it returns true, then it is
+assumed the new value is OK, otherwise the new
+value is rejected (the edition will not take
+place). Errors in this command are handled in the
+background. It uses the %-substition model
+described in COMMAND SUBSTITUTION below.
+<P>
+Command-Line Name:<B>-variable</B><BR>
+
+Database Name: <B>variable</B><BR>
+
+Database Class: <B>Variable</B>
+<P>
+Global Tcl array variable to attach to the table's
+C array. It will be created if it doesn't already
+exist or is a simple variable. Keys used by the
+table in the array are of the form <I>row</I>,<I>col</I> for
+cells and the special key <I>active</I> which contains the
+value of the active cell buffer. The Tcl array is
+managed as a sparse array (the table doesn't
+require all valid indices have values). No stored
+value for an index is equivalent to the empty
+string, and clearing a cell will remove that index
+from the Tcl array, unless the <B>-sparsearray</B> options
+is set to 0.
+<P>
+Command-Line Name:<B>-width</B><BR>
+
+Database Name: <B>width</B><BR>
+
+Database Class: <B>Width</B>
+<P>
+Specifies the desired width for the window, in
+columns. If zero or less, then the desired width
+for the window is made just large enough to hold
+all the columns in the table. The width can be
+further limited by <B>-maxwidth</B>.
+<P>
+Command-Line Name:<B>-wrap</B><BR>
+
+Database Name: <B>wrap</B><BR>
+
+Database Class: <B>Wrap</B>
+<P>
+Specifies the default wrap value for tags.
+Defaults to 0.<BR>
+
+_________________________________________________________________
+
+<H2><A NAME="sect4" HREF="#toc4"><B>Description</B></A></H2>
+
+<P>
+The <B>table</B> command creates a 2-dimensional grid of cells.
+The table can use a Tcl array variable or Tcl command for
+data storage and retrieval. The widget has an active
+cell, the contents of which can be edited (when the state
+is normal). The widget supports a default style for the
+cells and also multiple <I>tags</I>, which can be used to change
+the style of a row, column or cell (see TAGS for details).
+A cell <I>flash</I> can be set up so that changed cells will
+change color for a specified amount of time ("blink").
+Cells can have embedded images or windows, as described in
+TAGS and &laquo;EMBEDDED WINDOWS&raquo; respectively.
+<P>
+One or more cells may be selected as described below. If
+a table is exporting its selection (see <B>-exportselection</B>
+option), then it will observe the standard X11 protocols
+for handling the selection. See THE SELECTION for
+details.
+<P>
+It is not necessary for all the cells to be displayed in
+the table window at once; commands described below may be
+used to change the view in the window. Tables allow
+scrolling in both directions using the standard <B>-xscrollcommand</B>
+and <B>-yscrollcommand</B> options. They also support
+scanning, as described below.
+<P>
+In order to obtain good performance, the table widget supports
+multiple drawing modes, two of which are fully Tk
+compatible.
+
+<H2><A NAME="sect5" HREF="#toc5"><B>Initialization</B></A></H2>
+
+<P>
+When the <B>table</B> command is loaded into an interpreter, a
+built-in Tcl command, <B>tkTableInit</B>, is evaluated. This
+will search for the appropriate table binding init file to
+load. The directories searched are those in <I>$tcl</I><B>_</B><I>pkgPath</I>,
+both with Tktable(version) appended and without,
+<I>$tk</I><B>_</B><I>library</I> and <I>[pwd]</I> (the current directory). You can
+also define an <I>$env(TK</I><B>_</B><I>TABLE</I><B>_</B><I>LIBRARY)</I> to head this search
+list. By default, the file searched for is called
+<B>tkTable.tcl</B>, but this can be overridden by setting
+<I>$env(TK</I><B>_</B><I>TABLE</I><B>_</B><I>LIBRARY</I><B>_</B><I>FILE)</I>.
+<P>
+This entire init script can be overridden by providing
+your own <B>tkTableInit</B> procedure before the library is
+loaded.         Otherwise,        the       aforementioned
+<I>env(TK</I><B>_</B><I>TABLE</I><B>_</B><I>LIBRARY)</I> variable will be set with the directory
+in which <I>$env(TK</I><B>_</B><I>TABLE</I><B>_</B><I>LIBRARY</I><B>_</B><I>FILE)</I> was found.
+
+<H2><A NAME="sect6" HREF="#toc6"><B>Indices</B></A></H2>
+
+<P>
+Many of the widget commands for tables take one or more
+indices as arguments. An index specifies a particular
+cell of the table, in any of the following ways:
+<P>
+<I>number,number</I><BR>
+
+Specifies the cell as a numerical index of
+row,col which corresponds to the index of the
+associated Tcl array, where <B>-roworigin,-colorigin</B>
+corresponds to the first cell in the
+table (0,0 by default).
+
+<DL>
+
+<DT><B>active</B> </DT></DT>
+<DD>     Indicates the cell that has the location cursor.
+It is specified with the <B>activate</B> widget
+command.
+</DD>
+
+<DT><B>anchor</B> </DT></DT>
+<DD>     Indicates the anchor point for the selection,
+which is set with the <B>selection</B> <B>anchor</B> widget
+command.
+</DD>
+</DL>
+<P>
+<B>bottomright</B> Indicates the bottom-rightmost cell visible in
+the table.
+
+<DL>
+
+<DT><B>end</B> </DT></DT>
+<DD>        Indicates the bottom right cell of the table.
+</DD>
+
+<DT><B>origin</B> </DT></DT>
+<DD>     Indicates the top-leftmost editable cell of
+the table, not necessarily in the display.
+This takes into account the user specified
+origin and title area.
+</DD>
+
+<DT><B>topleft</B> </DT></DT>
+<DD>    Indicates the top-leftmost editable cell visible
+in the table (this excludes title cells).
+</DD>
+
+<DT><B>@</B><I>x</I><B>,</B><I>y</I> </DT></DT>
+<DD>       Indicates the cell that covers the point in
+the table window specified by <I>x</I> and <I>y</I> (in
+pixel coordinates). If no cell covers that
+point, then the closest cell to that point is
+used.
+</DD>
+</DL>
+<P>
+In the widget command descriptions below, arguments named
+<I>index</I>, <I>first</I>, and <I>last</I> always contain text indices in one
+of the above forms.
+
+<H2><A NAME="sect7" HREF="#toc7"><B>Tags</B></A></H2>
+
+<P>
+A tag is a textual string that is associated with zero or
+more rows, columns or cells in a table. Tags may contain
+arbitrary characters, but it is probably best to avoid
+using names which look like indices to reduce coding confusion.
+There may be any number of tags in a table, but
+each row, column or cell can only have one tag associated
+with it at a time. There are several permanent tags in
+each table that can be configured by the user and will
+determine the attributes for special cells:
+
+<DL>
+
+<DT><B>active</B> </DT></DT>
+<DD>   This tag is given to the <I>active</I> cell
+</DD>
+
+<DT><B>flash</B> </DT></DT>
+<DD>    If flash mode is on, this tag is given to
+any recently edited cells.
+</DD>
+
+<DT><B>sel</B> </DT></DT>
+<DD>      This tag is given to any selected cells.
+</DD>
+
+<DT><B>title</B> </DT></DT>
+<DD>    This tag is given to any cells in the
+title rows and columns. This tag has
+<B>-state</B> <I>disabled</I> by default.
+</DD>
+</DL>
+<P>
+Tags control the way cells are displayed on the screen.
+Where appropriate, the default for displaying cells is
+determined by the options for the table widget. However,
+display options may be associated with individual tags
+using the ``<I>pathName</I> <B>tag</B> <B>configure</B>'' widget command. If a
+cell, row or column has been tagged, then the display
+options associated with the tag override the default display
+style. The following options are currently supported
+for tags:
+
+<DL>
+
+<DT><B>-anchor</B> <I>anchor</I></DT></DT>
+<DD>
+Anchor for item in the cell space.
+</DD>
+
+<DT><B>-background</B> or <B>-bg</B> <I>color</I></DT></DT>
+<DD>
+Background color of the cell.
+</DD>
+
+<DT><B>-borderwidth</B> or <B>-bd</B> <I>pixelList</I></DT></DT>
+<DD>
+Borderwidth of the cell, of the same format
+for the table, but may also be empty to
+inherit the default table borderwidth value
+(the default).
+</DD>
+
+<DT><B>-font</B> <I>fontName</I></DT></DT>
+<DD>
+Font for text in the cell.
+</DD>
+
+<DT><B>-foreground</B> or <B>-fg</B> <I>color</I></DT></DT>
+<DD>
+Foreground color of the cell.
+</DD>
+
+<DT><B>-justify</B> <I>justify</I></DT></DT>
+<DD>
+How to justify multi-line text in a cell.
+It must be one of <B>left</B>, <B>right</B>, or <B>center</B>.
+</DD>
+
+<DT><B>-image</B> <I>imageName</I></DT></DT>
+<DD>
+An image to display in the cell instead of
+text.
+</DD>
+
+<DT><B>-multiline</B> <I>boolean</I></DT></DT>
+<DD>
+Whether to display text with newlines on
+multiple lines.
+</DD>
+
+<DT><B>-relief</B> <I>relief</I></DT></DT>
+<DD>
+The relief for the cell.
+</DD>
+
+<DT><B>-showtext</B> <I>boolean</I></DT></DT>
+<DD>
+Whether to show the text over an image.
+</DD>
+
+<DT><B>-state</B> <I>state</I></DT></DT>
+<DD>
+The state of the cell, to allow for certain
+cells to be disabled. This prevents the
+cell from being edited by the <I>insert</I> or
+<I>delete</I> methods, but a direct <I>set</I> will not be
+prevented.
+</DD>
+
+<DT><B>-wrap</B> <I>boolean</I></DT></DT>
+<DD>
+Whether characters should wrap in a cell
+that is not wide enough.
+</DD>
+</DL>
+<P>
+A priority order is defined among tags based on creation
+order (first created tag has highest default priority),
+and this order is used in implementing some of the
+tag-related functions described below. When a cell is
+displayed, its properties are determined by the tags which
+are assigned to it. The priority of a tag can be modified
+by the ``<I>pathName</I> <B>tag</B> <B>lower</B>'' and ``<I>pathName</I> <B>tag</B> <B>raise</B>''
+widget commands.
+<P>
+If a cell has several tags associated with it that define
+the same display options (eg - a <B>title</B> cell with specific
+<B>row</B> and <B>cell</B> tags), then the options of the highest priority
+tag are used. If a particular display option hasn't
+been specified for a particular tag, or if it is specified
+as an empty string, then that option will not be used; the
+next-highest-priority tag's option will be used instead.
+If no tag specifies a particular display option, then the
+default style for the widget will be used.
+<P>
+Images are used for display purposes only. Editing in
+that cell will still be enabled and any querying of the
+cell will show the text value of the cell, regardless of
+the value of <B>-showtext</B>.
+
+<H2><A NAME="sect8" HREF="#toc8"><B>Embedded</B> <B>Windows</B></A></H2>
+
+<P>
+There may be any number of embedded windows in a table
+widget (one per cell), and any widget may be used as an
+embedded window (subject to the usual rules for geometry
+management, which require the table window to be the parent
+of the embedded window or a descendant of its parent).
+The embedded window's position on the screen will be
+updated as the table is modified or scrolled, and it will
+be mapped and unmapped as it moves into and out of the
+visible area of the table widget. Each embedded window
+occupies one cell's worth of space in the table widget,
+and it is referred to by the index of the cell in the
+table. Windows associated with the table widget are
+destroyed when the table widget is destroyed.
+<P>
+Windows are used for display purposes only. A value still
+exists for that cell, but will not be shown unless the
+window is deleted in some way. If the window is destroyed
+or lost by the table widget to another geometry manager,
+then any data associated with it is lost (the cell it
+occupied will no longer appear in <B>window</B> <B>names</B>).
+<P>
+When an embedded window is added to a table widget with
+the window configure widget command, several configuration
+options may be associated with it. These options may be
+modified with later calls to the window configure widget
+command. The following options are currently supported:
+
+<DL>
+
+<DT><B>-create</B> <I>script</I></DT></DT>
+<DD>
+NOT CURRENTLY SUPPORTED. Specifies a Tcl
+script that may be evaluated to create the
+window for the annotation. If no -window
+option has been specified for this cell then
+this script will be evaluated when the cell
+is about to be displayed on the screen.
+Script must create a window for the cell and
+return the name of that window as its
+result. If the cell's window should ever be
+deleted, the script will be evaluated again
+the next time the cell is displayed.
+</DD>
+
+<DT><B>-background</B> or <B>-bg</B> <I>color</I></DT></DT>
+<DD>
+Background color of the cell. If not specified,
+it uses the table's default background.
+</DD>
+
+<DT><B>-borderwidth</B> or <B>-bd</B> <I>pixelList</I></DT></DT>
+<DD>
+Borderwidth of the cell, of the same format
+for the table, but may also be empty to
+inherit the default table borderwidth value
+(the default).
+</DD>
+
+<DT><B>-padx</B> <I>pixels</I></DT></DT>
+<DD>
+As defined in the Tk options man page.
+</DD>
+
+<DT><B>-pady</B> <I>pixels</I></DT></DT>
+<DD>
+As defined in the Tk options man page.
+</DD>
+
+<DT><B>-relief</B> <I>relief</I></DT></DT>
+<DD>
+The relief to use for the cell in which the
+window lies. If not specified, it uses the
+table's default relief.
+</DD>
+
+<DT><B>-sticky</B> <I>sticky</I></DT></DT>
+<DD>
+Stickiness of the window inside the cell, as
+defined by the <B>grid</B> command.
+</DD>
+
+<DT><B>-window</B> <I>pathName</I></DT></DT>
+<DD>
+Specifies the name of a window to display in
+the annotation. It must exist before being
+specified here.
+</DD>
+</DL>
+
+<H2><A NAME="sect9" HREF="#toc9"><B>the</B> <B>Selection</B></A></H2>
+
+<P>
+Table selections are available as type STRING.    By
+default, the value of the selection will be the values of
+the selected cells in nested Tcl list form where each row
+is a list and each column is an element of a row list.
+You can change the way this value is interpreted by setting
+the <B>-rowseparator</B> and <B>-colseparator</B> options. For
+example, default Excel format would be to set <B>-rowseparator</B>
+to &laquo;\n&raquo; and <B>-colseparator</B> to &laquo;\t". Changing these
+values affects both how the table sends out the selection
+and reads in pasted data, ensuring that the table should
+always be able to cut and paste to itself. It is possible
+to change how pastes are handled by editing the table
+library procedure <B>tk_tablePasteHandler</B>. This might be
+necessary if <B>-selectioncommand</B> is set.
+
+<H2><A NAME="sect10" HREF="#toc10"><B>Row/Col</B> <B>Spanning</B></A></H2>
+
+<P>
+Individual cells can span multiple rows and/or columns.
+This is done via the <B>spans</B> command (see below for exact
+arguments). Cells in the title area that span are not
+permitted to span beyond the title area, and will be constrained
+accordingly. If the title area shrinks during a
+configure, sanity checking will occur to ensure the above.
+You may set spans on regular cells that extend beyond the
+defined row/col area. These spans will not be constrained,
+so that when the defined row/col area expands,
+the span will expand with it.
+<P>
+When setting a span, checks are made as to whether the
+span would overlap an already spanning or hidden cell.
+This is an error and it not allowed. Spans can affect the
+overall speed of table drawing, although not significantly.
+If spans are not used, then there is no performance
+loss.
+<P>
+Cells <I>hidden</I> by spanning cells still have valid data.
+This will be seen during cut and paste operations that
+involve hidden cells, or through direct access by a command
+like <B>get</B> or <B>set</B>.
+<P>
+The drawing properties of spanning cells apply to only the
+visual area of the cell. For example, if a cell is center
+justified over 5 columns, then when viewing any portion of
+those columns, it will appear centered in the visible
+area. The non-visible column area will not be considered
+in the centering calculations.
+
+<H2><A NAME="sect11" HREF="#toc11"><B>Command</B> <B>Substitution</B></A></H2>
+
+<P>
+The various option based commands that the table supports
+all support the familiar Tk %-substitution model (see <B>bind</B>
+for more details). The following %-sequences are recognized
+and substituted by the table widget:
+<P>
+<B>%c</B> For <B>SelectionCommand</B>, it is the maximum number of
+columns in any row in the selection. Otherwise it is
+the column of the triggered cell.
+
+<DL>
+
+<DT><B>%C</B> </DT></DT>
+<DD>A convenience substitution for <I>%r</I>,<I>%c</I>.
+</DD>
+
+<DT><B>%i</B> </DT></DT>
+<DD>For <B>SelectionCommand</B>, it is the total number of cells
+in the selection. For <B>Command</B>, it is 0 for a read
+(get) and 1 for a write (set). Otherwise it is the
+current cursor position in the cell.
+</DD>
+
+<DT><B>%r</B> </DT></DT>
+<DD>For <B>SelectionCommand</B>, it is the number of rows in the
+selection. Otherwise it is the row of the triggered
+cell.
+</DD>
+</DL>
+<P>
+<B>%s</B> For <B>ValidateCommand</B>, it is the current value of the
+cell being validated. For <B>SelectionCommand</B>, it is
+the default value of the selection. For <B>BrowseCommand</B>,
+it is the index of the last active cell. For
+<B>Command</B>, it is empty for reads (get) and the current
+value of the cell for writes (set).
+
+<DL>
+
+<DT><B>%S</B> </DT></DT>
+<DD>For <B>ValidateCommand</B>, it is the potential new value of
+the cell being validated. For <B>BrowseCommand</B>, it is
+the index of the new active cell.
+</DD>
+</DL>
+<P>
+<B>%W</B> The pathname to the window for which the command was
+generated.
+
+<H2><A NAME="sect12" HREF="#toc12"><B>Widget</B> <B>Command</B></A></H2>
+
+<P>
+The <B>table</B> command creates a new Tcl command whose name is
+<I>pathName</I>. This command may be used to invoke various
+operations on the widget. It has the following general
+form:<BR>
+
+<I>pathName</I> <I>option</I> ?<I>arg</I> <I>arg</I> <I>...</I>?<BR>
+
+<I>Option</I> and the <I>arg</I>s determine the exact behavior of the
+command.
+<P>
+The following commands are possible for <B>table</B> widgets:
+<P>
+<I>pathName</I> <B>activate</B> <I>index</I><BR>
+
+Sets the active cell to the one indicated by <I>index</I>.
+<P>
+<I>pathName</I> <B>bbox</B> <I>first</I> ?<I>last</I>?<BR>
+
+It returns the bounding box for the specified cell
+(range) as a 4-tuple of x, y, width and height in
+pixels. It clips the box to the visible portion,
+if any, otherwise an empty string is returned.
+<P>
+<I>pathName</I> <B>border</B> <I>option</I> <I>args</I><BR>
+
+This command is a voodoo hack to implement border
+sizing for tables. This is normally called through
+bindings, with the following as valid options:
+<P>
+<I>pathName</I> <B>border</B> <B>mark</B> <I>x</I> <I>y</I> ?<I>row|col</I>?
+Records <I>x</I> and <I>y</I> and the row and/or column
+border under that point in the table window,
+if any; used in conjunction with later <B>border</B>
+<B>dragto</B> commands. Typically this command
+is associated with a mouse button press in
+the widget. If <I>row</I> or <I>col</I> is not specified,
+it returns a tuple of both border indices
+(an empty item means no border). Otherwise,
+just the specified item is returned.
+<P>
+<I>pathName</I> <B>border</B> <B>dragto</B> <I>x</I> <I>y</I><BR>
+
+This command computes the difference between
+its <I>x</I> and <I>y</I> arguments and the <I>x</I> and <I>y</I> arguments
+to the last <B>border</B> <B>mark</B> command for
+the widget. It then adjusts the previously
+marked border by the difference. This command
+is typically associated with mouse
+motion events in the widget, to produce the
+effect of interactive border resizing.
+<P>
+<I>pathName</I> <B>cget</B> <I>option</I><BR>
+
+Returns the current value of the configuration
+option given by <I>option</I>. <I>Option</I> may have any of the
+values accepted by the <B>table</B> command.
+<P>
+<I>pathName</I> <B>clear</B> <I>option</I> ?<I>first</I>? ?<I>last</I>?<BR>
+
+This command is a convenience routine to clear certain
+state information managed by the table. <I>first</I>
+and <I>last</I> represent valid table indices. If neither
+are specified, then the command operates on the
+whole table. The following options are recognized:
+<P>
+<I>pathName</I> <B>clear</B> <B>cache</B> ?<I>first</I>? ?<I>last</I>?
+Clears the specified section of the cache,
+if the table has been keeping one.
+<P>
+<I>pathName</I> <B>clear</B> <B>sizes</B> ?<I>first</I>? ?<I>last</I>?
+Clears the specified row and column areas of
+specific height/width dimensions. When just
+one index is specified, for example <B>2,0</B>,
+that is interpreted as row 2 <B>and</B> column 0.
+<P>
+<I>pathName</I> <B>clear</B> <B>tags</B> ?<I>first</I>? ?<I>last</I>?
+Clears the specified area of tags (all row,
+column and cell tags).
+<P>
+<I>pathName</I> <B>clear</B> <B>all</B> ?<I>first</I>? ?<I>last</I>?
+Performs all of the above clear functions on
+the specified area.
+<P>
+<I>pathName</I> <B>configure</B> ?<I>option</I>? ?<I>value</I> <I>option</I> <I>value</I> <I>...</I>?
+Query or modify the configuration options of the
+widget. If no <I>option</I> is specified, returns a list
+describing all of the available options for <I>path</I><B>_N</B><I>ame</I>
+(see <B>Tk_ConfigureInfo</B> for information on the
+format of this list). If <I>option</I> is specified with
+no <I>value</I>, then the command returns a list describing
+the one named option (this list will be identical
+to the corresponding sublist of the value
+returned if no <I>option</I> is specified). If one or
+more <I>option-value</I> pairs are specified, then the
+command modifies the given widget option(s) to have
+the given value(s); in this case the command
+returns an empty string. <I>Option</I> may have any of
+the values accepted by the <B>table</B> command.
+<P>
+<I>pathName</I> <B>curselection</B> ?<I>value</I>?<BR>
+
+With no arguments, it returns the sorted indices of
+the currently selected cells. Otherwise it sets
+all the selected cells to the given value. The set
+has no effect if there is no associated Tcl array
+or the state is disabled.
+<P>
+<I>pathName</I> <B>curvalue</B> ?<I>value</I>?<BR>
+
+If no value is given, the value of the cell being
+edited (indexed by <B>active</B>) is returned, else it is
+set to the given value.
+<P>
+<I>pathName</I> <B>delete</B> <I>option</I> <I>arg</I> ?<I>arg</I>?<BR>
+
+This command is used to delete various things in a
+table. It has several forms, depending on the
+<I>option</I>:
+<I>pathName</I> <B>delete</B> <B>active</B> <I>index</I> ?<I>index</I>?
+Deletes text from the active cell. If only
+one index is given, it deletes the character
+after that index, otherwise it deletes from
+the first index to the second. <I>index</I> can be
+a number, <B>insert</B> or <B>end</B>.
+<P>
+<I>pathName</I> <B>delete</B> <B>cols</B> ?<I>switches</I>? <I>index</I> ?<I>count</I>?
+Deletes <I>count</I> cols starting at (and including)
+col <I>index</I>. The <I>index</I> will be constrained
+to the limits of the tables. If
+<I>count</I> is negative, it deletes cols to the
+left. Otherwise it deletes cols to the
+right. <I>count</I> defaults to 1 (meaning just
+the column specified). At the moment, spans
+are not adjusted with this action. Optional
+switches are:
+
+<DL>
+
+<DT><B>-holddimensions</B></DT></DT>
+<DD>
+Causes the table cols to be unaffected
+by the deletion (empty cols
+may appear). By default the dimensions
+are adjusted by <B>count</B>.
+</DD>
+
+<DT><B>-holdselection</B></DT></DT>
+<DD>
+Causes the selection to be maintained
+on the absolute cells values.
+Otherwise, the selection will be
+cleared..
+</DD>
+
+<DT><B>-holdtags</B></DT></DT>
+<DD>
+Causes the tags specified by the <I>tag</I>
+method to not move along with the
+data. Also prevents specific widths
+set by the <I>width</I> method from being
+adjusted. By default, these tags
+are properly adjusted.
+</DD>
+
+<DT><B>-holdwindows</B></DT></DT>
+<DD>
+Causes the embedded windows created
+with the <I>window</I> method to not move
+along with the data. By default,
+these windows are properly adjusted.
+</DD>
+
+<DT><B>-keeptitles</B></DT></DT>
+<DD>
+Prevents title area cells from being
+changed. Otherwise they are treated
+just like regular cells and will
+move as specified.
+</DD>
+
+<DT><B>--</B> </DT></DT>
+<DD>    Signifies the end of the switches.
+</DD>
+</DL>
+<P>
+<I>pathName</I> <B>delete</B> <B>rows</B> ?<I>switches</I>? <I>index</I> ?<I>count</I>?
+Deletes <B>count</B> rows starting at (and
+including) row <B>index</B>. If <B>count</B> is negative,
+it deletes rows going up. Otherwise it
+deletes rows going down. The selection will
+be cleared. The switches are the same as
+those for column deletion.
+<P>
+<I>pathName</I> <B>get</B> <I>first</I> ?<I>last</I>?<BR>
+
+Returns the value of the cells specified by the
+table indices <I>first</I> and (optionally) <I>last</I> in a
+list.
+<P>
+<I>pathName</I> <B>height</B> ?<I>row</I>? ?<I>value</I> <I>row</I> <I>value</I> <I>...</I>?
+If no <I>row</I> is specified, returns a list describing
+all rows for which a height has been set. If <B>row</B>
+is specified with no value, it prints out the
+height of that row in characters (positive number)
+or pixels (negative number). If one or more
+<I>row-value</I> pairs are specified, then it sets each
+row to be that height in lines (positive number) or
+pixels (negative number). If <I>value</I> is <I>default</I>,
+then the row uses the default height, specified by
+<B>-rowheight</B>.
+<P>
+<I>pathName</I> <B>hidden</B> ?<I>index</I>? ?<I>index</I> <I>...</I>?<BR>
+
+When called without args, it returns all the <I>hidden</I>
+cells (those cells covered by a spanning cell). If
+one index is specified, it returns the spanning
+cell covering that index, if any. If multiple
+indices are specified, it returns 1 if all indices
+are hidden cells, 0 otherwise.
+<P>
+<I>pathName</I> <B>icursor</B> ?<I>arg</I>?<BR>
+
+With no arguments, prints out the location of the
+insertion cursor in the active cell. With one
+argument, sets the cursor to that point in the
+string. 0 is before the first character, you can
+also use <B>insert</B> or <B>end</B> for the current insertion
+point or the end of the text. If there is no
+active cell, or the cell or table is disabled, this
+will return -1.
+<P>
+<I>pathName</I> <B>index</B> <I>index</I> ?<I>row|col</I>?<BR>
+
+Returns the integer cell coordinate that corresponds
+to <I>index</I> in the form row,col. If <B>row</B> or <B>col</B>
+is specified, then only the row or column index is
+returned.
+<P>
+<I>pathName</I> <B>insert</B> <I>option</I> <I>arg</I> <I>arg</I><BR>
+
+This command is used to into various things into a
+table. It has several forms, depending on the
+<I>option</I>:
+<P>
+<I>pathName</I> <B>insert</B> <B>active</B> <I>index</I> <I>value</I>
+The <I>value</I> is a text string which is inserted
+at the <I>index</I> postion of the active cell.
+The cursor is then positioned after the new
+text. <I>index</I> can be a number, <B>insert</B> or <B>end</B>.
+<P>
+<I>pathName</I> <B>insert</B> <B>cols</B> ?<I>switches</I>? <I>index</I> ?<I>count</I>?
+Inserts <B>count</B> cols starting at col <B>index</B>.
+If <B>count</B> is negative, it inserts before the
+specified col. Otherwise it inserts after
+the specified col. The selection will be
+cleared. The switches are the same as those
+for column deletion.
+<P>
+<I>pathName</I> <B>insert</B> <B>rows</B> ?<I>switches</I>? <I>index</I> ?<I>count</I>?
+Inserts <B>count</B> rows starting at row <B>index</B>.
+If <B>count</B> is negative, it inserts before the
+specified row. Otherwise it inserts after
+the specified row. The selection will be
+cleared. The switches are the same as those
+for column deletion.
+<P>
+<I>pathName</I> <B>reread</B><BR>
+
+Rereads the old contents of the cell back into the
+editing buffer. Useful for a key binding when
+&lt;Escape&gt; is pressed to abort the edit (a default
+binding).
+<P>
+<I>pathName</I> <B>scan</B> <I>option</I> <I>args</I><BR>
+
+This command is used to implement scanning on
+tables. It has two forms, depending on <I>option</I>:
+<P>
+<I>pathName</I> <B>scan</B> <B>mark</B> <I>x</I> <I>y</I><BR>
+
+Records <I>x</I> and <I>y</I> and the current view in the
+table window; used in conjunction with
+later <B>scan</B> <B>dragto</B> commands. Typically this
+command is associated with a mouse button
+press in the widget. It returns an empty
+string.
+<P>
+<I>pathName</I> <B>scan</B> <B>dragto</B> <I>x</I> <I>y</I>.<BR>
+
+This command computes the difference between
+its <I>x</I> and <I>y</I> arguments and the <I>x</I> and <I>y</I> arguments
+to the last <B>scan</B> <B>mark</B> command for the
+widget. It then adjusts the view by 5 times
+the difference in coordinates. This command
+is typically associated with mouse motion
+events in the widget, to produce the effect
+of dragging the list at high speed through
+the window. The return value is an empty
+string.
+<P>
+<I>pathName</I> <B>see</B> <I>index</I><BR>
+
+Adjust the view in the table so that the cell given
+by <I>index</I> is positioned as the cell one off from top
+left (excluding title rows and columns) if the cell
+is not currently visible on the screen. The actual
+cell may be different to keep the screen full.
+<P>
+<I>pathName</I> <B>selection</B> <I>option</I> <I>arg</I><BR>
+
+This command is used to adjust the selection within
+a table. It has several forms, depending on
+<I>option</I>:
+<P>
+<I>pathName</I> <B>selection</B> <B>anchor</B> <I>index</I><BR>
+
+Sets the selection anchor to the cell given
+by <I>index</I>. The selection anchor is the end
+of the selection that is fixed while dragging
+out a selection with the mouse. The
+index <B>anchor</B> may be used to refer to the
+anchor cell.
+<P>
+<I>pathName</I> <B>selection</B> <B>clear</B> <I>first</I> ?<I>last</I>?
+If any of the cells between <I>first</I> and <I>last</I>
+(inclusive) are selected, they are deselected.
+The selection state is not changed
+for cells outside this range. <I>first</I> may be
+specified as <B>all</B> to remove the selection
+from all cells.
+<P>
+<I>pathName</I> <B>selection</B> <B>includes</B> <I>index</I>
+Returns 1 if the cell indicated by <I>index</I> is
+currently selected, 0 if it isn't.
+<P>
+<I>pathName</I> <B>selection</B> <B>set</B> <I>first</I> ?<I>last</I>?
+Selects all of the cells in the range
+between <I>first</I> and <I>last</I>, inclusive, without
+affecting the selection state of cells outside
+that range.
+<P>
+<I>pathName</I> <B>set</B> ?<I>row|col</I>? <I>index</I> ?<I>value</I>? ?<I>index</I> <I>value</I> <I>...</I>?
+Sets the specified index to the associated value.
+Table validation will not be triggered via this
+method. If <B>row</B> or <B>col</B> precedes the list of
+index/value pairs, then the value is assumed to be
+a Tcl list whose values will be split and set into
+the subsequent columns (if <B>row</B> is specified) or
+rows (for <B>col</B>). For example, <B>set</B> <B>row</B> <B>2,3</B> <B>{2,3</B> <B>2,4</B>
+<B>2,5}</B> will set 3 cells, from 2,3 to 2,5. The setting
+of cells is silently bounded by the known
+table dimensions.
+<P>
+<I>pathName</I> <B>spans</B> ?<I>index</I>? ?<I>rows,cols</I> <I>index</I> <I>rows,cols</I> <I>...</I>?
+This command is used to manipulate row/col spans.
+When called with no arguments, all known spans are
+returned as a list of tuples of the form {index
+span}. When called with only the <I>index</I>, the span
+for that <I>index</I> only is returned, if any. Otherwise
+an even number of <I>index</I> <I>rows,cols</I> pairs are used to
+set spans. A span starts at the <I>index</I> and
+continues for the specified number of rows and
+cols. Negative spans are not supported. A span of
+0,0 unsets any span on that cell. See EXAMPLES for
+more info.
+<P>
+<I>pathName</I> <B>tag</B> option ?<I>arg</I> <I>arg</I> <I>...</I>?<BR>
+
+This command is used to manipulate tags. The exact
+behavior of the command depends on the <I>option</I> argument
+that follows the <B>tag</B> argument. <I>cget</I>, <I>cell</I>,
+and <I>row|col</I> complain about unknown tag names. The
+following forms of the command are currently supported:
+<P>
+<I>pathName</I> <B>tag</B> <B>cell</B> <I>tagName</I> <I>?index</I> <I>...?</I>
+With no arguments, prints out the list of
+cells that use the <I>tag</I>. Otherwise it sets
+the specified cells to use the named tag,
+replacing any tag that may have been set
+using this method before. If <I>tagName</I> is {},
+the cells are reset to the default <I>tag</I>.
+Tags added during -*tagcommand evaluation do
+not register here. If <I>tagName</I> does not
+exist, it will be created with the default
+options.
+<P>
+<I>pathName</I> <B>tag</B> <B>cget</B> <I>tagName</I> <I>option</I>
+This command returns the current value of
+the option named <I>option</I> associated with the
+tag given by <I>tagName</I>. <I>Option</I> may have any
+of the values accepted by the <B>tag</B> <B>configure</B>
+widget command.
+<P>
+<I>pathName</I> <B>tag</B> <B>col</B> <I>tagName</I> <I>?col</I> <I>...?</I>
+With no arguments, prints out the list of
+cols that use the <I>tag</I>. Otherwise it sets
+the specified columns to use the named tag,
+replacing any tag that may have been set
+using this method before. If <I>tagName</I> is {},
+the cols are reset to the default <I>tag</I>. Tags
+added during -coltagcommand evaluation do
+not register here. If <I>tagName</I> does not
+exist, it will be created with the default
+options.
+<P>
+<I>pathName</I> <B>tag</B> <B>configure</B> <I>tagName</I> ?<I>option</I>? ?<I>value</I>?
+?<I>option</I> <I>value</I> <I>...</I>?<BR>
+
+This command is similar to the <B>configure</B>
+widget command except that it modifies
+options associated with the tag given by
+<I>tagName</I> instead of modifying options for the
+overall table widget. If no <I>option</I> is specified,
+the command returns a list describing
+all of the available options for <I>tagName</I>
+(see <B>Tk_ConfigureInfo</B> for information on the
+format of this list). If <I>option</I> is specified
+with no <I>value</I>, then the command returns
+a list describing the one named option (this
+list will be identical to the corresponding
+sublist of the value returned if no <I>option</I>
+is specified). If one or more <I>option-value</I>
+pairs are specified, then the command modifies
+the given option(s) to have the given
+value(s) in <I>tagName</I>; in this case the command
+returns an empty string. See TAGS
+above for details on the options available
+for tags.
+<P>
+<I>pathName</I> <B>tag</B> <B>delete</B> <I>tagName</I><BR>
+
+Deletes a tag. No error if the tag does not
+exist.
+<P>
+<I>pathName</I> <B>tag</B> <B>exists</B> <I>tagName</I><BR>
+
+Returns 1 if the named tag exists, 0 otherwise.
+<P>
+<I>pathName</I> <B>tag</B> <B>includes</B> <I>tagName</I> <I>index</I>
+Returns 1 if the specified index has the
+named tag, 0 otherwise.
+<P>
+<I>pathName</I> <B>tag</B> <B>lower</B> <I>tagName</I> ?<I>belowThis</I>?
+Lower the priority of the named tag. If
+<I>belowThis</I> is not specified, then the tag's
+priority is lowered to the bottom, otherwise
+it is lowered to one below <I>belowThis</I>.
+<P>
+<I>pathName</I> <B>tag</B> <B>names</B> ?<I>pattern</I>?<BR>
+
+If no pattern is specified, shows the names
+of all defined tags. Otherwise the <I>pattern</I>
+is used as a glob pattern to show only tags
+matching that pattern. Tag names are
+returned in priority order (highest priority
+tag first).
+<P>
+<I>pathName</I> <B>tag</B> <B>raise</B> <I>tagName</I> ?<I>aboveThis</I>?
+Raise the priority of the named tag. If
+<I>aboveThis</I> is not specified, then the tag's
+priority is raised to the top, otherwise it
+is raised to one above <I>aboveThis</I>.
+<P>
+<I>pathName</I> <B>tag</B> <B>row</B> <I>tagName</I> ?<I>row</I> <I>...</I>?
+With no arguments, prints out the list of
+rows that use the <I>tag</I>. Otherwise it sets
+the specified columns to use the named tag,
+replacing any tag that may have been set
+using this method before. If <I>tagName</I> is {},
+the rows are reset to use the default tag.
+Tags added during -rowtagcommand evaluation
+do not register here. If <I>tagName</I> does not
+exist, it will be created with the default
+options.
+<P>
+<I>pathName</I> <B>validate</B> <I>index</I><BR>
+
+Explicitly validates the specified index based on
+the current <B>-validatecommand</B> and returns 0 or 1
+based on whether the cell was validated.
+<P>
+<I>pathName</I> <B>width</B> ?<I>col</I>? ?<I>value</I> <I>col</I> <I>value</I> <I>...</I>?
+If no <I>col</I> is specified, returns a list describing
+all cols for which a width has been set. If <B>col</B> is
+specified with no value, it prints out the width of
+that col in characters (positive number) or pixels
+(negative number). If one or more <I>col-value</I> pairs
+are specified, then it sets each col to be that
+width in characters (positive number) or pixels
+(negative number). If <I>value</I> is <I>default</I>, then the
+col uses the default width, specified by <B>-colwidth</B>.
+<P>
+<I>pathName</I> <B>window</B> option ?<I>arg</I> <I>arg</I> <I>...</I>?<BR>
+
+This command is used to manipulate embedded windows.
+The exact behavior of the command depends on
+the <I>option</I> argument that follows the <B>window</B> argument.
+The following forms of the command are currently
+supported:
+<P>
+<I>pathName</I> <B>window</B> <B>cget</B> <I>index</I> <I>option</I>
+This command returns the current value of
+the option named <I>option</I> associated with the
+window given by <I>index</I>. <I>Option</I> may have any
+of the values accepted by the <B>window</B> <B>configure</B>
+widget command.
+<P>
+<I>pathName</I> <B>window</B> <B>configure</B> <I>index</I> ?<I>option</I>? ?<I>value</I>?
+?<I>option</I> <I>value</I> <I>...</I>?<BR>
+
+This command is similar to the <B>configure</B>
+widget command except that it modifies
+options associated with the embedded window
+given by <I>index</I> instead of modifying options
+for the overall table widget. If no <I>option</I>
+is specified, the command returns a list
+describing all of the available options for
+<I>index</I> (see <B>Tk_ConfigureInfo</B> for information
+on the format of this list). If <I>option</I> is
+specified with no <I>value</I>, then the command
+returns a list describing the one named
+option (this list will be identical to the
+corresponding sublist of the value returned
+if no <I>option</I> is specified). If one or more
+<I>option-value</I> pairs are specified, then the
+command modifies the given option(s) to have
+the given value(s) in <I>index</I>; in this case
+the command returns an empty string. See
+EMBEDDED WINDOWS above for details on the
+options available for windows.
+<P>
+<I>pathName</I> <B>window</B> <B>delete</B> <I>index</I> ?<I>index</I> <I>...</I>?
+Deletes an embedded window from the table.
+The associated window will also be deleted.
+<P>
+<I>pathName</I> <B>window</B> <B>move</B> <I>indexFrom</I> <I>indexTo</I>
+Moves an embedded window from one cell to
+another. If a window already exists in the
+target cell, it will be deleted.
+<P>
+<I>pathName</I> <B>window</B> <B>names</B> ?<I>pattern</I>?<BR>
+
+If no pattern is specified, shows the cells
+of all embedded windows. Otherwise the <I>pat</I><B>_</B>t<I>ern</I>
+is used as a glob pattern to show only
+cells matching that pattern.
+<P>
+<I>pathName</I> <B>xview</B> <I>args</I><BR>
+
+This command is used to query and change the horizontal
+position of the information in the widget's
+window. It can take any of the following forms:
+<P>
+<I>pathName</I> <B>xview</B><BR>
+
+Returns a list containing two elements.
+Each element is a real fraction between 0
+and 1; together they describe the horizontal
+span that is visible in the window. For
+example, if the first element is .2 and the
+second element is .6, 20% of the table's
+text is off-screen to the left, the middle
+40% is visible in the window, and 40% of the
+text is off-screen to the right. These are
+the same values passed to scrollbars via the
+<B>-xscrollcommand</B> option.
+<P>
+<I>pathName</I> <B>xview</B> <I>index</I><BR>
+
+Adjusts the view in the window so that the
+column given by <I>index</I> is displayed at the
+left edge of the window.
+<P>
+<I>pathName</I> <B>xview</B> <B>moveto</B> <I>fraction</I><BR>
+
+Adjusts the view in the window so that <I>frac</I><B>_</B>t<I>ion</I>
+of the total width of the table text is
+off-screen to the left. <I>fraction</I> must be a
+fraction between 0 and 1.
+<P>
+<I>pathName</I> <B>xview</B> <B>scroll</B> <I>number</I> <I>what</I>
+This command shifts the view in the window
+left or right according to <I>number</I> and <I>what</I>.
+<I>Number</I> must be an integer. <I>What</I> must be
+either <B>units</B> or <B>pages</B> or an abbreviation of
+one of these. If <I>what</I> is <B>units</B>, the view
+adjusts left or right by <I>number</I> character
+units (the width of the <B>0</B> character) on the
+display; if it is <B>pages</B> then the view
+adjusts by <I>number</I> screenfuls. If <I>number</I> is
+negative then characters farther to the left
+become visible; if it is positive then
+characters farther to the right become visible.
+<P>
+<I>pathName</I> <B>yview</B> <I>?args</I>?<BR>
+
+This command is used to query and change the vertical
+position of the text in the widget's window.
+It can take any of the following forms:
+<P>
+<I>pathName</I> <B>yview</B><BR>
+
+Returns a list containing two elements, both
+of which are real fractions between 0 and 1.
+The first element gives the position of the
+table element at the top of the window, relative
+to the table as a whole (0.5 means it
+is halfway through the table, for example).
+The second element gives the position of the
+table element just after the last one in the
+window, relative to the table as a whole.
+These are the same values passed to scrollbars
+via the <B>-yscrollcommand</B> option.
+<P>
+<I>pathName</I> <B>yview</B> <I>index</I><BR>
+
+Adjusts the view in the window so that the
+row given by <I>index</I> is displayed at the top
+of the window.
+<P>
+<I>pathName</I> <B>yview</B> <B>moveto</B> <I>fraction</I><BR>
+
+Adjusts the view in the window so that the
+element given by <I>fraction</I> appears at the top
+of the window. <I>Fraction</I> is a fraction
+between 0 and 1; 0 indicates the first element
+in the table, 0.33 indicates the element
+one-third the way through the table,
+and so on.
+<P>
+<I>pathName</I> <B>yview</B> <B>scroll</B> <I>number</I> <I>what</I>
+This command adjusts the view in the window
+up or down according to <I>number</I> and <I>what</I>.
+<I>Number</I> must be an integer. <I>What</I> must be
+either <B>units</B> or <B>pages</B>. If <I>what</I> is <B>units</B>,
+the view adjusts up or down by <I>number</I> lines;
+if it is <B>pages</B> then the view adjusts by <I>num</I><B>_</B>b<I>er</I>
+screenfuls. If <I>number</I> is negative then
+earlier elements become visible; if it is
+positive then later elements become visible.
+
+<H2><A NAME="sect13" HREF="#toc13"><B>Default</B> <B>Bindings</B></A></H2>
+
+<P>
+The initialization creates class bindings that give the
+following default behaviour:
+
+<DL>
+
+<DT>[1] </DT></DT>
+<DD>   Clicking Button-1 in a cell activates that cell.
+Clicking into an already active cell moves the
+insertion cursor to the character nearest the
+mouse.
+</DD>
+
+<DT>[2] </DT></DT>
+<DD>   Moving the mouse while Button-1 is pressed will
+stroke out a selection area. Exiting while Button-1
+is pressed causing scanning to occur on the
+table along with selection.
+</DD>
+
+<DT>[3] </DT></DT>
+<DD>   Moving the mouse while Button-2 is pressed causes
+scanning to occur without any selection.
+</DD>
+
+<DT>[4] </DT></DT>
+<DD>   Home moves the table to have the origin in view.
+</DD>
+
+<DT>[5] </DT></DT>
+<DD>   End moves the table to have the <B>end</B> cell in view.
+</DD>
+
+<DT>[6] </DT></DT>
+<DD>   Control-Home moves the table to the origin and
+activates that cell.
+</DD>
+
+<DT>[7] </DT></DT>
+<DD>   Control-End moves the table to the end and activates
+that cell.
+</DD>
+
+<DT>[8] </DT></DT>
+<DD>   Shift-Control-Home extends the selection to the
+origin.
+</DD>
+
+<DT>[9] </DT></DT>
+<DD>   Shift-Control-End extends the selection to the end.
+</DD>
+
+<DT>[10] </DT></DT>
+<DD>The left, right, up and down arrows move the active
+cell.
+</DD>
+</DL>
+<P>
+[11] Shift-&lt;arrow&gt; extends the selection in that direction.
+<P>
+[12] Control-leftarrow and Control-rightarrow move the
+insertion cursor within the cell.
+
+<DL>
+
+<DT>[13] </DT></DT>
+<DD>Control-slash selects all the cells.
+</DD>
+</DL>
+<P>
+[14] Control-backslash clears selection from all the
+cells.
+<P>
+[15] Backspace deletes the character before the insertion
+cursor in the active cell.
+<P>
+[16] Delete deletes the character after the insertion
+cursor in the active cell.
+<P>
+[17] Escape rereads the value of the active cell from
+the specified data source, discarding any edits
+that have may been performed on the cell.
+<P>
+[18] Control-a moves the insertion cursor to the beginning
+of the active cell.
+<P>
+[19] Control-e moves the insertion cursor to the end of
+the active cell.
+<P>
+[20] Control-minus and Control-equals decrease and
+increase the width of the column with the active
+cell in it.
+<P>
+[21] Moving the mouse while Button-3 (the right button
+on Windows) is pressed while you are over a border
+will cause interactive resizing of that row and/or
+column to occur, based on the value of <B>-resizeborders</B>.
+<P>
+Some bindings may have slightly different behavior dependent
+on the <B>-selectionmode</B> of the widget.
+<P>
+If the widget is disabled using the <B>-state</B> option, then
+its view can still be adjusted and cells can still be
+selected, but no insertion cursor will be displayed and no
+cell modifications will take place.
+<P>
+The behavior of tables can be changed by defining new
+bindings for individual widgets or by redefining the class
+bindings. The default bindings are either compiled in or
+read from a file expected to correspond to: &laquo;[lindex
+$tcl_pkgPath 0]/Tktable&lt;version&gt;/tkTable.tcl".
+
+<H2><A NAME="sect14" HREF="#toc14"><B>Performance</B> <B>Issues</B></A></H2>
+
+<P>
+The number of rows and columns or a table widget should
+not significantly affect the speed of redraw. Recalculation
+and redraw of table parameters and cells is
+restricted as much as possible.
+<P>
+The display cell with the insert cursor is redrawn each
+time the cursor blinks, which causes a steady stream of
+graphics traffic. Set the <B>-insertofftime</B> option to 0
+avoid this. The use of a <B>-command</B> with the table without
+a cache can cause significant slow-down, as the command is
+called once for each request of a cell value.
+
+<H2><A NAME="sect15" HREF="#toc15"><B>Examples</B></A></H2>
+
+<P>
+Set the topleft title area to be one spanning cell. This
+overestimates both row and column span by one, but the
+command does all the constraining for us.
+$table span [$table cget -roworigin],[$table cget -colorigin] [$table cget -titlerows],[$table cget -titlecols]
+Force a table window refresh (useful for the slight chance
+that a bug in the table is not causing proper refresh):
+$table configure -padx [$table cget -padx]
+
+<H2><A NAME="sect16" HREF="#toc16"><B>Keywords</B></A></H2>
+
+<P>
+table, widget, extension
+<P>
+
+<HR><P>
+<A NAME="toc"><B>Table of Contents</B></A><P>
+<UL>
+<LI><A NAME="toc0" HREF="#sect0">Name</A></LI>
+<LI><A NAME="toc1" HREF="#sect1">Synopsis</A></LI>
+<LI><A NAME="toc2" HREF="#sect2">Standard Options</A></LI>
+<LI><A NAME="toc3" HREF="#sect3">Widget-specific Options</A></LI>
+<LI><A NAME="toc4" HREF="#sect4">Description</A></LI>
+<LI><A NAME="toc5" HREF="#sect5">Initialization</A></LI>
+<LI><A NAME="toc6" HREF="#sect6">Indices</A></LI>
+<LI><A NAME="toc7" HREF="#sect7">Tags</A></LI>
+<LI><A NAME="toc8" HREF="#sect8">Embedded Windows</A></LI>
+<LI><A NAME="toc9" HREF="#sect9">the Selection</A></LI>
+<LI><A NAME="toc10" HREF="#sect10">Row/Col Spanning</A></LI>
+<LI><A NAME="toc11" HREF="#sect11">Command Substitution</A></LI>
+<LI><A NAME="toc12" HREF="#sect12">Widget Command</A></LI>
+<LI><A NAME="toc13" HREF="#sect13">Default Bindings</A></LI>
+<LI><A NAME="toc14" HREF="#sect14">Performance Issues</A></LI>
+<LI><A NAME="toc15" HREF="#sect15">Examples</A></LI>
+<LI><A NAME="toc16" HREF="#sect16">Keywords</A></LI>
+</UL>
+</BODY></HTML>
index 4fa4be2..75f9480 100644 (file)
@@ -1,6 +1,8 @@
 '\" The definitions below are for supplemental macros used in Tcl/Tk
 '\" manual entries.
 '\"
+'\" RCS: @(#) $Id$
+'\"
 '\" .AP type name in/out ?indent?
 '\"    Start paragraph describing an argument to a library procedure.
 '\"    type is type of argument (int, etc.), in/out is either "in", "out",
 '\" .UL arg1 arg2
 '\"    Print arg1 underlined, then print arg2 normally.
 '\"
-'\" SCCS: @(#) man.macros 1.8 96/02/15 20:02:24
-'\"
 '\"    # Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
 .if t .wh -1.3i ^B
 .nr ^l \n(.l
 .ad b
 '\"    # Start an argument description
 .de AP
-.ie !"\\$4"" .TP \\$4
+.ie !'\\$4'' .TP \\$4
 .el \{\
-.   ie !"\\$2"" .TP \\n()Cu
+.   ie !'\\$2'' .TP \\n()Cu
 .   el          .TP 15
 .\}
-.ie !"\\$3"" \{\
+.ie !'\\$3'' \{\
 .ta \\n()Au \\n()Bu
 \&\\$1 \\fI\\$2\\fP    (\\$3)
 .\".b
 .\}
 .el \{\
 .br
-.ie !"\\$2"" \{\
+.ie !'\\$2'' \{\
 \&\\$1 \\fI\\$2\\fP
 .\}
 .el \{\
 '\"    # define tabbing values for .AP
 .de AS
 .nr )A 10n
-.if !"\\$1"" .nr )A \\w'\\$1'u+3n
+.if !'\\$1'' .nr )A \\w'\\$1'u+3n
 .nr )B \\n()Au+15n
 .\"
-.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
+.if !'\\$2'' .nr )B \\w'\\$2'u+\\n()Au+3n
 .nr )C \\n()Bu+\\w'(in/out)'u+2n
 ..
 .AS Tcl_Interp Tcl_CreateInterp in/out
 '\"    # ^Y = starting y location
 '\"    # ^v = 1 (for troff;  for nroff this doesn't matter)
 .de VS
-.if !"\\$1"" .br
+.if !'\\$1'' .br
 .mk ^Y
 .ie n 'mc \s12\(br\s0
 .el .nr ^v 1u
@@ -232,7 +232,7 @@ Database Class:     \\fB\\$3\\fR
 .de UL
 \\$1\l'|0\(ul'\\$2
 ..
-.TH table n 2.00 Tk "Tk Table Extension"
+.TH table n 2.7 Tk "Tk Table Extension"
 .HS table tk
 .BS
 .SH NAME
@@ -240,12 +240,12 @@ table \- Create and manipulate tables
 .SH SYNOPSIS
 \fBtable\fI \fIpathName \fR?\fIoptions\fR?
 .SO
-\-anchor       \-background    \-borderwidth   \-cursor
+\-anchor       \-background    \-cursor
 \-exportselection      \-font  \-foreground
 \-highlightbackground  \-highlightcolor        \-highlightthickness
 \-insertbackground     \-insertborderwidth     \-insertofftime
-\-insertontime \-insertwidth   \-invertselected        \-padx
-\-pady \-relief        \-takefocus     \-xscrollcommand
+\-insertontime \-insertwidth   \-invertselected
+\-relief       \-takefocus     \-xscrollcommand
 \-yscrollcommand
 .SE
 
@@ -253,15 +253,21 @@ table \- Create and manipulate tables
 .OP \-autoclear autoClear AutoClear
 A boolean value which specifies whether the first keypress in a cell will
 delete whatever text was previously there.  Defaults to 0.
-.OP \-batchmode batchMode BatchMode
-If true, updates are not forced out at any point, the widget waits for Tk to
-be idle before it repaints the screen.  If false, flashes, variable updates
-and the cursor changes are forced immediately to the screen.
-Defaults to false.
 .OP \-bordercursor borderCursor Cursor
 Specifies the name of the cursor to show when over borders, a visual
 indication that interactive resizing is allowed (it is thus affect by
 the value of \-resizeborders).  Defaults to \fIcrosshair\fR.
+.OP "\-borderwidth or \-bd" borderWidth BorderWidth
+Specifies a non-negative pixel value or list of values indicating the width
+of the 3-D border to draw on interior table cells (if such a border is
+being drawn; the \fBrelief\fR option typically determines this).  If one
+value is specified, a rectangle of this width will be drawn.  If two values
+are specified, then only the left and right edges of the cell will have
+borders.  If four values are specified, then the values correspond to the
+{left right top bottom} edges.  This can be overridden by the a tag's
+borderwidth option.  It can also be affected by the defined
+\fB\-drawmode\fR for the table.  Each value in the list must have one of
+the forms acceptable to \fBTk_GetPixels\fR.
 .OP "\-browsecommand or \-browsecmd" browseCommand BrowseCommand
 Specifies a command which will be evaluated anytime the active cell changes.
 It uses the %\-substition model described in COMMAND SUBSTITUTION below.
@@ -271,7 +277,7 @@ contents should be kept.  This greatly enhances speed performance when used
 with \fB\-command\fR but uses extra memory.  Can maintain state when both
 \fB\-command\fR and \fB\-variable\fR are empty.  The cache is automatically
 flushed whenever the value of \fB\-cache\fR or \fB\-variable\fR changes,
-otherwise you have to explicitly \fBflush\fR it.  Defaults to false.
+otherwise you have to explicitly call \fBclear\fR on it.  Defaults to off.
 .OP \-colorigin colOrigin Origin
 Specifies what column index to interpret as the leftmost column in the table.
 This value is used for user indices in the table.  Defaults to 0.
@@ -331,9 +337,9 @@ Sets the table drawing mode to one of the following options:
 .RS
 .TP
 \fBslow\fR
-The table is drawn to an offscreen pixmap using the Tk bordering functions.
-This means there will be no flashing, but this mode is slow for all but
-small tables.
+The table is drawn to an offscreen pixmap using the Tk bordering functions
+(double-buffering).  This means there will be no flashing, but this mode is
+slow for larger tables.
 .TP
 \fBcompatible\fR
 The table is drawn directly to the screen using the Tk border functions.
@@ -341,10 +347,10 @@ It is faster, but the screen may flash on update.  This is the default.
 .TP
 \fBfast\fR
 The table is drawn directly to the screen and the borders are done with
-fast X calls, so they are always one pixel wide only.  As a side effect,
-it sets \fB\-borderwidth\fR to 1.  This mode provides best performance for
-large tables, but can flash on redraw and is not 100% Tk compatible on the
-border mode.
+fast X calls, so they are always one pixel wide only.  As a side effect, it
+restricts \fB\-borderwidth\fR to a range of 0 or 1.  This mode provides
+best performance for large tables, but can flash on redraw and is not 100%
+Tk compatible on the border mode.
 .TP
 \fBsingle\fR
 The table is drawn to the screen as in fast mode, but only single pixel
@@ -356,7 +362,7 @@ changes.  The table tag \fBflash\fR will be applied to these cells for the
 duration specified by \fB\-flashtime\fR.  Defaults to 0.
 .OP \-flashtime flashTime FlashTime
 The amount of time, in 1/4 second increments, for which a cell should flash
-when it is edited.  \fB\-flashmode\fR must be on.  Defaults to 2.
+when its value has changed.  \fB\-flashmode\fR must be on.  Defaults to 2.
 .OP \-height height Height
 Specifies the desired height for the window, in rows.
 If zero or less, then the desired height for the window is made just
@@ -365,11 +371,39 @@ further limited by \fB\-maxheight\fR.
 .OP \-invertselected invertSelected InvertSelected
 Specifies whether the foreground and background of an item should simply
 have their values swapped instead of merging the \fIsel\fR tag options
-when the cell is selected.  Defaults to 0 (merge).
+when the cell is selected.  Defaults to 0 (merge \fIsel\fR tag).
+.OP \-ipadx ipadX Pad
+A pixel value specifying the internal offset X padding for text in a cell.
+This value does not grow the size of the cell, it just causes the text to
+be drawn further from the cell border.  It only affects one side (depending
+on anchor).  Defaults to 0.  See \fB\-padx\fR for an alternate padding
+style.
+.OP \-ipady ipadY Pad
+A pixel value specifying the internal offset Y padding for text in a cell.
+This value does not grow the size of the cell, it just causes the text to
+be drawn further from the cell border.  It only affects one side (depending
+on anchor).  Defaults to 0.  See \fB\-pady\fR for an alternate padding
+style.
 .OP \-maxheight maxHeight MaxHeight
 The max height in pixels that the window will request.  Defaults to 600.
 .OP \-maxwidth maxWidth MaxWidth
 The max width in pixels that the window will request.  Defaults to 800.
+.OP \-multiline multiline Multiline
+Specifies the default setting for the multiline tag option.  Defaults to 1.
+.OP \-padx padX Pad
+A pixel value specifying the offset X padding for a cell.  This value
+causes the default size of the cell to increase by two times the value (one
+for each side), unless a specific pixel size is chosen for the cell with
+the \fBwidth\fR command.  This will force an empty area on the left and
+right of each cell edge.  This padding affects all types of data in the
+cell.  Defaults to 0.  See \fB\-ipadx\fR for an alternate padding style.
+.OP \-pady padY Pad
+A pixel value specifying the offset Y padding for a cell.  This value
+causes the default size of the cell to increase by two times the value (one
+for each side), unless a specific pixel size is chosen for the cell with
+the \fBheight\fR command.  This will force an empty area on the top and
+bottom of each cell edge.  This padding affects all types of data in the
+cell.  Defaults to 0.  See \fB\-ipadx\fR for an alternate padding style.
 .OP \-resizeborders resizeBorders ResizeBorders
 Specifies what kind of interactive border resizing to allow, must be one of
 row, col, both (default) or none.
@@ -395,11 +429,12 @@ defined by the user to accept a single argument (the row number), and
 return a tag name or null string.  This operates in a similar manner as
 \fB\-coltagcommand\fR, except that it applies to row tags.
 .OP "\-selectioncommand or \-selcmd" selectionCommand SelectionCommand
-Specifies a command to evaluate when the selection is retrieved from a table
-via the selection mechanism (ie: evaluating "selection get").  The return
-value from this command will become the string passed on by the selection
-mechanism.  It uses the %\-substition model described in COMMAND SUBSTITUTION
-below.
+Specifies a command to evaluate when the selection is retrieved from a
+table via the selection mechanism (ie: evaluating "\fBselection get\fR").
+The return value from this command will become the string passed on by the
+selection mechanism.  It uses the %\-substition model described in COMMAND
+SUBSTITUTION below.  If an error occurs, a Tcl background error is
+generated and nothing is returned.
 .OP \-selectmode selectMode SelectMode
 Specifies one of several styles for manipulating the selection.  The value
 of the option may be arbitrary, but the default bindings expect it to be
@@ -415,11 +450,17 @@ option may be one of \fBrow\fR, \fBcol\fR, \fBcell\fR, or \fBboth\fR
 (meaning \fBrow && col\fR); the default value is \fBcell\fR.  These types
 define whether an entire row/col is affected when a cell's selection is
 changed (set or clear).
+.OP \-sparsearray sparseArray SparseArray
+A boolean value that specifies whether an associated Tcl array should be
+kept as a sparse array (1, the default) or as a full array (0).  If true,
+then cell values that are empty will be deleted from the array (taking
+less memory).  If false, then all values in the array will be maintained.
 .OP \-state state State
 Specifies one of two states for the entry:  \fBnormal\fR or \fBdisabled\fR.
 If the table is disabled then the value may not be changed using widget
 commands and no insertion cursor will be displayed, even if the input focus
-is in the widget.  Defaults to \fBnormal\fR.
+is in the widget.  Also, all insert or delete methods will be ignored.
+Defaults to \fBnormal\fR.
 .OP \-titlecols titleCols TitleCols
 Number of columns to use as a title area.  Defaults to 0.
 .OP \-titlerows titleRows TitleRows
@@ -445,12 +486,14 @@ the special key \fIactive\fR which contains the value of the active cell
 buffer.  The Tcl array is managed as a sparse array (the table doesn't
 require all valid indices have values).  No stored value for an index is
 equivalent to the empty string, and clearing a cell will remove that index
-from the Tcl array.
+from the Tcl array, unless the \fB\-sparsearray\fR options is set to 0.
 .OP \-width width Width
 Specifies the desired width for the window, in columns.
 If zero or less, then the desired width for the window is made just
 large enough to hold all the columns in the table.  The width can be
 further limited by \fB\-maxwidth\fR.
+.OP \-wrap wrap Wrap
+Specifies the default wrap value for tags.  Defaults to 0.
 .BE
 
 .SH DESCRIPTION
@@ -476,8 +519,24 @@ Tables allow scrolling in both directions using the standard
 \fB\-xscrollcommand\fR and \fB\-yscrollcommand\fR options.  They also support
 scanning, as described below.
 .PP
-In order to obtain good performance, the table widget supports three drawing
-modes, two of which are fully Tk compatible.
+In order to obtain good performance, the table widget supports multiple
+drawing modes, two of which are fully Tk compatible.
+
+.SH "INITIALIZATION"
+.PP
+When the \fBtable\fR command is loaded into an interpreter, a built-in
+Tcl command, \fBtkTableInit\fR, is evaluated.  This will search for the
+appropriate table binding init file to load.  The directories searched
+are those in \fI$tcl_pkgPath\fR, both with Tktable(version) appended and
+without, \fI$tk_library\fR and \fI[pwd]\fR (the current directory).  You
+can also define an \fI$env(TK_TABLE_LIBRARY)\fR to head this search list.
+By default, the file searched for is called \fBtkTable.tcl\fR, but this
+can be overridden by setting \fI$env(TK_TABLE_LIBRARY_FILE)\fR.
+.PP
+This entire init script can be overridden by providing your own
+\fBtkTableInit\fR procedure before the library is loaded.  Otherwise, the
+aforementioned \fIenv(TK_TABLE_LIBRARY)\fR variable will be set with the
+directory in which \fI$env(TK_TABLE_LIBRARY_FILE)\fR was found.
 
 .SH "INDICES"
 .PP
@@ -525,12 +584,13 @@ the above forms.
 
 .SH TAGS
 .PP
-A tag is a textual string that is associated with zero or more rows, columns
-or cells in a table.  Tags may contain arbitrary characters, but it is
-probably best to avoid using names which look like indices.  There may be
-any number of tags associated with rows, columns or cells in a table.  There
-are several permanent tags in each table that can be configured by the user
-and will determine the attributes for special cells:
+A tag is a textual string that is associated with zero or more rows,
+columns or cells in a table.  Tags may contain arbitrary characters, but it
+is probably best to avoid using names which look like indices to reduce
+coding confusion.  There may be any number of tags in a table, but each
+row, column or cell can only have one tag associated with it at a time.
+There are several permanent tags in each table that can be configured by
+the user and will determine the attributes for special cells:
 .RS
 .TP 10
 \fBactive\fR
@@ -548,22 +608,24 @@ This tag is given to any cells in the title rows and columns.  This
 tag has \fB\-state\fR \fIdisabled\fR by default.
 .RE
 .PP
-Tags control the way cells are displayed on the screen.
-By default, cells are displayed as determined by the
-\fBbackground\fR, \fBfont\fR, and \fBforeground\fR options for the
-table widget.
-However, display options may be associated with individual tags
-using the ``\fIpathName \fBtag configure\fR'' widget command.
-If a cell has been tagged, then the display options associated
-with the tag override the default display style.
-The following options are currently supported for tags:
+Tags control the way cells are displayed on the screen.  Where appropriate,
+the default for displaying cells is determined by the options for the table
+widget.  However, display options may be associated with individual tags
+using the ``\fIpathName \fBtag configure\fR'' widget command.  If a cell,
+row or column has been tagged, then the display options associated with the
+tag override the default display style.  The following options are
+currently supported for tags:
 .RS
 .TP
 \fB\-anchor \fIanchor\fR
 Anchor for item in the cell space.
 .TP
 \fB\-background\fR or \fB\-bg\fR \fIcolor\fR
-Background color of the cell
+Background color of the cell.
+.TP
+\fB\-borderwidth\fR or \fB\-bd\fR \fIpixelList\fR
+Borderwidth of the cell, of the same format for the table, but may also
+be empty to inherit the default table borderwidth value (the default).
 .TP
 \fB\-font \fIfontName\fR
 Font for text in the cell.
@@ -578,6 +640,9 @@ It must be one of \fBleft\fR, \fBright\fR, or \fBcenter\fR.
 \fB\-image \fIimageName\fR
 An image to display in the cell instead of text.
 .TP
+\fB\-multiline \fIboolean\fR
+Whether to display text with newlines on multiple lines.
+.TP
 \fB\-relief \fIrelief\fR
 The relief for the cell.
 .TP
@@ -593,19 +658,21 @@ methods, but a direct \fIset\fR will not be prevented.
 Whether characters should wrap in a cell that is not wide enough.
 .RE
 .PP
-A priority order is defined among tags, and this order is used in
+A priority order is defined among tags based on creation order (first
+created tag has highest default priority), and this order is used in
 implementing some of the tag\-related functions described below.  When a cell
 is displayed, its properties are determined by the tags which are assigned
-to it.  Including the special tags, this order is \fBflash\fR, \fBactive\fR,
-\fBsel\fR, \fBtitle\fR, \fBcelltag\fR, \fBrowtag\fR, \fBcoltag\fR, default.
+to it.  The priority of a tag can be modified by the ``\fIpathName \fBtag
+lower\fR'' and ``\fIpathName \fBtag raise\fR'' widget commands.
 .PP
-If a cell has several tags associated with it, and if their display options
-conflict, then the options of the highest priority tag are used.  If a
+If a cell has several tags associated with it that define the same display
+options (eg - a \fBtitle\fR cell with specific \fBrow\fR and \fBcell\fR
+tags), then the options of the highest priority tag are used.  If a
 particular display option hasn't been specified for a particular tag, or if
-it is specified as an empty string, then that option will never be used; the
-next\-highest\-priority tag's option will used instead.  If no tag specifies a
-particular display option, then the default style for the widget will be
-used.
+it is specified as an empty string, then that option will not be used; the
+next\-highest\-priority tag's option will be used instead.  If no tag
+specifies a particular display option, then the default style for the
+widget will be used.
 .PP
 Images are used for display purposes only.  Editing in that cell will still
 be enabled and any querying of the cell will show the text value of the cell,
@@ -625,7 +692,10 @@ to by the index of the cell in the table.  Windows associated with the
 table widget are destroyed when the table widget is destroyed.
 .PP
 Windows are used for display purposes only.  A value still exists for that
-cell, but will not be shown unless the window is deleted in some way.
+cell, but will not be shown unless the window is deleted in some way.  If
+the window is destroyed or lost by the table widget to another geometry
+manager, then any data associated with it is lost (the cell it occupied
+will no longer appear in \fBwindow names\fR).
 .PP
 When an embedded window is added to a table widget with the window
 configure widget command, several configuration options may be associated
@@ -646,6 +716,10 @@ again the next time the cell is displayed.
 Background color of the cell.  If not
 specified, it uses the table's default background.
 .TP
+\fB\-borderwidth\fR or \fB\-bd\fR \fIpixelList\fR
+Borderwidth of the cell, of the same format for the table, but may also
+be empty to inherit the default table borderwidth value (the default).
+.TP
 \fB\-padx \fIpixels\fR
 As defined in the Tk options man page.
 .TP
@@ -673,13 +747,39 @@ form where each row is a list and each column is an element of a row list.
 You can change the way this value is interpreted by setting the
 \fB\-rowseparator\fR and \fB\-colseparator\fR options.  For example,
 default Excel format would be to set \fB\-rowseparator\fR to "\\n" and
-\fB\-colseparator\fR to "\\t".  Changes these values affects both how the
+\fB\-colseparator\fR to "\\t".  Changing these values affects both how the
 table sends out the selection and reads in pasted data, ensuring that the
 table should always be able to cut and paste to itself.  It is possible to
 change how pastes are handled by editing the table library procedure
 \fBtk_tablePasteHandler\fR.  This might be necessary if
 \fB\-selectioncommand\fR is set.
 
+.SH "ROW/COL SPANNING"
+.PP
+Individual cells can span multiple rows and/or columns.  This is done
+via the \fBspans\fR command (see below for exact arguments).  Cells in
+the title area that span are not permitted to span beyond the title area,
+and will be constrained accordingly.  If the title area shrinks during a
+configure, sanity checking will occur to ensure the above.  You may set
+spans on regular cells that extend beyond the defined row/col area.  These
+spans will not be constrained, so that when the defined row/col area
+expands, the span will expand with it.
+.PP
+When setting a span, checks are made as to whether the span would overlap
+an already spanning or hidden cell.  This is an error and it not allowed.
+Spans can affect the overall speed of table drawing, although not
+significantly.  If spans are not used, then there is no performance loss.
+.PP
+Cells \fIhidden\fR by spanning cells still have valid data.  This will
+be seen during cut and paste operations that involve hidden cells, or
+through direct access by a command like \fBget\fR or \fBset\fR.
+.PP
+The drawing properties of spanning cells apply to only the visual area
+of the cell.  For example, if a cell is center justified over 5 columns,
+then when viewing any portion of those columns, it will appear centered
+in the visible area. The non-visible column area will not be considered
+in the centering calculations.
+
 .SH "COMMAND SUBSTITUTION"
 .PP
 
@@ -741,8 +841,9 @@ x, y, width and height in pixels.  It clips the box to the visible portion,
 if any, otherwise an empty string is returned.
 .TP
 \fIpathName \fBborder\fR \fIoption args\fR
-This command is a voodoo hack to implement border sizing for tables.  Its
-options may change in the future.
+This command is a voodoo hack to implement border sizing for tables.
+This is normally called through bindings, with the following as valid
+options:
 .RS
 .TP
 \fIpathName \fBborder mark\fR \fIx y\fR ?\fIrow|col\fR?
@@ -753,7 +854,7 @@ button press in the widget.  If \fIrow\fR or \fIcol\fR is not specified, it
 returns a tuple of both border indices (an empty item means no border).
 Otherwise, just the specified item is returned.
 .TP
-\fIpathName \fBborder dragto\fR \fIx y\fR.
+\fIpathName \fBborder dragto\fR \fIx y\fR
 This command computes the difference between its \fIx\fR and \fIy\fR
 arguments and the \fIx\fR and \fIy\fR arguments to the last \fBborder
 mark\fR command for the widget.  It then adjusts the previously marked
@@ -767,6 +868,29 @@ Returns the current value of the configuration option given
 by \fIoption\fR.  \fIOption\fR may have any of the values accepted
 by the \fBtable\fR command.
 .TP
+\fIpathName \fBclear\fR \fIoption\fR ?\fIfirst\fR? ?\fIlast\fR?
+This command is a convenience routine to clear certain state information
+managed by the table.  \fIfirst\fR and \fIlast\fR represent valid table
+indices.  If neither are specified, then the command operates on the
+whole table.  The following options are recognized:
+.RS
+.TP
+\fIpathName \fBclear cache\fR ?\fIfirst\fR? ?\fIlast\fR?
+Clears the specified section of the cache, if the table has been
+keeping one.
+.TP
+\fIpathName \fBclear sizes\fR ?\fIfirst\fR? ?\fIlast\fR?
+Clears the specified row and column areas of specific height/width
+dimensions.  When just one index is specified, for example \fB2,0\fR,
+that is interpreted as row 2 \fBand\fR column 0.
+.TP
+\fIpathName \fBclear tags\fR ?\fIfirst\fR? ?\fIlast\fR?
+Clears the specified area of tags (all row, column and cell tags).
+.TP
+\fIpathName \fBclear all\fR ?\fIfirst\fR? ?\fIlast\fR?
+Performs all of the above clear functions on the specified area.
+.RE
+.TP
 \fIpathName \fBconfigure\fR ?\fIoption\fR? ?\fIvalue option value ...\fR?
 Query or modify the configuration options of the widget.
 If no \fIoption\fR is specified, returns a list describing all of
@@ -781,7 +905,7 @@ this case the command returns an empty string.
 \fIOption\fR may have any of the values accepted by the \fBtable\fR
 command.
 .TP
-\fIpathName \fBcurselection\fR ?\fIset value\fR?
+\fIpathName \fBcurselection\fR ?\fIvalue\fR?
 With no arguments, it returns the sorted indices of the currently selected
 cells.  Otherwise it sets all the selected cells to the given value.  The
 set has no effect if there is no associated Tcl array or the state is
@@ -802,37 +926,35 @@ the character after that index, otherwise it deletes from the first index to
 the second.  \fIindex\fR can be a number, \fBinsert\fR or \fBend\fR.
 .TP
 \fIpathName \fBdelete cols\fR ?\fIswitches\fR? \fIindex\fR ?\fIcount\fR?
-Deletes \fBcount\fR cols starting at (and including) col \fBindex\fR.  If
-\fBcount\fR is negative, it deletes cols to the left.  Otherwise it deletes
-cols to the right.  The selection will be cleared.  The optional switches
-are:
+Deletes \fIcount\fR cols starting at (and including) col \fIindex\fR.  The
+\fIindex\fR will be constrained to the limits of the tables.  If
+\fIcount\fR is negative, it deletes cols to the left.  Otherwise it deletes
+cols to the right.  \fIcount\fR defaults to 1 (meaning just the column
+specified).  At the moment, spans are
+not adjusted with this action.  Optional switches are:
 .RS
 .TP
-\fB\-cols\fR
-Sets an artificial maximum column boundary to use when collapsing the rest
-of the columns.  By default it uses the value of the \fB\-cols\fR widget
-option.  This can cause interesting side\-effects when used in conjunction
-with the other options.
-.TP
 \fB\-holddimensions\fR
 Causes the table cols to be unaffected by the deletion (empty cols may
 appear).  By default the dimensions are adjusted by \fBcount\fR.
 .TP
+\fB\-holdselection\fR
+Causes the selection to be maintained on the absolute cells values.
+Otherwise, the selection will be cleared..
+.TP
 \fB\-holdtags\fR
-Causes the tags specified by the \fItag\fR method to not collapse along
+Causes the tags specified by the \fItag\fR method to not move along
 with the data.  Also prevents specific widths set by the \fIwidth\fR method
 from being adjusted.  By default, these tags are properly adjusted.
 .TP
+\fB\-holdwindows\fR
+Causes the embedded windows created with the \fIwindow\fR method to not
+move along with the data.  By default, these windows are properly adjusted.
+.TP
 \fB\-keeptitles\fR
-Prevents title area cell contents from being moved.  Otherwise they are
+Prevents title area cells from being changed.  Otherwise they are
 treated just like regular cells and will move as specified.
 .TP
-\fB\-rows\fR
-Sets an artificial maximum row boundary to use when collapsing the rest of
-the rows.  By default it uses the value of the \fB\-rows\fR widget option.
-This can cause interesting side\-effects when used in conjunction with the
-other options.
-.TP
 \fB\-\-\fR
 Signifies the end of the switches.
 .RE
@@ -844,10 +966,6 @@ rows going down.  The selection will be cleared.  The switches are the same
 as those for column deletion.
 .RE
 .TP
-\fIpathName \fBflush\fR ?\fIfirst\fR? ?\fIlast\fR?
-Forces the table cache to be flushed from \fIfirst\fR to \fIlast\fR.  If
-neither are specified, it flushes the entire cache.
-.TP
 \fIpathName \fBget\fR \fIfirst\fR ?\fIlast\fR?
 Returns the value of the cells specified by the table indices \fIfirst\fR
 and (optionally) \fIlast\fR in a list.
@@ -861,11 +979,19 @@ then it sets each row to be that height in lines (positive number) or
 pixels (negative number).  If \fIvalue\fR is \fIdefault\fR, then the row
 uses the default height, specified by \fB\-rowheight\fR.
 .TP
+\fIpathName \fBhidden\fR ?\fIindex\fR? ?\fIindex ...\fR?
+When called without args, it returns all the \fIhidden\fR cells (those
+cells covered by a spanning cell).  If one index is specified, it returns
+the spanning cell covering that index, if any.  If multiple indices are
+specified, it returns 1 if all indices are hidden cells, 0 otherwise.
+.TP
 \fIpathName \fBicursor\fR ?\fIarg\fR?
 With no arguments, prints out the location of the insertion cursor in the
 active cell.  With one argument, sets the cursor to that point in the
 string.  0 is before the first character, you can also use \fBinsert\fR or
-\fBend\fR for the current insertion point or the end of the text.
+\fBend\fR for the current insertion point or the end of the text.  If
+there is no active cell, or the cell or table is disabled, this will
+return -1.
 .TP
 \fIpathName \fBindex\fR \fIindex\fR ?\fIrow|col\fR?
 Returns the integer cell coordinate that corresponds to \fIindex\fR in the
@@ -953,33 +1079,52 @@ inclusive, without affecting the selection state of cells outside that
 range.
 .RE
 .TP
-\fIpathName \fBset\fR \fIindex\fR ?\fIvalue\fR? ?\fIindex value ...\fR?
-Sets the specified index to the associated value.  Table validation will not
-be triggered via this method.
+\fIpathName \fBset\fR ?\fIrow|col\fR? \fIindex\fR ?\fIvalue\fR? ?\fIindex value ...\fR?
+Sets the specified index to the associated value.  Table validation will
+not be triggered via this method.  If \fBrow\fR or \fBcol\fR precedes the
+list of index/value pairs, then the value is assumed to be a Tcl list whose
+values will be split and set into the subsequent columns (if \fBrow\fR is
+specified) or rows (for \fBcol\fR).  For example, \fBset row 2,3
+{2,3 2,4 2,5}\fR will set 3 cells, from 2,3 to 2,5.  The setting of cells
+is silently bounded by the known table dimensions.
+.TP
+\fIpathName \fBspans\fR ?\fIindex\fR? ?\fIrows,cols index rows,cols ...\fR?
+This command is used to manipulate row/col spans.  When called with no
+arguments, all known spans are returned as a list of tuples of the form
+{index span}.  When called with only the \fIindex\fR, the span for that
+\fIindex\fR only is returned, if any.  Otherwise an even number of
+\fIindex rows,cols\fR pairs are used to set spans.  A span starts at the
+\fIindex\fR and continues for the specified number of rows and cols.
+Negative spans are not supported.  A span of 0,0 unsets any span on that
+cell.  See EXAMPLES for more info.
 .TP
 \fIpathName \fBtag\fR option ?\fIarg arg ...\fR?
 This command is used to manipulate tags.  The exact behavior of the command
 depends on the \fIoption\fR argument that follows the \fBtag\fR argument.
-Only \fIcget\fR complains about unknown tag names.
+\fIcget\fR, \fIcell\fR, and \fIrow|col\fR complain about unknown tag names.
 The following forms of the command are currently supported:
 .RS
 .TP
-\fIpathName \fBtag cell\fR \fItagName ?index ... ?\fR
+\fIpathName \fBtag cell\fR \fItagName ?index ...?\fR
 With no arguments, prints out the list of cells that use the \fItag\fR.
-Otherwise it sets the specified cells to use the \fItag\fR.  If \fItag\fR is
+Otherwise it sets the specified cells to use the named tag, replacing any
+tag that may have been set using this method before.  If \fItagName\fR is
 {}, the cells are reset to the default \fItag\fR.  Tags added during
-\-*tagcommand evaluation do not register here.
+\-*tagcommand evaluation do not register here.  If \fItagName\fR does
+not exist, it will be created with the default options.
 .TP
 \fIpathName \fBtag cget\fR \fItagName option\fR
 This command returns the current value of the option named \fIoption\fR
 associated with the tag given by \fItagName\fR.  \fIOption\fR may have any
 of the values accepted by the \fBtag configure\fR widget command.
 .TP
-\fIpathName \fBtag col\fR \fItagName ?col ... ?\fR
+\fIpathName \fBtag col\fR \fItagName ?col ...?\fR
 With no arguments, prints out the list of cols that use the \fItag\fR.
-Otherwise it sets the specified cols to use the \fItag\fR.  If \fItag\fR is
+Otherwise it sets the specified columns to use the named tag, replacing any
+tag that may have been set using this method before.  If \fItagName\fR is
 {}, the cols are reset to the default \fItag\fR.  Tags added during
-\-coltagcommand evaluation do not register here.
+\-coltagcommand evaluation do not register here.  If \fItagName\fR does
+not exist, it will be created with the default options.
 .TP
 \fIpathName \fBtag configure \fItagName\fR ?\fIoption\fR? ?\fIvalue\fR? ?\fIoption value ...\fR?
 This command is similar to the \fBconfigure\fR widget command except that
@@ -1005,16 +1150,29 @@ Returns 1 if the named tag exists, 0 otherwise.
 \fIpathName \fBtag includes\fR \fItagName index\fR
 Returns 1 if the specified index has the named tag, 0 otherwise.
 .TP
+\fIpathName \fBtag lower\fR \fItagName\fR ?\fIbelowThis\fR?
+Lower the priority of the named tag.  If \fIbelowThis\fR is not specified,
+then the tag's priority is lowered to the bottom, otherwise it is lowered
+to one below \fIbelowThis\fR.
+.TP
 \fIpathName \fBtag names\fR ?\fIpattern\fR?
 If no pattern is specified, shows the names of all defined tags.
 Otherwise the \fIpattern\fR is used as a glob pattern to show only
-tags matching that pattern.
+tags matching that pattern.  Tag names are returned in priority order
+(highest priority tag first).
 .TP
-\fIpathName \fBtag row\fR \fItagName ?row ...?\fR
+\fIpathName \fBtag raise\fR \fItagName\fR ?\fIaboveThis\fR?
+Raise the priority of the named tag.  If \fIaboveThis\fR is not specified,
+then the tag's priority is raised to the top, otherwise it is raised to
+one above \fIaboveThis\fR.
+.TP
+\fIpathName \fBtag row\fR \fItagName\fR ?\fIrow ...\fR?
 With no arguments, prints out the list of rows that use the \fItag\fR.
-Otherwise it sets the specified rows to use the tag.  If tag is {}, the rows
-are reset to use the default tag.  Tags added during \-rowtagcommand
-evaluation do not register here.
+Otherwise it sets the specified columns to use the named tag, replacing any
+tag that may have been set using this method before.  If \fItagName\fR is
+{}, the rows are reset to use the default tag.  Tags added during
+\-rowtagcommand evaluation do not register here.  If \fItagName\fR does
+not exist, it will be created with the default options.
 .RE
 .TP
 \fIpathName \fBvalidate\fR \fIindex\fR
@@ -1217,7 +1375,34 @@ take place.
 The behavior of tables can be changed by defining new bindings for
 individual widgets or by redefining the class bindings.  The default
 bindings are either compiled in or read from a file expected to
-correspond to: "[lindex $tcl_pkgPath 0]/Tktable/tkTable.tcl".
+correspond to: "[lindex $tcl_pkgPath 0]/Tktable<version>/tkTable.tcl".
+
+.SH "PERFORMANCE ISSUES"
+.PP
+The number of rows and columns or a table widget should not significantly
+affect the speed of redraw.  Recalculation and redraw of table parameters
+and cells is restricted as much as possible.
+.PP
+The display cell with the insert cursor is redrawn each time the cursor
+blinks, which causes a steady stream of graphics traffic.  Set the
+\fB\-insertofftime\fR option to 0 avoid this.  The use of a \fB\-command\fR
+with the table without a cache can cause significant slow\-down, as the
+command is called once for each request of a cell value.
+
+
+.SH EXAMPLES
+.PP
+Set the topleft title area to be one spanning cell.  This overestimates
+both row and column span by one, but the command does all the constraining
+for us.
+.CS
+$table span [$table cget -roworigin],[$table cget -colorigin] [$table cget -titlerows],[$table cget -titlecols]
+.CE
+Force a table window refresh (useful for the slight chance that a bug
+in the table is not causing proper refresh):
+.CS
+$table configure -padx [$table cget -padx]
+.CE
 
 .SH KEYWORDS
 table, widget, extension
index 7121230..d977d32 100644 (file)
@@ -143,25 +143,19 @@ ac_cv_c_itclsh = @ac_cv_c_itclsh@
 
 AUTOMAKE_OPTIONS = cygnus
 
-TCL = advice.tcl balloon.tcl bbox.tcl bgerror.tcl bindings.tcl \
-canvas.tcl cframe.tcl center.tcl debug.tcl def.tcl internet.tcl        \
-font.tcl gensym.tcl gettext.tcl hooks.tcl lframe.tcl list.tcl \
-looknfeel.tcl menu.tcl mono.tcl multibox.tcl parse_args.tcl path.tcl \
-postghost.tcl prefs.tcl print.tcl sendpr.tcl topbind.tcl toolbar.tcl \
-ulset.tcl wframe.tcl wingrab.tcl ventry.tcl combobox.tcl \
-pane.tcl panedwindow.tcl
+TCL = advice.tcl balloon.tcl bbox.tcl bgerror.tcl bindings.tcl canvas.tcl cframe.tcl center.tcl debug.tcl def.tcl internet.tcl font.tcl gensym.tcl gettext.tcl hooks.tcl lframe.tcl list.tcl looknfeel.tcl menu.tcl mono.tcl multibox.tcl parse_args.tcl path.tcl postghost.tcl prefs.tcl print.tcl sendpr.tcl topbind.tcl toolbar.tcl ulset.tcl wframe.tcl wingrab.tcl ventry.tcl combobox.tcl pane.tcl panedwindow.tcl
 
 
 PACKAGES = combobox.tcl
 
 guidir = $(datadir)/cygnus/gui
 gui_DATA = tclIndex pkgIndex.tcl $(TCL) $(PACKAGES)
-@TCL_SHARED_TRUE@SET_LIB_PATH = @TCL_SHARED_TRUE@$(RPATH_ENVVAR)=$$here/../../tcl/unix:$$here/../../itcl/itcl/unix:$$$(RPATH_ENVVAR); export $(RPATH_ENVVAR);
+@TCL_SHARED_TRUE@SET_LIB_PATH = $(RPATH_ENVVAR)=$$here/../../tcl/unix:$$here/../../itcl/itcl/unix:$$$(RPATH_ENVVAR); export $(RPATH_ENVVAR);
 @TCL_SHARED_FALSE@SET_LIB_PATH = 
 
 WISH = wish
-@CROSS_COMPILING_TRUE@ITCL_SH = @CROSS_COMPILING_TRUE@itclsh3.0
-@CROSS_COMPILING_FALSE@ITCL_SH = @CROSS_COMPILING_FALSE@@ITCL_SH@
+@CROSS_COMPILING_TRUE@ITCL_SH = itclsh3.0
+@CROSS_COMPILING_FALSE@ITCL_SH = @ITCL_SH@
 
 ETAGS_ARGS = --lang=none --regex='/[ \t]*\(proc\|method\|itcl_class\)[ \t]+\([^ \t]+\)/\1/' $(TCL) --lang=auto
 mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs
index 4e92d91..0be1fe3 100644 (file)
@@ -11,6 +11,9 @@ include_HEADERS = \
 
 endif
 
+datadir = @datadir@
+guidir  = $(datadir)/cygnus/gui
+
 # tkTable version info
 include $(srcdir)/tkTable_version.in
 
@@ -19,8 +22,8 @@ include $(srcdir)/tkTable_version.in
 # for those with another built-in "table" command
 TBL_COMMAND     = table
 
-tkTabletcl.h: $(srcdir)/tkTable.tcl
-       sed -e '/^$\#/d' -e '/^$$/d' -e 's/\"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' <$(srcdir)/tkTable.tcl >tkTabletcl.h || rm tkTabletcl.h 
+tkTable.tcl.h: $(srcdir)/tkTable.tcl
+       sed -e '/^$\#/d' -e '/^$$/d' -e 's/\"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' <$(srcdir)/tkTable.tcl >tkTable.tcl.h || rm tkTable.tcl.h 
 
 
 
@@ -44,15 +47,17 @@ $(TK_XINCLUDES) $(TCL_DEFS) $(TK_DEFS) \
 $(TKHDIR)/../unix $(TKHDIR)/../win \
 -DTBL_VERSION=\"$(TBL_VERSION)\"\
 -DTBL_COMMAND=\"$(TBL_COMMAND)\"\
--DTCL_RUNTIME=\"tkTable.tcl\"
+-DTBL_RUNTIME=\"tkTable.tcl\" -DTBL_RUNTIME_DIR=\"$(guidir)\"
+
+TKTABLE_SOURCES = tkTable.c tkTableCell.c tkTableCellSort.c \
+tkTableCmds.c tkTableEdit.c tkTableTag.c tkTableWin.c tkTableUtil.c
 
 libgui_a_SOURCES = guitcl.h paths.c subcommand.c subcommand.h \
 xpmlib.c tclmain.c tkGraphCanvas.c \
 tkCanvEdge.c tkCanvLayout.c tkCanvLayout.h tclhelp.c tclgetdir.c \
 tclwinprint.c tclsizebox.c tclshellexe.c tclmapi.c tclwinfont.c        \
 tclwingrab.c tclwinmode.c tclwinpath.c tclmsgbox.c tclcursor.c \
-tkTable.c tkTableCmd.c tkTableCell.c tkTableTag.c tkTableWin.c \
-tkWinPrintText.c tkWinPrintCanvas.c tkWarpPointer.c
+tkWinPrintText.c tkWinPrintCanvas.c tkWarpPointer.c $(TKTABLE_SOURCES)
 
 ## Dependencies
 
@@ -75,10 +80,14 @@ tclwinfont.$(OBJEXT): tclwinfont.c guitcl.h
 tclwingrab.$(OBJEXT): tclwingrab.c guitcl.h
 tclwinpath.$(OBJEXT): tclwinpath.c guitcl.h subcommand.h
 tclwinmode.$(OBJEXT): tclwinmode.c guitcl.h
-tkTable.$(OBJEXT): tkTable.c tkTable.h tkTableCmd.h tkTabletcl.h
-tkTableCell.$(OBJEXT): tkTableCell.c tkTable.h tkTableCmd.h
-tkTableTag.$(OBJEXT): tkTableTag.c tkTable.h tkTableCmd.h
-tkTableWin.$(OBJEXT):tkTableWin.c  tkTable.h tkTableCmd.h
-tkTableCmd.$(OBJEXT): tkTableCmd.c tkTableCmd.h
-tkTabletcl.h: tkTable.tcl
+tkTable.$(OBJEXT): tkTable.c tkTable.h tkTableInitScript.h tkTable.tcl.h
+tkTableCell.$(OBJEXT): tkTableCell.c tkTable.h
+tkTableCellSort.$(OBJEXT): tkTableCellSort.c tkTable.h
+tkTableCmds.$(OBJEXT): tkTableCmds.c tkTable.h
+tkTableEdit.$(OBJEXT): tkTableEdit.c tkTable.h
+tkTableTag.$(OBJEXT): tkTableTag.c tkTable.h
+tkTablePs.$(OBJECT): tkTablePs.c tkTable.h
+tkTableWin.$(OBJEXT):tkTableWin.c  tkTable.h
+tkTableUtil.$(OBJEXT): tkTableUtil.c tkTable.h
+tkTable.tcl.h: tkTable.tcl
 
index 32bf4ce..d8026a1 100644 (file)
@@ -11,6 +11,8 @@
 # PARTICULAR PURPOSE.
 
 
+#if 0
+
 
 SHELL = @SHELL@
 
@@ -23,7 +25,6 @@ exec_prefix = @exec_prefix@
 bindir = @bindir@
 sbindir = @sbindir@
 libexecdir = @libexecdir@
-datadir = @datadir@
 sysconfdir = @sysconfdir@
 sharedstatedir = @sharedstatedir@
 localstatedir = @localstatedir@
@@ -146,10 +147,14 @@ AUTOMAKE_OPTIONS = cygnus
 
 noinst_LIBRARIES = libgui.a
 
-@INSTALL_LIBGUI_TRUE@include_HEADERS = @INSTALL_LIBGUI_TRUE@\
-@INSTALL_LIBGUI_TRUE@  guitcl.h subcommand.h
+@INSTALL_LIBGUI_TRUE@include_HEADERS =         guitcl.h subcommand.h
+
+datadir = @datadir@
+guidir = $(datadir)/cygnus/gui
 
-TBL_VERSION = 2.1
+TBL_MAJOR_VERSION = 2
+TBL_MINOR_VERSION = 7
+TBL_VERSION = $(TBL_MAJOR_VERSION).$(TBL_MINOR_VERSION)
 
 # tkTable version info
 
@@ -160,22 +165,13 @@ TBL_COMMAND = table
 
 LIBGUI_CFLAGS = @LIBGUI_CFLAGS@
 
-INCLUDES = $(LIBGUI_CFLAGS) $(TCLHDIR) \
-$(TKHDIR) \
-$(TK_XINCLUDES) $(TCL_DEFS) $(TK_DEFS) \
-$(TKHDIR)/../unix $(TKHDIR)/../win \
--DTBL_VERSION=\"$(TBL_VERSION)\"\
--DTBL_COMMAND=\"$(TBL_COMMAND)\"\
--DTCL_RUNTIME=\"tkTable.tcl\"
+INCLUDES = $(LIBGUI_CFLAGS) $(TCLHDIR) $(TKHDIR) $(TK_XINCLUDES) $(TCL_DEFS) $(TK_DEFS) $(TKHDIR)/../unix $(TKHDIR)/../win -DTBL_VERSION=\"$(TBL_VERSION)\" -DTBL_COMMAND=\"$(TBL_COMMAND)\" -DTBL_RUNTIME=\"tkTable.tcl\" -DTBL_RUNTIME_DIR=\"$(guidir)\"
+
+
+TKTABLE_SOURCES = tkTable.c tkTableCell.c tkTableCellSort.c tkTableCmds.c tkTableEdit.c tkTableTag.c tkTableWin.c tkTableUtil.c
 
 
-libgui_a_SOURCES = guitcl.h paths.c subcommand.c subcommand.h \
-xpmlib.c tclmain.c tkGraphCanvas.c \
-tkCanvEdge.c tkCanvLayout.c tkCanvLayout.h tclhelp.c tclgetdir.c \
-tclwinprint.c tclsizebox.c tclshellexe.c tclmapi.c tclwinfont.c        \
-tclwingrab.c tclwinmode.c tclwinpath.c tclmsgbox.c tclcursor.c \
-tkTable.c tkTableCmd.c tkTableCell.c tkTableTag.c tkTableWin.c \
-tkWinPrintText.c tkWinPrintCanvas.c tkWarpPointer.c
+libgui_a_SOURCES = guitcl.h paths.c subcommand.c subcommand.h xpmlib.c tclmain.c tkGraphCanvas.c tkCanvEdge.c tkCanvLayout.c tkCanvLayout.h tclhelp.c tclgetdir.c tclwinprint.c tclsizebox.c tclshellexe.c tclmapi.c tclwinfont.c      tclwingrab.c tclwinmode.c tclwinpath.c tclmsgbox.c tclcursor.c tkWinPrintText.c tkWinPrintCanvas.c tkWarpPointer.c $(TKTABLE_SOURCES)
 
 mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs
 CONFIG_HEADER = ../config.h
@@ -194,10 +190,11 @@ tkCanvEdge.$(OBJEXT) tkCanvLayout.$(OBJEXT) tclhelp.$(OBJEXT) \
 tclgetdir.$(OBJEXT) tclwinprint.$(OBJEXT) tclsizebox.$(OBJEXT) \
 tclshellexe.$(OBJEXT) tclmapi.$(OBJEXT) tclwinfont.$(OBJEXT) \
 tclwingrab.$(OBJEXT) tclwinmode.$(OBJEXT) tclwinpath.$(OBJEXT) \
-tclmsgbox.$(OBJEXT) tclcursor.$(OBJEXT) tkTable.$(OBJEXT) \
-tkTableCmd.$(OBJEXT) tkTableCell.$(OBJEXT) tkTableTag.$(OBJEXT) \
-tkTableWin.$(OBJEXT) tkWinPrintText.$(OBJEXT) \
-tkWinPrintCanvas.$(OBJEXT) tkWarpPointer.$(OBJEXT)
+tclmsgbox.$(OBJEXT) tclcursor.$(OBJEXT) tkWinPrintText.$(OBJEXT) \
+tkWinPrintCanvas.$(OBJEXT) tkWarpPointer.$(OBJEXT) tkTable.$(OBJEXT) \
+tkTableCell.$(OBJEXT) tkTableCellSort.$(OBJEXT) tkTableCmds.$(OBJEXT) \
+tkTableEdit.$(OBJEXT) tkTableTag.$(OBJEXT) tkTableWin.$(OBJEXT) \
+tkTableUtil.$(OBJEXT)
 CFLAGS = @CFLAGS@
 COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
 CCLD = $(CC)
@@ -397,9 +394,13 @@ all-am all installdirs mostlyclean-generic distclean-generic \
 clean-generic maintainer-clean-generic clean mostlyclean distclean \
 maintainer-clean
 
+#endif
+#define TBL_MAJOR_VERSION 2
+#define TBL_MINOR_VERSION 7
+#define TBL_VERSION "2.7"
 
-tkTabletcl.h: $(srcdir)/tkTable.tcl
-       sed -e '/^$\#/d' -e '/^$$/d' -e 's/\"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' <$(srcdir)/tkTable.tcl >tkTabletcl.h || rm tkTabletcl.h 
+tkTable.tcl.h: $(srcdir)/tkTable.tcl
+       sed -e '/^$\#/d' -e '/^$$/d' -e 's/\"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' <$(srcdir)/tkTable.tcl >tkTable.tcl.h || rm tkTable.tcl.h 
 
 # Defining lib_LIBRARIES conditionally doesn't do the right thing.
 install-exec-local:
@@ -428,12 +429,16 @@ tclwinfont.$(OBJEXT): tclwinfont.c guitcl.h
 tclwingrab.$(OBJEXT): tclwingrab.c guitcl.h
 tclwinpath.$(OBJEXT): tclwinpath.c guitcl.h subcommand.h
 tclwinmode.$(OBJEXT): tclwinmode.c guitcl.h
-tkTable.$(OBJEXT): tkTable.c tkTable.h tkTableCmd.h tkTabletcl.h
-tkTableCell.$(OBJEXT): tkTableCell.c tkTable.h tkTableCmd.h
-tkTableTag.$(OBJEXT): tkTableTag.c tkTable.h tkTableCmd.h
-tkTableWin.$(OBJEXT):tkTableWin.c  tkTable.h tkTableCmd.h
-tkTableCmd.$(OBJEXT): tkTableCmd.c tkTableCmd.h
-tkTabletcl.h: tkTable.tcl
+tkTable.$(OBJEXT): tkTable.c tkTable.h tkTableInitScript.h tkTable.tcl.h
+tkTableCell.$(OBJEXT): tkTableCell.c tkTable.h
+tkTableCellSort.$(OBJEXT): tkTableCellSort.c tkTable.h
+tkTableCmds.$(OBJEXT): tkTableCmds.c tkTable.h
+tkTableEdit.$(OBJEXT): tkTableEdit.c tkTable.h
+tkTableTag.$(OBJEXT): tkTableTag.c tkTable.h
+tkTablePs.$(OBJECT): tkTablePs.c tkTable.h
+tkTableWin.$(OBJEXT):tkTableWin.c  tkTable.h
+tkTableUtil.$(OBJEXT): tkTableUtil.c tkTable.h
+tkTable.tcl.h: tkTable.tcl
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
index b6f0d7a..c26b4b4 100644 (file)
  * Tom Moore           tmoore@spatial.ca
  * Sebastian Wangnick  wangnick@orthogon.de
  *
- * Copyright (c) 1997-1998 Jeffrey Hobbs
+ * Copyright (c) 1997-2001 Jeffrey Hobbs
  *
- * See the file "license.terms" for information on usage and redistribution
+ * See the file "license.txt" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  *
+ * RCS: @(#) $Id$
  */
 
 #include "tkTable.h"
+
 #ifdef DEBUG
-#include "../../dprint.h"
+#include "dprint.h"
 #endif
 
-INLINE static void     TableFlushCache _ANSI_ARGS_((Table *tablePtr));
-static int     TableClear _ANSI_ARGS_((register Table *tablePtr, int mode,
-                                       char *first, char *last));
-INLINE static void     TableGetGc _ANSI_ARGS_((Display *display, Drawable d,
-                                       TableTag *tagPtr, GC *tagGc));
+static char ** StringifyObjects _ANSI_ARGS_((int objc,
+                       Tcl_Obj *CONST objv[]));
+
+static int     Tk_TableObjCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+
+static int     TableWidgetObjCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+static int     TableConfigure _ANSI_ARGS_((Tcl_Interp *interp,
+                       Table *tablePtr, int objc, Tcl_Obj *CONST objv[],
+                       int flags, int forceUpdate));
+static void    TableDestroy _ANSI_ARGS_((ClientData clientdata));
+static void    TableEventProc _ANSI_ARGS_((ClientData clientData,
+                       XEvent *eventPtr));
+static void    TableCmdDeletedProc _ANSI_ARGS_((ClientData clientData));
+
 static void    TableRedrawHighlight _ANSI_ARGS_((Table *tablePtr));
+static void    TableGetGc _ANSI_ARGS_((Display *display, Drawable d,
+                       TableTag *tagPtr, GC *tagGc));
+
 static void    TableDisplay _ANSI_ARGS_((ClientData clientdata));
 static void    TableFlashEvent _ANSI_ARGS_((ClientData clientdata));
-static void    TableAddFlash _ANSI_ARGS_((Table *tablePtr, int row, int col));
-static void    TableSetActiveIndex _ANSI_ARGS_((register Table *tablePtr));
-static void    TableGetActiveBuf _ANSI_ARGS_((register Table *tablePtr));
 static char *  TableVarProc _ANSI_ARGS_((ClientData clientData,
                        Tcl_Interp *interp, char *name, char *index,
                        int flags));
-static void    TableGeometryRequest _ANSI_ARGS_((Table *tablePtr));
-static void    TableAdjustActive _ANSI_ARGS_((register Table *tablePtr));
-static void    TableAdjustParams _ANSI_ARGS_((register Table *tablePtr));
 static void    TableCursorEvent _ANSI_ARGS_((ClientData clientData));
-static void    TableConfigCursor _ANSI_ARGS_((register Table *tablePtr));
 static int     TableFetchSelection _ANSI_ARGS_((ClientData clientData,
                        int offset, char *buffer, int maxBytes));
-static void    TableLostSelection _ANSI_ARGS_((ClientData clientData));
 static Tk_RestrictAction TableRestrictProc _ANSI_ARGS_((ClientData arg,
                        XEvent *eventPtr));
-static int     TableValidateChange _ANSI_ARGS_((Table *tablePtr, int r,
-                       int c, char *old, char *new, int index));
-static void    TableDeleteChars _ANSI_ARGS_((register Table *tablePtr,
-                                             int index, int count));
-static void    TableInsertChars _ANSI_ARGS_((register Table *tablePtr,
-                                             int index, char *string));
-static int     TableWidgetCmd _ANSI_ARGS_((ClientData clientData,
-                       Tcl_Interp *interp, int argc, char **argv));
-static void    TableDestroy _ANSI_ARGS_((ClientData clientdata));
-static void    TableEventProc _ANSI_ARGS_((ClientData clientData,
-                       XEvent *eventPtr));
-static int     TableConfigure _ANSI_ARGS_((Tcl_Interp *interp,
-                       Table *tablePtr, int argc, char **argv,
-                       int flags, int forceUpdate));
-static void    TableCmdDeletedProc _ANSI_ARGS_((ClientData clientData));
-static int     TableCmd _ANSI_ARGS_((ClientData clientData,
-                       Tcl_Interp *interp, int argc, char **argv));
 
 /*
- * The list of command values for all the widget commands
- * We could use enum for many of these #defines, but it adds
- * just that much more code size...
+ * The following tables define the widget commands (and sub-
+ * commands) and map the indexes into the string tables into 
+ * enumerated types used to dispatch the widget command.
  */
-#define CMD_ACTIVATE   1       /* activate command a la listbox */
-#define CMD_BBOX       3       /* bounding box of cell <index> */
-#define CMD_BORDER     5       /* border movement function */
-#define CMD_CGET       7       /* basic cget widget command */
-#define CMD_CLEAR      8       /* clear state command */
-#define        CMD_CONFIGURE   9       /* general configure command */
-#define CMD_CURSELECTION 11    /* get current selected cell(s) */
-#define CMD_CURVALUE   13      /* get current selection buffer */
-#define        CMD_DELETE      15      /* delete text in the selection */
-#define CMD_FLUSH      17      /* flush the table cache */
-#define CMD_GET                19      /* get mode a la listbox */
-#define        CMD_HEIGHT      21      /* (re)set row heights */
-#define CMD_ICURSOR    23      /* set the insertion cursor */
-#define CMD_INDEX      25      /* get an index */
-#define CMD_INSERT     27      /* insert text at any position */
-#define        CMD_REREAD      31      /* reread the current selection */
-#define CMD_SCAN       33      /* scan command a la listbox */
-#define CMD_SEE                35      /* see command a la listbox */
-#define CMD_SELECTION  37      /* selection command a la listbox */
-#define CMD_SET                39      /* set command, to set multiple items */
-#define        CMD_TAG         41      /* tag command menu */
-#define CMD_VALIDATE   43      /* validate contents of active cell */
-#define CMD_VERSION    45      /* hidden command to return version */
-#define        CMD_WIDTH       47      /* (re)set column widths */
-#define        CMD_WINDOW      49      /* manage embedded windows */
-#define CMD_XVIEW      51      /* change x view of widget (for scrollbars) */
-#define CMD_YVIEW      53      /* change y view of widget (for scrollbars) */
-
-/* The list of commands for the command parser */
-
-static Cmd_Struct main_cmds[] = {
-  {"activate",         CMD_ACTIVATE},
-  {"bbox",             CMD_BBOX},
-  {"border",           CMD_BORDER},
-  {"cget",             CMD_CGET},
-  {"clear",            CMD_CLEAR},
-  {"configure",                CMD_CONFIGURE},
-  {"curselection",     CMD_CURSELECTION},
-  {"curvalue",         CMD_CURVALUE},
-  {"delete",           CMD_DELETE},
-  {"flush",            CMD_FLUSH},
-  {"get",              CMD_GET},
-  {"height",           CMD_HEIGHT},
-  {"icursor",          CMD_ICURSOR},
-  {"index",            CMD_INDEX},
-  {"insert",           CMD_INSERT},
-  {"reread",           CMD_REREAD},
-  {"scan",             CMD_SCAN},
-  {"see",              CMD_SEE},
-  {"selection",                CMD_SELECTION},
-  {"set",              CMD_SET},
-  {"tag",              CMD_TAG},
-  {"validate",         CMD_VALIDATE},
-  {"version",          CMD_VERSION},
-  {"window",           CMD_WINDOW},
-  {"width",            CMD_WIDTH},
-  {"xview",            CMD_XVIEW},
-  {"yview",            CMD_YVIEW},
-  {"", 0}
+
+static char *selCmdNames[] = {
+    "anchor", "clear", "includes", "present", "set", (char *)NULL
+};
+enum selCommand {
+    CMD_SEL_ANCHOR, CMD_SEL_CLEAR, CMD_SEL_INCLUDES, CMD_SEL_PRESENT,
+    CMD_SEL_SET
 };
 
-/* selection subcommands */
-#define SEL_ANCHOR     1       /* set selection anchor */
-#define SEL_CLEAR      2       /* clear list from selection */
-#define SEL_INCLUDES   3       /* query items inclusion in selection */
-#define SEL_SET                4       /* include items in selection */
-
-static Cmd_Struct sel_cmds[]= {
-  {"anchor",    SEL_ANCHOR},
-  {"clear",     SEL_CLEAR},
-  {"includes",  SEL_INCLUDES},
-  {"set",       SEL_SET},
-  {"",          0 }
+static char *commandNames[] = {
+    "activate", "bbox", "border", "cget", "clear", "configure",
+    "curselection", "curvalue", "delete", "get", "height",
+    "hidden", "icursor", "index", "insert",
+#ifdef POSTSCRIPT
+    "postscript",
+#endif
+    "reread", "scan", "see", "selection", "set",
+    "spans", "tag", "validate", "version", "window", "width",
+    "xview", "yview", (char *)NULL
+};
+enum command {
+    CMD_ACTIVATE, CMD_BBOX, CMD_BORDER, CMD_CGET, CMD_CLEAR, CMD_CONFIGURE,
+    CMD_CURSEL, CMD_CURVALUE, CMD_DELETE, CMD_GET, CMD_HEIGHT,
+    CMD_HIDDEN, CMD_ICURSOR, CMD_INDEX, CMD_INSERT,
+#ifdef POSTSCRIPT
+    CMD_POSTSCRIPT,
+#endif
+    CMD_REREAD, CMD_SCAN, CMD_SEE, CMD_SELECTION, CMD_SET,
+    CMD_SPANS, CMD_TAG, CMD_VALIDATE, CMD_VERSION, CMD_WINDOW, CMD_WIDTH,
+    CMD_XVIEW, CMD_YVIEW
 };
 
 /* -selecttype selection type options */
-/* These alter how the selection set/clear commands behave */
-#define SEL_ROW                (1<<0)
-#define SEL_COL                (1<<1)
-#define SEL_BOTH       (1<<2)
-#define SEL_CELL       (1<<3)
-#define SEL_NONE       (1<<4)
-
 static Cmd_Struct sel_vals[]= {
-  {"row",       SEL_ROW},
-  {"col",       SEL_COL},
-  {"both",      SEL_BOTH},
-  {"cell",      SEL_CELL},
-  {"",          0 }
-};
-
-/* clear subcommands */
-#define CLEAR_TAGS     (1<<0)
-#define CLEAR_SIZES    (1<<1)
-#define CLEAR_CACHE    (1<<2)
-static Cmd_Struct clear_cmds[] = {
-  {"tags",     CLEAR_TAGS},
-  {"sizes",    CLEAR_SIZES},
-  {"cache",    CLEAR_CACHE},
-  {"all",      CLEAR_TAGS | CLEAR_SIZES | CLEAR_CACHE},
-  {"",         0}
+    {"row",     SEL_ROW},
+    {"col",     SEL_COL},
+    {"both",    SEL_BOTH},
+    {"cell",    SEL_CELL},
+    {"",        0 }
 };
 
 /* -resizeborders options */
 static Cmd_Struct resize_vals[]= {
-  {"row",       SEL_ROW},              /* allow rows to be dragged */
-  {"col",       SEL_COL},              /* allow cols to be dragged */
-  {"both",      SEL_ROW|SEL_COL},      /* allow either to be dragged */
-  {"none",      SEL_NONE},             /* allow nothing to be dragged */
-  {"",          0 }
-};
-
-/* insert/delete subcommands */
-#define MOD_ACTIVE     1
-#define MOD_COLS       2
-#define MOD_ROWS       3
-static Cmd_Struct mod_cmds[] = {
-  {"active",   MOD_ACTIVE},
-  {"cols",     MOD_COLS},
-  {"rows",     MOD_ROWS},
-  {"", 0}
-};
-
-/* border subcommands */
-#define BD_MARK                1
-#define BD_DRAGTO      2
-static Cmd_Struct bd_cmds[] = {
-  {"mark",     BD_MARK},
-  {"dragto",   BD_DRAGTO},
-  {"", 0}
+    {"row",     SEL_ROW},              /* allow rows to be dragged */
+    {"col",     SEL_COL},              /* allow cols to be dragged */
+    {"both",    SEL_ROW|SEL_COL},      /* allow either to be dragged */
+    {"none",    SEL_NONE},             /* allow nothing to be dragged */
+    {"",        0 }
 };
 
 /* drawmode values */
@@ -219,11 +128,11 @@ static Cmd_Struct bd_cmds[] = {
 #define DRAW_MODE_SINGLE       (1<<3)
 
 static Cmd_Struct drawmode_vals[] = {
-  {"fast",             DRAW_MODE_FAST},
-  {"compatible",       DRAW_MODE_TK_COMPAT},
-  {"slow",             DRAW_MODE_SLOW},
-  {"single",           DRAW_MODE_SINGLE},
-  {"", 0}
+    {"fast",           DRAW_MODE_FAST},
+    {"compatible",     DRAW_MODE_TK_COMPAT},
+    {"slow",           DRAW_MODE_SLOW},
+    {"single",         DRAW_MODE_SINGLE},
+    {"", 0}
 };
 
 /* stretchmode values */
@@ -238,163 +147,179 @@ static Cmd_Struct drawmode_vals[] = {
 #define STRETCH_MODE_FILL       (1<<4) /* More ROWS in Window */
 
 static Cmd_Struct stretch_vals[] = {
-  {"none",     STRETCH_MODE_NONE},
-  {"unset",    STRETCH_MODE_UNSET},
-  {"all",      STRETCH_MODE_ALL},
-  {"last",     STRETCH_MODE_LAST},
-  {"fill",     STRETCH_MODE_FILL},
-  {"", 0}
+    {"none",   STRETCH_MODE_NONE},
+    {"unset",  STRETCH_MODE_UNSET},
+    {"all",    STRETCH_MODE_ALL},
+    {"last",   STRETCH_MODE_LAST},
+    {"fill",   STRETCH_MODE_FILL},
+    {"", 0}
 };
 
 static Cmd_Struct state_vals[]= {
-  {"normal",    STATE_NORMAL},
-  {"disabled",  STATE_DISABLED},
-  {"",          0 }
+    {"normal",  STATE_NORMAL},
+    {"disabled", STATE_DISABLED},
+    {"",        0 }
 };
 
 /* The widget configuration table */
-static Tk_CustomOption drawOpt = { Cmd_OptionSet, Cmd_OptionGet,
-                                  (ClientData)(&drawmode_vals) };
-static Tk_CustomOption resizeTypeOpt = { Cmd_OptionSet, Cmd_OptionGet,
-                                        (ClientData)(&resize_vals) };
-static Tk_CustomOption stretchOpt = { Cmd_OptionSet, Cmd_OptionGet,
-                                     (ClientData)(&stretch_vals) };
-static Tk_CustomOption selTypeOpt = { Cmd_OptionSet, Cmd_OptionGet,
-                                     (ClientData)(&sel_vals) };
-static Tk_CustomOption stateTypeOpt = { Cmd_OptionSet, Cmd_OptionGet,
-                                       (ClientData)(&state_vals) };
-
-static Tk_ConfigSpec TableConfig[] = {
-  {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", "center",
-   Tk_Offset(Table, defaultTag.anchor), 0 },
-  {TK_CONFIG_BOOLEAN, "-autoclear", "autoClear", "AutoClear", "0",
-   Tk_Offset(Table, autoClear), 0 },
-  {TK_CONFIG_BORDER, "-background", "background", "Background", NORMAL_BG,
-   Tk_Offset(Table, defaultTag.bg), 0 },
-  {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, (char *) NULL, 0, 0},
-  {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *) NULL, 0, 0},
-  {TK_CONFIG_CURSOR, "-bordercursor", "borderCursor", "Cursor", "crosshair",
-   Tk_Offset(Table, bdcursor), TK_CONFIG_NULL_OK },
-  {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", "1",
-   Tk_Offset(Table, borderWidth), 0 },
-  {TK_CONFIG_STRING, "-browsecommand", "browseCommand", "BrowseCommand", "",
-   Tk_Offset(Table, browseCmd), TK_CONFIG_NULL_OK},
-  {TK_CONFIG_SYNONYM, "-browsecmd", "browseCommand", (char *) NULL,
-   (char *) NULL, 0, TK_CONFIG_NULL_OK},
-  {TK_CONFIG_BOOLEAN, "-cache", "cache", "Cache", "0",
-   Tk_Offset(Table, caching), 0},
-  {TK_CONFIG_INT, "-colorigin", "colOrigin", "Origin", "0",
-   Tk_Offset(Table, colOffset), 0 },
-  {TK_CONFIG_INT, "-cols", "cols", "Cols", "10",
-   Tk_Offset(Table, cols), 0 },
-  {TK_CONFIG_STRING, "-colseparator", "colSeparator", "Separator", NULL,
-   Tk_Offset(Table, colSep), TK_CONFIG_NULL_OK },
-  {TK_CONFIG_CUSTOM, "-colstretchmode", "colStretch", "StretchMode", "none",
-   Tk_Offset (Table, colStretch), 0 , &stretchOpt },
-  {TK_CONFIG_STRING, "-coltagcommand", "colTagCommand", "TagCommand", NULL,
-   Tk_Offset(Table, colTagCmd), TK_CONFIG_NULL_OK },
-  {TK_CONFIG_INT, "-colwidth", "colWidth", "ColWidth", "10",
-   Tk_Offset(Table, defColWidth), 0 },
-  {TK_CONFIG_STRING, "-command", "command", "Command", "",
-   Tk_Offset(Table, command), TK_CONFIG_NULL_OK},
-  {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", "xterm",
-   Tk_Offset(Table, cursor), TK_CONFIG_NULL_OK },
-  {TK_CONFIG_CUSTOM, "-drawmode", "drawMode", "DrawMode", "compatible",
-   Tk_Offset(Table, drawMode), 0, &drawOpt },
-  {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
-   "ExportSelection", "1", Tk_Offset(Table, exportSelection), 0},
-  {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL, (char *) NULL, 0, 0},
-  {TK_CONFIG_BOOLEAN, "-flashmode", "flashMode", "FlashMode", "0",
-   Tk_Offset(Table, flashMode), 0 },
-  {TK_CONFIG_INT, "-flashtime", "flashTime", "FlashTime", "2",
-   Tk_Offset(Table, flashTime), 0 },
-  {TK_CONFIG_FONT, "-font", "font", "Font",  DEF_TABLE_FONT,
-   Tk_Offset(Table, defaultTag.tkfont), 0 },
-  {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground", "black",
-   Tk_Offset(Table, defaultTag.fg), 0 },
-  {TK_CONFIG_INT, "-height", "height", "Height", "0",
-   Tk_Offset(Table, maxReqRows), 0 },
-  {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
-   "HighlightBackground", NORMAL_BG, Tk_Offset(Table, highlightBgColorPtr), 0},
-  {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
-   HIGHLIGHT, Tk_Offset(Table, highlightColorPtr), 0 },
-  {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
-   "HighlightThickness", "2", Tk_Offset(Table, highlightWidth), 0 },
-  {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
-   "Black", Tk_Offset(Table, insertBg), 0 },
-  {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
-   "0", Tk_Offset(Table, insertBorderWidth), TK_CONFIG_COLOR_ONLY},
-  {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
-   "0", Tk_Offset(Table, insertBorderWidth), TK_CONFIG_MONO_ONLY},
-  {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime", "300",
-   Tk_Offset(Table, insertOffTime), 0},
-  {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime", "600",
-   Tk_Offset(Table, insertOnTime), 0},
-  {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth", "2",
-   Tk_Offset(Table, insertWidth), 0},
-   {TK_CONFIG_BOOLEAN, "-invertselected", "invertSelected", "InvertSelected",
-    "0", Tk_Offset(Table, invertSelected), 0},
-  {TK_CONFIG_PIXELS, "-maxheight", "maxHeight", "MaxHeight", "600",
-   Tk_Offset(Table, maxReqHeight), 0 },
-  {TK_CONFIG_PIXELS, "-maxwidth", "maxWidth", "MaxWidth", "800",
-   Tk_Offset(Table, maxReqWidth), 0 },
-  {TK_CONFIG_BOOLEAN, "-multiline", "multiline", "Multiline", "1",
-   Tk_Offset(Table, defaultTag.multiline), 0 },
-  {TK_CONFIG_PIXELS, "-padx", "padX", "Pad", "2", Tk_Offset(Table, padX), 0},
-  {TK_CONFIG_PIXELS, "-pady", "padY", "Pad", "1", Tk_Offset(Table, padY), 0},
-  {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", "sunken",
-   Tk_Offset(Table, defaultTag.relief), 0 },
-  {TK_CONFIG_CUSTOM, "-resizeborders", "resizeBorders", "ResizeBorders",
-   "both", Tk_Offset(Table, resize), 0, &resizeTypeOpt },
-  {TK_CONFIG_PIXELS, "-rowheight", "rowHeight", "RowHeight", "1",
-   Tk_Offset(Table, defRowHeight), 0 },
-  {TK_CONFIG_INT, "-roworigin", "rowOrigin", "Origin", "0",
-   Tk_Offset(Table, rowOffset), 0 },
-  {TK_CONFIG_INT, "-rows", "rows", "Rows", "10", Tk_Offset(Table, rows), 0 },
-  {TK_CONFIG_STRING, "-rowseparator", "rowSeparator", "Separator", NULL,
-   Tk_Offset(Table, rowSep), TK_CONFIG_NULL_OK },
-  {TK_CONFIG_CUSTOM, "-rowstretchmode", "rowStretch", "StretchMode", "none",
-   Tk_Offset(Table, rowStretch), 0 , &stretchOpt },
-  {TK_CONFIG_STRING, "-rowtagcommand", "rowTagCommand", "TagCommand", NULL,
-   Tk_Offset(Table, rowTagCmd), TK_CONFIG_NULL_OK },
-  {TK_CONFIG_SYNONYM, "-selcmd", "selectionCommand", (char *) NULL,
-   (char *) NULL, 0, TK_CONFIG_NULL_OK},
-  {TK_CONFIG_STRING, "-selectioncommand", "selectionCommand",
-   "SelectionCommand", NULL, Tk_Offset(Table, selCmd), TK_CONFIG_NULL_OK },
-  {TK_CONFIG_STRING, "-selectmode", "selectMode", "SelectMode", "browse",
-   Tk_Offset(Table, selectMode), TK_CONFIG_NULL_OK },
-  {TK_CONFIG_BOOLEAN, "-selecttitles", "selectTitles", "SelectTitles", "0",
-   Tk_Offset(Table, selectTitles), 0 },
-  {TK_CONFIG_CUSTOM, "-selecttype", "selectType", "SelectType", "cell",
-   Tk_Offset(Table, selectType), 0, &selTypeOpt },
-  {TK_CONFIG_CUSTOM, "-state", "state", "State", "normal",
-   Tk_Offset(Table, state), 0, &stateTypeOpt},
-  {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus", (char *) NULL,
-   Tk_Offset(Table, takeFocus), TK_CONFIG_NULL_OK },
-  {TK_CONFIG_INT, "-titlecols", "titleCols", "TitleCols", "0",
-   Tk_Offset(Table, titleCols), TK_CONFIG_NULL_OK },
-  {TK_CONFIG_INT, "-titlerows", "titleRows", "TitleRows", "0",
-   Tk_Offset(Table, titleRows), TK_CONFIG_NULL_OK },
-  {TK_CONFIG_BOOLEAN, "-usecommand", "useCommand", "UseCommand", "1",
-   Tk_Offset(Table, useCmd), 0},
-  {TK_CONFIG_STRING, "-variable", "variable", "Variable", (char *) NULL,
-   Tk_Offset(Table, arrayVar), TK_CONFIG_NULL_OK },
-  {TK_CONFIG_BOOLEAN, "-validate", "validate", "Validate", "0",
-   Tk_Offset(Table, validate), 0 },
-  {TK_CONFIG_STRING, "-validatecommand", "validateCommand", "ValidateCommand",
-   "", Tk_Offset(Table, valCmd), TK_CONFIG_NULL_OK},
-  {TK_CONFIG_SYNONYM, "-vcmd", "validateCommand", (char *) NULL,
-   (char *) NULL, 0, TK_CONFIG_NULL_OK},
-  {TK_CONFIG_INT, "-width", "width", "Width", "0",
-   Tk_Offset(Table, maxReqCols), 0 },
-  {TK_CONFIG_BOOLEAN, "-wrap", "wrap", "Wrap", "0",
-   Tk_Offset(Table, defaultTag.wrap), 0 },
-  {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
-   NULL, Tk_Offset(Table, xScrollCmd), TK_CONFIG_NULL_OK },
-  {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
-   NULL, Tk_Offset(Table, yScrollCmd), TK_CONFIG_NULL_OK },
-  {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
-   (char *) NULL, 0, 0 }
+static Tk_CustomOption drawOpt         = { Cmd_OptionSet, Cmd_OptionGet,
+                                           (ClientData)(&drawmode_vals) };
+static Tk_CustomOption resizeTypeOpt   = { Cmd_OptionSet, Cmd_OptionGet,
+                                           (ClientData)(&resize_vals) };
+static Tk_CustomOption stretchOpt      = { Cmd_OptionSet, Cmd_OptionGet,
+                                           (ClientData)(&stretch_vals) };
+static Tk_CustomOption selTypeOpt      = { Cmd_OptionSet, Cmd_OptionGet,
+                                           (ClientData)(&sel_vals) };
+static Tk_CustomOption stateTypeOpt    = { Cmd_OptionSet, Cmd_OptionGet,
+                                           (ClientData)(&state_vals) };
+static Tk_CustomOption bdOpt           = { TableOptionBdSet, TableOptionBdGet,
+                                           (ClientData) BD_TABLE };
+
+Tk_ConfigSpec tableSpecs[] = {
+    {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", "center",
+     Tk_Offset(Table, defaultTag.anchor), 0},
+    {TK_CONFIG_BOOLEAN, "-autoclear", "autoClear", "AutoClear", "0",
+     Tk_Offset(Table, autoClear), 0},
+    {TK_CONFIG_BORDER, "-background", "background", "Background", NORMAL_BG,
+     Tk_Offset(Table, defaultTag.bg), 0},
+    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_CURSOR, "-bordercursor", "borderCursor", "Cursor", "crosshair",
+     Tk_Offset(Table, bdcursor), TK_CONFIG_NULL_OK },
+    {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth", "1",
+     Tk_Offset(Table, defaultTag), TK_CONFIG_NULL_OK, &bdOpt },
+    {TK_CONFIG_STRING, "-browsecommand", "browseCommand", "BrowseCommand", "",
+     Tk_Offset(Table, browseCmd), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_SYNONYM, "-browsecmd", "browseCommand", (char *)NULL,
+     (char *)NULL, 0, TK_CONFIG_NULL_OK},
+    {TK_CONFIG_BOOLEAN, "-cache", "cache", "Cache", "0",
+     Tk_Offset(Table, caching), 0},
+    {TK_CONFIG_INT, "-colorigin", "colOrigin", "Origin", "0",
+     Tk_Offset(Table, colOffset), 0},
+    {TK_CONFIG_INT, "-cols", "cols", "Cols", "10",
+     Tk_Offset(Table, cols), 0},
+    {TK_CONFIG_STRING, "-colseparator", "colSeparator", "Separator", NULL,
+     Tk_Offset(Table, colSep), TK_CONFIG_NULL_OK },
+    {TK_CONFIG_CUSTOM, "-colstretchmode", "colStretch", "StretchMode", "none",
+     Tk_Offset (Table, colStretch), 0 , &stretchOpt },
+    {TK_CONFIG_STRING, "-coltagcommand", "colTagCommand", "TagCommand", NULL,
+     Tk_Offset(Table, colTagCmd), TK_CONFIG_NULL_OK },
+    {TK_CONFIG_INT, "-colwidth", "colWidth", "ColWidth", "10",
+     Tk_Offset(Table, defColWidth), 0},
+    {TK_CONFIG_STRING, "-command", "command", "Command", "",
+     Tk_Offset(Table, command), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", "xterm",
+     Tk_Offset(Table, cursor), TK_CONFIG_NULL_OK },
+    {TK_CONFIG_CUSTOM, "-drawmode", "drawMode", "DrawMode", "compatible",
+     Tk_Offset(Table, drawMode), 0, &drawOpt },
+    {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
+     "ExportSelection", "1", Tk_Offset(Table, exportSelection), 0},
+    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
+    {TK_CONFIG_BOOLEAN, "-flashmode", "flashMode", "FlashMode", "0",
+     Tk_Offset(Table, flashMode), 0},
+    {TK_CONFIG_INT, "-flashtime", "flashTime", "FlashTime", "2",
+     Tk_Offset(Table, flashTime), 0},
+    {TK_CONFIG_FONT, "-font", "font", "Font",  DEF_TABLE_FONT,
+     Tk_Offset(Table, defaultTag.tkfont), 0},
+    {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground", "black",
+     Tk_Offset(Table, defaultTag.fg), 0},
+#ifdef PROCS
+    {TK_CONFIG_BOOLEAN, "-hasprocs", "hasProcs", "hasProcs", "0",
+     Tk_Offset(Table, hasProcs), 0},
+#endif
+    {TK_CONFIG_INT, "-height", "height", "Height", "0",
+     Tk_Offset(Table, maxReqRows), 0},
+    {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
+     "HighlightBackground", NORMAL_BG, Tk_Offset(Table, highlightBgColorPtr), 0},
+    {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+     HIGHLIGHT, Tk_Offset(Table, highlightColorPtr), 0},
+    {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
+     "HighlightThickness", "2", Tk_Offset(Table, highlightWidth), 0},
+    {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
+     "Black", Tk_Offset(Table, insertBg), 0},
+    {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
+     "0", Tk_Offset(Table, insertBorderWidth), TK_CONFIG_COLOR_ONLY},
+    {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
+     "0", Tk_Offset(Table, insertBorderWidth), TK_CONFIG_MONO_ONLY},
+    {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime", "300",
+     Tk_Offset(Table, insertOffTime), 0},
+    {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime", "600",
+     Tk_Offset(Table, insertOnTime), 0},
+    {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth", "2",
+     Tk_Offset(Table, insertWidth), 0},
+    {TK_CONFIG_BOOLEAN, "-invertselected", "invertSelected", "InvertSelected",
+     "0", Tk_Offset(Table, invertSelected), 0},
+    {TK_CONFIG_PIXELS, "-ipadx", "ipadX", "Pad", "0",
+     Tk_Offset(Table, ipadX), 0},
+    {TK_CONFIG_PIXELS, "-ipady", "ipadY", "Pad", "0",
+     Tk_Offset(Table, ipadY), 0},
+    {TK_CONFIG_PIXELS, "-maxheight", "maxHeight", "MaxHeight", "600",
+     Tk_Offset(Table, maxReqHeight), 0},
+    {TK_CONFIG_PIXELS, "-maxwidth", "maxWidth", "MaxWidth", "800",
+     Tk_Offset(Table, maxReqWidth), 0},
+    {TK_CONFIG_BOOLEAN, "-multiline", "multiline", "Multiline", "1",
+     Tk_Offset(Table, defaultTag.multiline), 0},
+    {TK_CONFIG_PIXELS, "-padx", "padX", "Pad", "0", Tk_Offset(Table, padX), 0},
+    {TK_CONFIG_PIXELS, "-pady", "padY", "Pad", "0", Tk_Offset(Table, padY), 0},
+    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", "sunken",
+     Tk_Offset(Table, defaultTag.relief), 0},
+    {TK_CONFIG_CUSTOM, "-resizeborders", "resizeBorders", "ResizeBorders",
+     "both", Tk_Offset(Table, resize), 0, &resizeTypeOpt },
+    {TK_CONFIG_INT, "-rowheight", "rowHeight", "RowHeight", "1",
+     Tk_Offset(Table, defRowHeight), 0},
+    {TK_CONFIG_INT, "-roworigin", "rowOrigin", "Origin", "0",
+     Tk_Offset(Table, rowOffset), 0},
+    {TK_CONFIG_INT, "-rows", "rows", "Rows", "10", Tk_Offset(Table, rows), 0},
+    {TK_CONFIG_STRING, "-rowseparator", "rowSeparator", "Separator", NULL,
+     Tk_Offset(Table, rowSep), TK_CONFIG_NULL_OK },
+    {TK_CONFIG_CUSTOM, "-rowstretchmode", "rowStretch", "StretchMode", "none",
+     Tk_Offset(Table, rowStretch), 0 , &stretchOpt },
+    {TK_CONFIG_STRING, "-rowtagcommand", "rowTagCommand", "TagCommand", NULL,
+     Tk_Offset(Table, rowTagCmd), TK_CONFIG_NULL_OK },
+    {TK_CONFIG_SYNONYM, "-selcmd", "selectionCommand", (char *)NULL,
+     (char *)NULL, 0, TK_CONFIG_NULL_OK},
+    {TK_CONFIG_STRING, "-selectioncommand", "selectionCommand",
+     "SelectionCommand", NULL, Tk_Offset(Table, selCmd), TK_CONFIG_NULL_OK },
+    {TK_CONFIG_STRING, "-selectmode", "selectMode", "SelectMode", "browse",
+     Tk_Offset(Table, selectMode), TK_CONFIG_NULL_OK },
+    {TK_CONFIG_BOOLEAN, "-selecttitles", "selectTitles", "SelectTitles", "0",
+     Tk_Offset(Table, selectTitles), 0},
+    {TK_CONFIG_CUSTOM, "-selecttype", "selectType", "SelectType", "cell",
+     Tk_Offset(Table, selectType), 0, &selTypeOpt },
+#ifdef PROCS
+    {TK_CONFIG_BOOLEAN, "-showprocs", "showProcs", "showProcs", "0",
+     Tk_Offset(Table, showProcs), 0},
+#endif
+    {TK_CONFIG_BOOLEAN, "-sparsearray", "sparseArray", "SparseArray", "1",
+     Tk_Offset(Table, sparse), 0},
+    {TK_CONFIG_CUSTOM, "-state", "state", "State", "normal",
+     Tk_Offset(Table, state), 0, &stateTypeOpt},
+    {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus", (char *)NULL,
+     Tk_Offset(Table, takeFocus), TK_CONFIG_NULL_OK },
+    {TK_CONFIG_INT, "-titlecols", "titleCols", "TitleCols", "0",
+     Tk_Offset(Table, titleCols), TK_CONFIG_NULL_OK },
+    {TK_CONFIG_INT, "-titlerows", "titleRows", "TitleRows", "0",
+     Tk_Offset(Table, titleRows), TK_CONFIG_NULL_OK },
+    {TK_CONFIG_BOOLEAN, "-usecommand", "useCommand", "UseCommand", "1",
+     Tk_Offset(Table, useCmd), 0},
+    {TK_CONFIG_STRING, "-variable", "variable", "Variable", (char *)NULL,
+     Tk_Offset(Table, arrayVar), TK_CONFIG_NULL_OK },
+    {TK_CONFIG_BOOLEAN, "-validate", "validate", "Validate", "0",
+     Tk_Offset(Table, validate), 0},
+    {TK_CONFIG_STRING, "-validatecommand", "validateCommand", "ValidateCommand",
+     "", Tk_Offset(Table, valCmd), TK_CONFIG_NULL_OK},
+    {TK_CONFIG_SYNONYM, "-vcmd", "validateCommand", (char *)NULL,
+     (char *)NULL, 0, TK_CONFIG_NULL_OK},
+    {TK_CONFIG_INT, "-width", "width", "Width", "0",
+     Tk_Offset(Table, maxReqCols), 0},
+    {TK_CONFIG_BOOLEAN, "-wrap", "wrap", "Wrap", "0",
+     Tk_Offset(Table, defaultTag.wrap), 0},
+    {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
+     NULL, Tk_Offset(Table, xScrollCmd), TK_CONFIG_NULL_OK },
+    {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
+     NULL, Tk_Offset(Table, yScrollCmd), TK_CONFIG_NULL_OK },
+    {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+     (char *)NULL, 0, 0}
 };
 
 /*
@@ -402,4472 +327,3526 @@ static Tk_ConfigSpec TableConfig[] = {
  * occur, so we should have a quick lookup table for them.
  * Keep this in sync with the above values.
  */
-static Cmd_Struct update_config[] = {
-  {"-anchor",          1},  {"-background",    1},
-  {"-bg",              1},  {"-bd",            1},
-  {"-borderwidth",     1},  {"-cache",         1},
-  {"-command",         1},  {"-colorigin",     1},
-  {"-cols",            1},  {"-colstretchmode",        1},
-  {"-coltagcommand",   1},  {"-drawmode",      1},
-  {"-fg",              1},  {"-font",          1},
-  {"-foreground",      1},
-  {"-height",          1},  {"-highlightbackground",   1},
-  {"-highlightcolor",  1},  {"-highlightthickness",    1},
-  {"-insertbackground",        1},  {"-insertborderwidth",     1},
-  {"-insertwidth",     1},  {"-invertselected",        1},
-  {"-maxheight",       1},  {"-maxwidth",              1},
-  {"-multiline",       1},
-  {"-padx",            1},  {"-pady",          1},
-  {"-relief",          1},  {"-roworigin",     1},
-  {"-rows",            1},  {"-rowstretchmode",        1},
-  {"-rowtagcommand",   1},  {"-state",         1},
-  {"-titlecols",       1},  {"-titlerows",     1},
-  {"-usecommand",      1},  {"-variable",              1},
-  {"-width",           1},  {"-wrap",          1},
-  {"-xscrollcommand",  1},  {"-yscrollcommand",        1},
-  {"", 0},
+
+static char *updateOpts[] = {
+    "-anchor",         "-background",  "-bg",          "-bd",  
+    "-borderwidth",    "-cache",       "-command",     "-colorigin",
+    "-cols",           "-colstretchmode",              "-coltagcommand",
+    "-drawmode",       "-fg",          "-font",        "-foreground",
+    "-hasprocs",       "-height",      "-highlightbackground",
+    "-highlightcolor", "-highlightthickness",          "-insertbackground",
+    "-insertborderwidth",              "-insertwidth", "-invertselected",
+    "-ipadx",          "-ipady",
+    "-maxheight",      "-maxwidth",    "-multiline",
+    "-padx",           "-pady",        "-relief",      "-roworigin",
+    "-rows",           "-rowstretchmode",              "-rowtagcommand",
+    "-showprocs",      "-state",       "-titlecols",   "-titlerows",
+    "-usecommand",     "-variable",    "-width",       "-wrap",        
+    "-xscrollcommand", "-yscrollcommand", (char *) NULL
 };
 
+#ifdef WIN32
 /*
- * END HEADER INFORMATION
+ * Some code from TkWinInt.h that we use to correct and speed up
+ * drawing of cells that need clipping in TableDisplay.
  */
+typedef struct {
+    int type;
+    HWND handle;
+    void *winPtr;
+} TkWinWindow;
+
+typedef struct {
+    int type;
+    HBITMAP handle;
+    Colormap colormap;
+    int depth;
+} TkWinBitmap;
+
+typedef struct {
+    int type;
+    HDC hdc;
+} TkWinDC;
+
+typedef union {
+    int type;
+    TkWinWindow window;
+    TkWinBitmap bitmap;
+    TkWinDC winDC;
+} TkWinDrawable;
+#endif
 
 /*
- *----------------------------------------------------------------------
+ * END HEADER INFORMATION
+ */
+\f
+/*
+ *---------------------------------------------------------------------------
  *
- * TableFlushCache --
- *     Flushes the internal cache of the table.
+ * StringifyObjects -- (from tclCmdAH.c)
+ *
+ *     Helper function to bridge the gap between an object-based procedure
+ *     and an older string-based procedure.
+ * 
+ *     Given an array of objects, allocate an array that consists of the
+ *     string representations of those objects.
  *
  * Results:
- *     None.
+ *     The return value is a pointer to the newly allocated array of
+ *     strings.  Elements 0 to (objc-1) of the string array point to the
+ *     string representation of the corresponding element in the source
+ *     object array; element objc of the string array is NULL.
  *
  * Side effects:
- *     None.
+ *     Memory allocated.  The caller must eventually free this memory
+ *     by calling ckfree() on the return value.
  *
- *----------------------------------------------------------------------
+           int result;
+           char **argv;
+           argv   = StringifyObjects(objc, objv);
+           result = StringBasedCmd(interp, objc, argv);
+           ckfree((char *) argv);
+           return result;
+ *
+ *---------------------------------------------------------------------------
  */
-INLINE static void
-TableFlushCache(register Table *tablePtr)
+
+static char **
+StringifyObjects(objc, objv)
+     int objc;                 /* Number of arguments. */
+     Tcl_Obj *CONST objv[];    /* Argument objects. */
 {
-  /* Just get rid of it and reinit it */
-  Tcl_DeleteHashTable(tablePtr->cache);
-  ckfree((char *) (tablePtr->cache));
-  tablePtr->cache = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
-  Tcl_InitHashTable(tablePtr->cache, TCL_STRING_KEYS);
+    int i;
+    char **argv;
+    
+    argv = (char **) ckalloc((objc + 1) * sizeof(char *));
+    for (i = 0; i < objc; i++) {
+       argv[i] = Tcl_GetString(objv[i]);
+    }
+    argv[i] = NULL;
+    return argv;
 }
 
-
-
 /*
- *----------------------------------------------------------------------
- *
- * TableRefresh --
- *     Refreshes an area of the table based on the mode.
- *     row,col in real coords (0-based)
- *
- * Results:
- *     Will cause redraw for visible cells
- *
- * Side effects:
- *     None.
+ * As long as we wait for the Function in general
  *
- *----------------------------------------------------------------------
+ * This parses the "-class" option for the table.
  */
-void
-TableRefresh(register Table *tablePtr, int row, int col, int mode)
+static int
+Tk_ClassOptionObjCmd(Tk_Window tkwin, char *defaultclass,
+                    int objc, Tcl_Obj *CONST objv[])
 {
-  int x, y, w, h;
+    char *classname = defaultclass;
+    int offset = 0;
 
-  if (mode & CELL) {
-    if (TableCellVCoords(tablePtr, row, col, &x, &y, &w, &h, 0)) {
-      TableInvalidate(tablePtr, x, y, w, h, mode);
-    }
-  } else if (mode & ROW) {
-    /* get the position of the leftmost cell in the row */
-    if ((mode & INV_FILL) && row < tablePtr->topRow) {
-      /* Invalidate whole table */
-      TableInvalidateAll(tablePtr, mode);
-    } else if (TableCellVCoords(tablePtr, row, tablePtr->leftCol,
-                        &x, &y, &w, &h, 0)) {
-      /* Invalidate from this row, maybe to end */
-      TableInvalidate(tablePtr, 0, y, Tk_Width(tablePtr->tkwin),
-                     (mode&INV_FILL)?Tk_Height(tablePtr->tkwin):h, mode);
-    }
-  } else if (mode & COL) {
-    /* get the position of the topmost cell on the column */
-    if ((mode & INV_FILL) && col < tablePtr->leftCol) {
-      /* Invalidate whole table */
-      TableInvalidateAll(tablePtr, mode);
-    } else if (TableCellVCoords(tablePtr, tablePtr->topRow, col,
-                        &x, &y, &w, &h, 0)) {
-      /* Invalidate from this column, maybe to end */
-      TableInvalidate(tablePtr, x, 0,
-                     (mode&INV_FILL)?Tk_Width(tablePtr->tkwin):w,
-                     Tk_Height(tablePtr->tkwin), mode);
+    if ((objc >= 4) && STREQ(Tcl_GetString(objv[2]),"-class")) {
+       classname = Tcl_GetString(objv[3]);
+       offset = 2;
     }
-  }
+    Tk_SetClass(tkwin, classname);
+    return offset;
 }
 
 /*
- *----------------------------------------------------------------------
+ *--------------------------------------------------------------
  *
- * TableClear --
- *     Clears state information about the table.
+ * Tk_TableObjCmd --
+ *     This procedure is invoked to process the "table" Tcl
+ *     command.  See the user documentation for details on what
+ *     it does.
  *
  * Results:
- *     Cached info can be lost.  Returns valid Tcl result.
+ *     A standard Tcl result.
  *
  * Side effects:
- *     Can cause redraw.
+ *     See the user documentation.
  *
- *----------------------------------------------------------------------
+ *--------------------------------------------------------------
  */
 static int
-TableClear(register Table *tablePtr, int mode, char *first, char *last)
+Tk_TableObjCmd(clientData, interp, objc, objv)
+    ClientData clientData;     /* Main window associated with interpreter. */
+    Tcl_Interp *interp;
+    int objc;                  /* Number of arguments. */
+    Tcl_Obj *CONST objv[];     /* Argument objects. */
 {
-  int redraw = 0;
-
-  if (mode == 0) return TCL_ERROR;
-
-  if (first == NULL) {
-    if (mode & CLEAR_TAGS) {
-      Tcl_DeleteHashTable(tablePtr->rowStyles);
-      Tcl_DeleteHashTable(tablePtr->colStyles);
-      Tcl_DeleteHashTable(tablePtr->cellStyles);
-      Tcl_DeleteHashTable(tablePtr->flashCells);
-      Tcl_DeleteHashTable(tablePtr->selCells);
-
-      /* style hash tables */
-      Tcl_InitHashTable(tablePtr->rowStyles, TCL_ONE_WORD_KEYS);
-      Tcl_InitHashTable(tablePtr->colStyles, TCL_ONE_WORD_KEYS);
-      Tcl_InitHashTable(tablePtr->cellStyles, TCL_STRING_KEYS);
-
-      /* special style hash tables */
-      Tcl_InitHashTable(tablePtr->flashCells, TCL_STRING_KEYS);
-      Tcl_InitHashTable(tablePtr->selCells, TCL_STRING_KEYS);
-    }
-
-    if (mode & CLEAR_SIZES) {
-      Tcl_DeleteHashTable(tablePtr->colWidths);
-      Tcl_DeleteHashTable(tablePtr->rowHeights);
+    register Table *tablePtr;
+    Tk_Window tkwin, mainWin = (Tk_Window) clientData;
+    int offset;
 
-      /* style hash tables */
-      Tcl_InitHashTable(tablePtr->colWidths, TCL_ONE_WORD_KEYS);
-      Tcl_InitHashTable(tablePtr->rowHeights, TCL_ONE_WORD_KEYS);
+    if (objc < 2) {
+       Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");
+       return TCL_ERROR;
     }
 
-    if (mode & CLEAR_CACHE) {
-      TableFlushCache(tablePtr);
-      /* If we were caching and we have no other data source,
-       * invalidate all the cells */
-      if (tablePtr->dataSource == DATA_CACHE) {
-       TableGetActiveBuf(tablePtr);
-      }
+    tkwin = Tk_CreateWindowFromPath(interp, mainWin, Tcl_GetString(objv[1]),
+           (char *)NULL);
+    if (tkwin == NULL) {
+       return TCL_ERROR;
     }
-    redraw = 1;
-  } else {
-    int row, col, r1, r2, c1, c2;
-    Tcl_HashEntry *entryPtr;
-    char buf[INDEX_BUFSIZE];
 
-    if (TableGetIndex(tablePtr, first, &row, &col) == TCL_ERROR ||
-       (last != NULL && TableGetIndex(tablePtr, last, &r2, &c2)==TCL_ERROR)) {
-      return TCL_ERROR;
-    }
-    if (last == NULL) {
-      r1 = r2 = row;
-      c1 = c2 = col;
-    } else {
-      r1 = MIN(row,r2); r2 = MAX(row,r2);
-      c1 = MIN(col,c2); c2 = MAX(col,c2);
-    }
-    for (row = r1; row <= r2; row++) {
-      /* Note that *Styles entries are user based (no offset)
-       * while size entries are 0-based (real) */
-      if ((mode & CLEAR_TAGS) &&
-         (entryPtr = Tcl_FindHashEntry(tablePtr->rowStyles, (char *) row))) {
-       Tcl_DeleteHashEntry(entryPtr);
-       redraw = 1;
-      }
-
-      if ((mode & CLEAR_SIZES) &&
-         (entryPtr = Tcl_FindHashEntry(tablePtr->rowHeights,
-                                       (char *) row-tablePtr->rowOffset))) {
-       Tcl_DeleteHashEntry(entryPtr);
-       redraw = 1;
-      }
-
-      for (col = c1; col <= c2; col++) {
-       TableMakeArrayIndex(row, col, buf);
+    tablePtr                   = (Table *) ckalloc(sizeof(Table));
+    memset((VOID *) tablePtr, 0, sizeof(Table));
 
-       if (mode & CLEAR_TAGS) {
-         if ((row == r1) && (entryPtr = Tcl_FindHashEntry(tablePtr->colStyles,
-                                                          (char *) col))) {
-           Tcl_DeleteHashEntry(entryPtr);
-           redraw = 1;
-         }
-         if ((entryPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf))) {
-           Tcl_DeleteHashEntry(entryPtr);
-           redraw = 1;
-         }
-         if ((entryPtr = Tcl_FindHashEntry(tablePtr->flashCells, buf))) {
-           Tcl_DeleteHashEntry(entryPtr);
-           redraw = 1;
-         }
-         if ((entryPtr = Tcl_FindHashEntry(tablePtr->selCells, buf))) {
-           Tcl_DeleteHashEntry(entryPtr);
-           redraw = 1;
-         }
-       }
+    /*
+     * Set the structure elments that aren't 0/NULL by default,
+     * and that won't be set by the initial configure call.
+     */
+    tablePtr->tkwin            = tkwin;
+    tablePtr->display          = Tk_Display(tkwin);
+    tablePtr->interp           = interp;
+    tablePtr->widgetCmd        = Tcl_CreateObjCommand(interp,
+           Tk_PathName(tablePtr->tkwin), TableWidgetObjCmd,
+           (ClientData) tablePtr, (Tcl_CmdDeleteProc *) TableCmdDeletedProc);
+
+    tablePtr->anchorRow                = -1;
+    tablePtr->anchorCol                = -1;
+    tablePtr->activeRow                = -1;
+    tablePtr->activeCol                = -1;
+    tablePtr->oldTopRow                = -1;
+    tablePtr->oldLeftCol       = -1;
+    tablePtr->oldActRow                = -1;
+    tablePtr->oldActCol                = -1;
+    tablePtr->seen[0]          = -1;
+
+    tablePtr->dataSource       = DATA_NONE;
+    tablePtr->activeBuf                = ckalloc(1);
+    *(tablePtr->activeBuf)     = '\0';
+
+    tablePtr->cursor           = None;
+    tablePtr->bdcursor         = None;
+
+    tablePtr->defaultTag.justify       = TK_JUSTIFY_LEFT;
+    tablePtr->defaultTag.state         = STATE_UNKNOWN;
+
+    /* misc tables */
+    tablePtr->tagTable = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
+    Tcl_InitHashTable(tablePtr->tagTable, TCL_STRING_KEYS);
+    tablePtr->winTable = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
+    Tcl_InitHashTable(tablePtr->winTable, TCL_STRING_KEYS);
+
+    /* internal value cache */
+    tablePtr->cache    = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
+    Tcl_InitHashTable(tablePtr->cache, TCL_STRING_KEYS);
+
+    /* style hash tables */
+    tablePtr->colWidths = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
+    Tcl_InitHashTable(tablePtr->colWidths, TCL_ONE_WORD_KEYS);
+    tablePtr->rowHeights = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
+    Tcl_InitHashTable(tablePtr->rowHeights, TCL_ONE_WORD_KEYS);
+
+    /* style hash tables */
+    tablePtr->rowStyles = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
+    Tcl_InitHashTable(tablePtr->rowStyles, TCL_ONE_WORD_KEYS);
+    tablePtr->colStyles = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
+    Tcl_InitHashTable(tablePtr->colStyles, TCL_ONE_WORD_KEYS);
+    tablePtr->cellStyles = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
+    Tcl_InitHashTable(tablePtr->cellStyles, TCL_STRING_KEYS);
+
+    /* special style hash tables */
+    tablePtr->flashCells = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
+    Tcl_InitHashTable(tablePtr->flashCells, TCL_STRING_KEYS);
+    tablePtr->selCells = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
+    Tcl_InitHashTable(tablePtr->selCells, TCL_STRING_KEYS);
 
-       if ((mode & CLEAR_SIZES) && row == r1 &&
-           (entryPtr = Tcl_FindHashEntry(tablePtr->colWidths, (char *)
-                                         col-tablePtr->colOffset))) {
-         Tcl_DeleteHashEntry(entryPtr);
-         redraw = 1;
-       }
+    /*
+     * List of tags in priority order.  30 is a good default number to alloc.
+     */
+    tablePtr->tagPrioMax = 30;
+    tablePtr->tagPrioNames = (char **) ckalloc(
+       sizeof(char *) * tablePtr->tagPrioMax);
+    tablePtr->tagPrios = (TableTag **) ckalloc(
+       sizeof(TableTag *) * tablePtr->tagPrioMax);
+    tablePtr->tagPrioSize = 0;
+    for (offset = 0; offset < tablePtr->tagPrioMax; offset++) {
+       tablePtr->tagPrioNames[offset] = (char *) NULL;
+       tablePtr->tagPrios[offset] = (TableTag *) NULL;
+    }
+
+#ifdef PROCS
+    tablePtr->inProc = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
+    Tcl_InitHashTable(tablePtr->inProc, TCL_STRING_KEYS);
+#endif
 
-       if ((mode & CLEAR_CACHE) &&
-           (entryPtr = Tcl_FindHashEntry(tablePtr->cache, buf))) {
-         Tcl_DeleteHashEntry(entryPtr);
-         /* if the cache is our data source,
-          * we need to invalidate the cells changed */
-         if ((tablePtr->dataSource == DATA_CACHE) &&
-             (row-tablePtr->rowOffset == tablePtr->activeRow &&
-              col-tablePtr->colOffset == tablePtr->activeCol))
-           TableGetActiveBuf(tablePtr);
-         redraw = 1;
-       }
-      }
-    }
-  }
-  /* This could be more sensitive about what it updates,
-   * but that can actually be a lot more costly in some cases */
-  if (redraw) {
-    if (mode & CLEAR_SIZES) {
-      TableAdjustParams(tablePtr);
-      /* rerequest geometry */
-      TableGeometryRequest(tablePtr);
+    /*
+     * Handle class name and selection handlers
+     */
+    offset = 2 + Tk_ClassOptionObjCmd(tkwin, "Table", objc, objv);
+    Tk_CreateEventHandler(tablePtr->tkwin,
+           PointerMotionMask|ExposureMask|StructureNotifyMask|FocusChangeMask|VisibilityChangeMask,
+           TableEventProc, (ClientData) tablePtr);
+    Tk_CreateSelHandler(tablePtr->tkwin, XA_PRIMARY, XA_STRING,
+           TableFetchSelection, (ClientData) tablePtr, XA_STRING);
+
+    if (TableConfigure(interp, tablePtr, objc - offset, objv + offset,
+           0, 1 /* force update */) != TCL_OK) {
+       Tk_DestroyWindow(tkwin);
+       return TCL_ERROR;
     }
-    TableInvalidateAll(tablePtr, 0);
-  }
-  return TCL_OK;
-}
-
-/* 
- *----------------------------------------------------------------------
- *
- * TableGetGc --
- *     Gets a GC corresponding to the tag structure passed.
- *
- * Results:
- *     Returns usable GC.
- *
- * Side effects:
- *     None
- *
- *----------------------------------------------------------------------
- */
-INLINE static void
-TableGetGc(Display *display, Drawable d, TableTag *tagPtr, GC *tagGc)
-{
-  XGCValues gcValues;
-  gcValues.foreground = Tk_3DBorderColor(tagPtr->fg)->pixel;
-  gcValues.background = Tk_3DBorderColor(tagPtr->bg)->pixel;
-  gcValues.font = Tk_FontId(tagPtr->tkfont);
-  if (*tagGc == NULL) {
-    gcValues.graphics_exposures = False;
-    *tagGc = XCreateGC(display, d,
-                      GCForeground|GCBackground|GCFont|GCGraphicsExposures,
-                      &gcValues);
-  } else {
-    XChangeGC(display, *tagGc, GCForeground|GCBackground|GCFont, &gcValues);
-  }
-}
-
-#define TableFreeGc    XFreeGC
+    TableInitTags(tablePtr);
 
-/* 
- *----------------------------------------------------------------------
- *
- * TableRedrawHighlight --
- *     Redraws just the highlight for the window
- *
- * Results:
- *     None.
- *
- * Side effects:
- *     None
- *
- *----------------------------------------------------------------------
- */
-INLINE static void
-TableRedrawHighlight(Table *tablePtr)
-{
-  if ((tablePtr->flags & REDRAW_BORDER) && tablePtr->highlightWidth > 0) {
-    GC gc = Tk_GCForColor((tablePtr->flags & HAS_FOCUS)
-                         ?(tablePtr->highlightColorPtr)
-                         :(tablePtr->highlightBgColorPtr),
-                         Tk_WindowId(tablePtr->tkwin));
-    Tk_DrawFocusHighlight(tablePtr->tkwin, gc, tablePtr->highlightWidth,
-                         Tk_WindowId(tablePtr->tkwin));
-  }
-  tablePtr->flags &= ~REDRAW_BORDER;
+    Tcl_SetStringObj(Tcl_GetObjResult(interp),
+                    Tk_PathName(tablePtr->tkwin), -1);
+    return TCL_OK;
 }
 
 /*
  *--------------------------------------------------------------
  *
- * TableUndisplay --
- *     This procedure removes the contents of a table window
- *     that have been moved offscreen.
+ * TableWidgetObjCmd --
+ *     This procedure is invoked to process the Tcl command
+ *     that corresponds to a widget managed by this module.
+ *     See the user documentation for details on what it does.
  *
  * Results:
- *     Embedded windows can be unmapped.
+ *     A standard Tcl result.
  *
  * Side effects:
- *     Information disappears from the screen.
+ *     See the user documentation.
  *
  *--------------------------------------------------------------
  */
-static void
-TableUndisplay(register Table *tablePtr)
+static int
+TableWidgetObjCmd(clientData, interp, objc, objv)
+     ClientData clientData;
+     Tcl_Interp *interp;
+     int objc;                 /* Number of arguments. */
+     Tcl_Obj *CONST objv[];    /* Argument objects. */
 {
-  register int *seen = tablePtr->seen;
-  int row, col;
-
-  TableGetLastCell(tablePtr, &row, &col);
-  if (seen[0] != -1) {
-    if (seen[0] < tablePtr->topRow) {
-      /* Remove now hidden rows */
-      EmbWinUnmap(tablePtr, seen[0], tablePtr->topRow-1, 0, seen[3]);
-    }
-    if (seen[1] < tablePtr->leftCol) {
-      /* Remove now hidden cols */
-      EmbWinUnmap(tablePtr, 0, seen[2], seen[1], tablePtr->leftCol-1);
-    }
-    if (seen[2] > row) {
-      /* Remove now off-screen rows */
-      EmbWinUnmap(tablePtr, row+1, seen[2], 0, seen[3]);
+    register Table *tablePtr = (Table *) clientData;
+    int row, col, i, cmdIndex, result = TCL_OK;
+    Tcl_Obj *resultPtr;
+
+    if (objc < 2) {
+       Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
+       return TCL_ERROR;
     }
-    if (seen[3] > col) {
-      /* Remove now off-screen cols */
-      EmbWinUnmap(tablePtr, 0, seen[2], col+1, seen[3]);
+
+    /* parse the first parameter */
+    result = Tcl_GetIndexFromObj(interp, objv[1], commandNames,
+                                "option", 0, &cmdIndex);
+    if (result != TCL_OK) {
+       return result;
     }
-  }
-  seen[0] = tablePtr->topRow;
-  seen[1] = tablePtr->leftCol;
-  seen[2] = row;
-  seen[3] = col;
-}
+    /*
+     * It's important to ensure that between here and setting the resultPtr,
+     * we don't cause the result to change, as that might free resultPtr.
+     */
+    resultPtr = Tcl_GetObjResult(interp);
+    Tcl_Preserve((ClientData) tablePtr);
 
-/*
- *--------------------------------------------------------------
- *
- * TableDisplay --
- *     This procedure redraws the contents of a table window.
- *     The conditional code in this function is due to these factors:
- *             o Lack of XSetClipRectangles on Windows
- *
- * Results:
- *     None.
- *
- * Side effects:
- *     Information appears on the screen.
- *
- *--------------------------------------------------------------
- */
-static void
-TableDisplay(ClientData clientdata)
-{
-  register Table *tablePtr = (Table *) clientdata;
-  Tk_Window tkwin = tablePtr->tkwin;
-  Display *display = tablePtr->display;
-  Drawable window;
-#ifdef _WIN32
-  Drawable clipWind;
-#else
-  XRectangle clipRect;
-#endif
-  int rowFrom, rowTo, colFrom, colTo,
-    invalidX, invalidY, invalidWidth, invalidHeight,
-    x, y, width, height, itemX, itemY, itemW, itemH,
-    row, col, urow, ucol, cx, cy, cw, ch, bd,
-    numBytes, new, boundW, boundH, maxW, maxH,
-    originX, originY, activeCell, clipRectSet, shouldInvert;
-  GC tagGc = NULL, topGc, bottomGc;
-  char *string = NULL;
-  char buf[INDEX_BUFSIZE];
-  TableTag *tagPtr = NULL, *titlePtr, *selPtr, *activePtr, *flashPtr,
-    *rowPtr, *colPtr;
-  Tcl_HashEntry *entryPtr;
-  static XPoint rect[3] = { {0, 0}, {0, 0}, {0, 0} };
-  Tcl_HashTable *colTagsCache = NULL;
-  Tk_TextLayout textLayout = NULL;
-  TableEmbWindow *ewPtr;
-
-  if ((tablePtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
-    return;
-  }
-  tablePtr->flags &= ~REDRAW_PENDING;
-
-  bd = tablePtr->borderWidth;
-  boundW = Tk_Width(tkwin)-tablePtr->highlightWidth;
-  boundH = Tk_Height(tkwin)-tablePtr->highlightWidth;
-
-  /* Constrain drawable to not include highlight borders */
-  invalidX = MAX(tablePtr->highlightWidth, tablePtr->invalidX);
-  invalidY = MAX(tablePtr->highlightWidth, tablePtr->invalidY);
-  invalidWidth = MIN(tablePtr->invalidWidth, MAX(1, boundW-invalidX));
-  invalidHeight = MIN(tablePtr->invalidHeight, MAX(1, boundH-invalidY));
-
-  /* 
-   * if we are using the slow drawing mode with a pixmap 
-   * create the pixmap and adjust x && y for offset in pixmap
-   */
-  if (tablePtr->drawMode == DRAW_MODE_SLOW) {
-    window = Tk_GetPixmap(display, Tk_WindowId(tkwin),
-                         invalidWidth, invalidHeight, Tk_Depth(tkwin));
-  } else {
-    window = Tk_WindowId(tkwin);
-  }
-#ifdef _WIN32
-  clipWind = Tk_GetPixmap(display, window,
-                         invalidWidth, invalidHeight, Tk_Depth(tkwin));
-#endif
+    switch ((enum command) cmdIndex) {
+       case CMD_ACTIVATE:
+           result = Table_ActivateCmd(clientData, interp, objc, objv);
+           break;
 
-  /* set up the permanent tag styles */
-  entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, "title");
-  titlePtr = (TableTag *) Tcl_GetHashValue(entryPtr);
-  entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, "sel");
-  selPtr   = (TableTag *) Tcl_GetHashValue(entryPtr);
-  entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, "active");
-  activePtr= (TableTag *) Tcl_GetHashValue(entryPtr);
-  entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, "flash");
-  flashPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
-
-  /* find out the cells represented by the invalid region */
-  TableWhatCell(tablePtr, invalidX, invalidY, &rowFrom, &colFrom);
-  TableWhatCell(tablePtr, invalidX+invalidWidth-1,
-               invalidY+invalidHeight-1, &rowTo, &colTo);
+       case CMD_BBOX:
+           result = Table_BboxCmd(clientData, interp, objc, objv);
+           break;
 
-#ifdef DEBUG
-  tcl_dprintf(tablePtr->interp, "display %d,%d => %d,%d",
-             rowFrom+tablePtr->rowOffset, colFrom+tablePtr->colOffset,
-             rowTo+tablePtr->rowOffset, colTo+tablePtr->colOffset);
-#endif
+       case CMD_BORDER:
+           result = Table_BorderCmd(clientData, interp, objc, objv);
+           break;
 
-  /* 
-   * Initialize colTagsCache hash table to cache column tag names.
-   */
-  colTagsCache = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
-  Tcl_InitHashTable(colTagsCache, TCL_ONE_WORD_KEYS);
+       case CMD_CGET:
+           if (objc != 3) {
+               Tcl_WrongNumArgs(interp, 2, objv, "option");
+               result = TCL_ERROR;
+           } else {
+               result = Tk_ConfigureValue(interp, tablePtr->tkwin, tableSpecs,
+                       (char *) tablePtr, Tcl_GetString(objv[2]), 0);
+           }
+           break;
 
-  /* Cycle through the cells and display them */
-  for (row = rowFrom; row <= rowTo; row++) {
-    /* 
-     * are we in the 'dead zone' between the
-     * title rows and the first displayed row 
-     */
-    if (row < tablePtr->topRow && row >= tablePtr->titleRows) {
-      row = tablePtr->topRow;
-    }
+       case CMD_CLEAR:
+           result = Table_ClearCmd(clientData, interp, objc, objv);
+           break;
 
-    /* Cache the row in user terms */
-    urow = row+tablePtr->rowOffset;
-
-    /* Get the row tag once for all iterations of col */
-    rowPtr = FindRowColTag(tablePtr, urow, ROW);
-
-    for (col = colFrom; col <= colTo; col++) {
-      activeCell = 0;
-      /* 
-       * are we in the 'dead zone' between the
-       * title cols and the first displayed col
-       */
-      if (col < tablePtr->leftCol && col >= tablePtr->titleCols) {
-       col = tablePtr->leftCol;
-      }
-
-      /* Cache the col in user terms */
-      ucol = col+tablePtr->colOffset;
-
-      /* put the use cell ref into a buffer for the hash lookups */
-      TableMakeArrayIndex(urow, ucol, buf);
-
-      /* get the coordinates for the cell */
-      TableCellCoords(tablePtr, row, col, &x, &y, &width, &height);
-
-      /* Constrain drawn size to the visual boundaries */
-      if (width > boundW-x) {
-       width = boundW-x;
-      }
-      if (height > boundH-y) {
-       height = boundH-y;
-      }
-
-      /* Create the tag here */
-      tagPtr = TableNewTag();
-      /* First, merge in the default tag */
-      TableMergeTag(tagPtr, &(tablePtr->defaultTag));
-
-      if ((entryPtr = Tcl_FindHashEntry(tablePtr->winTable, buf)) != NULL) {
-       ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
-
-       if (ewPtr->tkwin != NULL) {
-         /* Display embedded window instead of text */
-
-         /* if active, make it disabled to avoid unnecessary editing */
-         if ((tablePtr->flags & HAS_ACTIVE)
-             && row == tablePtr->activeRow && col == tablePtr->activeCol) {
-           tablePtr->flags |= ACTIVE_DISABLED;
-         }
-
-         EmbWinDisplay(tablePtr, window, ewPtr, tagPtr,
-                       x, y, width, height);
-
-         if (tablePtr->drawMode == DRAW_MODE_SLOW) {
-           /* Correctly adjust x && y with the offset */
-           x -= invalidX;
-           y -= invalidY;
-         }
-         Tk_Fill3DRectangle(tkwin, window, tagPtr->bg,
-                            x, y, width, height, bd, TK_RELIEF_FLAT);
-
-         goto ImageUsed;
-       }
-      }
-
-      if (tablePtr->drawMode == DRAW_MODE_SLOW) {
-       /* Correctly adjust x && y with the offset */
-       x -= invalidX;
-       y -= invalidY;
-      }
-
-      shouldInvert = 0;
-      /*
-       * get the combined tag structure for the cell
-       * first clear out a new tag structure that we will build in
-       * then add tags as we realize they belong.
-       * Tags with the highest priority are added first
-       */
-
-      /*
-       * Merge colPtr if it exists
-       * let's see if we have the value cached already
-       * if not, run the findColTag routine and cache the value
-       */
-      entryPtr = Tcl_CreateHashEntry(colTagsCache, (char *)ucol, &new);
-      if (new) {
-       colPtr = FindRowColTag(tablePtr, ucol, COL);
-       Tcl_SetHashValue(entryPtr, colPtr);
-      } else {
-       colPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
-      }
-      if (colPtr != (TableTag *) NULL)
-       TableMergeTag(tagPtr, colPtr);
-      /* Merge rowPtr if it exists */
-      if (rowPtr != (TableTag *) NULL)
-       TableMergeTag(tagPtr, rowPtr);
-      /* Am I in the titles */
-      if (row < tablePtr->topRow || col < tablePtr->leftCol)
-       TableMergeTag(tagPtr, titlePtr);
-      /* Does this have a cell tag */
-      if ((entryPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf)) != NULL)
-       TableMergeTag(tagPtr, (TableTag *) Tcl_GetHashValue(entryPtr));
-      /* is this cell selected? */
-      if (Tcl_FindHashEntry(tablePtr->selCells, buf) != NULL) {
-        if (tablePtr->invertSelected && !activeCell) {
-          shouldInvert = 1;
-        } else {
-         TableMergeTag(tagPtr, selPtr);
-       }
-      }
-      /* is this cell active? */
-      if ((tablePtr->flags & HAS_ACTIVE) && tablePtr->state == STATE_NORMAL
-         && row == tablePtr->activeRow && col == tablePtr->activeCol) {
-       if (tagPtr->state == STATE_DISABLED) {
-         tablePtr->flags |= ACTIVE_DISABLED;
-       } else {
-         TableMergeTag(tagPtr, activePtr);
-         activeCell = 1;
-         tablePtr->flags &= ~ACTIVE_DISABLED;
-       }
-      }
-      /* if flash mode is on, is this cell flashing */
-      if (tablePtr->flashMode &&
-         Tcl_FindHashEntry(tablePtr->flashCells, buf) != NULL)
-       TableMergeTag(tagPtr, flashPtr);
-
-      if (shouldInvert) TableInvertTag(tagPtr);
-
-      /*
-       * first fill in a blank rectangle. This is left as a Tk call instead
-       * of a direct X call for Tk compatibilty. The TK_RELIEF_FLAT ensures
-       * that only XFillRectangle is called anyway so the speed is the same
-       */
-      Tk_Fill3DRectangle(tkwin, window, tagPtr->bg,
-                        x, y, width, height, bd, TK_RELIEF_FLAT);
-
-      /*
-       * If an image is in the tag, draw it
-       */
-      if (tagPtr->image != NULL) {
-       Tk_SizeOfImage(tagPtr->image, &itemW, &itemH);
-       /* Handle anchoring of image in cell space */
-       switch (tagPtr->anchor) {
-       case TK_ANCHOR_NW:
-       case TK_ANCHOR_W:
-       case TK_ANCHOR_SW:      /* western position */
-         originX = itemX = 0;
-         break;
-       case TK_ANCHOR_N:
-       case TK_ANCHOR_S:
-       case TK_ANCHOR_CENTER:  /* centered position */
-         itemX = MAX(0, (itemW-width)/2-bd);
-         originX = MAX(0, (width-itemW)/2);
-         break;
-       default:        /* eastern position */
-         itemX = MAX(0, itemW-width-2*bd);
-         originX = MAX(0, width-itemW);
-       }
-       switch (tagPtr->anchor) {
-       case TK_ANCHOR_N:
-       case TK_ANCHOR_NE:
-       case TK_ANCHOR_NW:      /* northern position */
-         originY = itemY = 0;
-         break;
-       case TK_ANCHOR_W:
-       case TK_ANCHOR_E:
-       case TK_ANCHOR_CENTER:  /* centered position */
-         itemY = MAX(0, (itemH-height)/2-bd);
-         originY = MAX(0, (height-itemH)/2);
-         break;
-       default:        /* southern position */
-         itemY = MAX(0, itemH-height-2*bd);
-         originY = MAX(0, height-itemH);
-       }
-       Tk_RedrawImage(tagPtr->image, itemX, itemY,
-                      MIN(itemW, width-originX-2*bd),
-                      MIN(itemH, height-originY-2*bd), window,
-                      x+originX+bd, y+originY+bd);
-       /* Jump to avoid display of the text value */
-       if (tagPtr->showtext == 0)
-         goto ImageUsed;
-      }
-
-      /* get the GC for this particular blend of tags
-       * this creates the GC if it never existed, otherwise it
-       * modifies the one we have */
-      TableGetGc(display, window, tagPtr, &tagGc);
-
-      /* if this is the active cell, use the buffer */
-      if (activeCell) {
-       string = tablePtr->activeBuf;
-      } else {
-       /* Is there a value in the cell? If so, draw it  */
-       string = TableGetCellValue(tablePtr, urow, ucol);
-      }
-
-      numBytes = strlen(string);
-      /* If there is a string, show it */
-      if (activeCell || numBytes) {
-       /* get the dimensions of the string */
-       textLayout = Tk_ComputeTextLayout(tagPtr->tkfont, string, numBytes,
-                                         (tagPtr->wrap>0) ? width : 0,
-                                         tagPtr->justify,
-                                         (tagPtr->multiline>0) ? 0 :
-                                         TK_IGNORE_NEWLINES,
-                                         &itemW, &itemH);
+       case CMD_CONFIGURE:
+           if (objc < 4) {
+               result = Tk_ConfigureInfo(interp, tablePtr->tkwin, tableSpecs,
+                       (char *) tablePtr, (objc == 3) ?
+                       Tcl_GetString(objv[2]) : (char *) NULL, 0);
+           } else {
+               result = TableConfigure(interp, tablePtr, objc - 2, objv + 2,
+                       TK_CONFIG_ARGV_ONLY, 0);
+           }
+           break;
 
-       /* 
-        * Set the origin coordinates of the string to draw using the anchor.
-        * origin represents the (x,y) coordinate of the lower left corner of
-        * the text box, relative to the internal (inside the border) window
-        */
+       case CMD_CURSEL:
+           result = Table_CurselectionCmd(clientData, interp, objc, objv);
+           break;
 
-       /* set the X origin first */
-       switch (tagPtr->anchor) {
-       case TK_ANCHOR_NW:
-       case TK_ANCHOR_W:
-       case TK_ANCHOR_SW:      /* western position */
-         originX = tablePtr->padX;
-         break;
-       case TK_ANCHOR_N:
-       case TK_ANCHOR_S:
-       case TK_ANCHOR_CENTER:  /* centered position */
-         originX = (width-itemW)/2 - bd;
-         break;
-       default:        /* eastern position */
-         originX = width-itemW-2*bd-tablePtr->padX;
-       }
+       case CMD_CURVALUE:
+           result = Table_CurvalueCmd(clientData, interp, objc, objv);
+           break;
 
-       /* then set the Y origin */
-       switch (tagPtr->anchor) {
-       case TK_ANCHOR_N:
-       case TK_ANCHOR_NE:
-       case TK_ANCHOR_NW:      /* northern position */
-         originY = tablePtr->padY;
-         break;
-       case TK_ANCHOR_W:
-       case TK_ANCHOR_E:
-       case TK_ANCHOR_CENTER:  /* centered position */
-         originY = (height-itemH)/2 - bd;
-         break;
-       default:        /* southern position */
-         originY = height-itemH-2*bd-tablePtr->padY;
-       }
+       case CMD_DELETE:
+       case CMD_INSERT:
+           result = Table_EditCmd(clientData, interp, objc, objv);
+           break;
 
-       /*
-        * if this is the selected cell and we are editing
-        * ensure that the cursor will be displayed
-        */
-       if (activeCell) {
-#if (TK_MINOR_VERSION > 0)
-         int insertByte;
+       case CMD_GET:
+           result = Table_GetCmd(clientData, interp, objc, objv);
+           break;
 
-         insertByte = Tcl_UtfAtIndex(string, tablePtr->icursor) - string;
-         Tk_CharBbox(textLayout, MIN(numBytes, insertByte),
-                     &cx, &cy, &cw, &ch);
-#else
-         Tk_CharBbox(textLayout, MIN(numBytes, tablePtr->icursor),
-                     &cx, &cy, &cw, &ch);
-#endif
-         /* we have to fudge with maxW because of odd width
-          * determination for newlines at the end of a line */
-         maxW = width-bd-tablePtr->padX-tablePtr->insertWidth
-           -(cx+MIN(tablePtr->charWidth, cw));
-         maxH = height-bd-tablePtr->padY-(cy+ch);
-         if (originX < tablePtr->padX+bd-cx) {
-           /* cursor off cell to the left */
-           /* use western positioning to cet cursor at left edge
-            * with slight variation to show some text */
-           originX = tablePtr->padX+bd-cx
-             +MIN(cx, width-2*bd-tablePtr->padX-tablePtr->insertWidth);
-         } else if (originX > maxW) {
-           /* cursor off cell to the right */
-           /* use eastern positioning to cet cursor at right edge */
-           originX = maxW;
-         }
-         if (originY < tablePtr->padY+bd-cy) {
-           /* cursor before top of cell */
-           /* use northern positioning to cet cursor at top edge */
-           originY = tablePtr->padY+bd-cy;
-         } else if (originY > maxH) {
-           /* cursor beyond bottom of cell */
-           /* use southern positioning to cet cursor at bottom edge */
-           originY = maxH;
-         }
-         tablePtr->activeLayout = textLayout;
-         tablePtr->activeX = originX;
-         tablePtr->activeY = originY;
-       }
-       /*
-        * use a clip rectangle only if necessary as it means
-        * updating the GC in the server which slows everything down.
-        * The bd offsets allow us to fudge a little more since the
-        * borders are drawn after drawing the string.
-        */
-       if ((clipRectSet = ((originX < bd) || (originY < bd)
-                           || (originX+itemW > width-bd)
-                           || (originY+itemH > height-bd)))) {
-#ifdef _WIN32
-         /* We always draw in the upper-left corner of the clipWind */
-         Tk_Fill3DRectangle(tkwin, clipWind, tagPtr->bg, 0, 0,
-                            width, height, bd, TK_RELIEF_FLAT);
-         Tk_DrawTextLayout(display, clipWind, tagGc, textLayout,
-                           originX+bd, originY+bd, 0, -1);
-         XCopyArea(display, clipWind, window, tagGc, 0, 0,
-                   width, height, x, y);
-#else
-         /* set the clipping rectangle */
-         clipRect.x = x;
-         clipRect.y = y;
-         clipRect.width = width;
-         clipRect.height = height;
-         XSetClipRectangles(display, tagGc, 0, 0, &clipRect, 1, Unsorted);
-#endif
+       case CMD_HEIGHT:
+       case CMD_WIDTH:
+           result = Table_AdjustCmd(clientData, interp, objc, objv);
+           break;
+
+       case CMD_HIDDEN:
+           result = Table_HiddenCmd(clientData, interp, objc, objv);
+           break;
+
+       case CMD_ICURSOR:
+           if (objc != 2 && objc != 3) {
+               Tcl_WrongNumArgs(interp, 2, objv, "?cursorPos?");
+               result = TCL_ERROR;
+               goto done;
+           }
+           if (!(tablePtr->flags & HAS_ACTIVE) ||
+                   (tablePtr->flags & ACTIVE_DISABLED) ||
+                   tablePtr->state == STATE_DISABLED) {
+               Tcl_SetIntObj(resultPtr, -1);
+               goto done;
+           }
+           if (objc == 3) {
+               if (TableGetIcursorObj(tablePtr, objv[2], NULL) != TCL_OK) {
+                   result = TCL_ERROR;
+                   goto done;
+               }
+               TableRefresh(tablePtr, tablePtr->activeRow,
+                       tablePtr->activeCol, CELL);
+           }
+           Tcl_SetIntObj(resultPtr, tablePtr->icursor);
+           break;
+
+       case CMD_INDEX: {
+           char *which = NULL;
+
+           if (objc == 4) {
+               which = Tcl_GetString(objv[3]);
+           }
+           if ((objc < 3 || objc > 4) ||
+                   ((objc == 4) && (strcmp(which, "row")
+                           && strcmp(which, "col")))) {
+               Tcl_WrongNumArgs(interp, 2, objv, "<index> ?row|col?");
+               result = TCL_ERROR;
+           } else if (TableGetIndexObj(tablePtr, objv[2], &row, &col)
+                   != TCL_OK) {
+               result = TCL_ERROR;
+           } else if (objc == 3) {
+               char buf[INDEX_BUFSIZE];
+               /* recreate the index, just in case it got bounded */
+               TableMakeArrayIndex(row, col, buf);
+               Tcl_SetStringObj(resultPtr, buf, -1);
+           } else {    /* INDEX row|col */
+               Tcl_SetIntObj(resultPtr, (*which == 'r') ? row : col);
+           }
+           break;
        }
 
-#ifdef _WIN32  /* no cliprect on windows */
-       if (!clipRectSet)
-#endif
-         Tk_DrawTextLayout(display, window, tagGc, textLayout,
-                           x+originX+bd, y+originY+bd, 0, -1);
-
-#ifndef _WIN32 /* no cliprect on windows */
-       /* reset the clip mask */
-       if (clipRectSet) {
-         XSetClipMask(display, tagGc, None);
-        }
+#ifdef POSTSCRIPT
+       case CMD_POSTSCRIPT:
+           result = Table_PostscriptCmd(clientData, interp, objc, objv);
+           break;
 #endif
 
-       /* if this is the active cell draw the cursor if it's on.
-        * this ignores clip rectangles. */
-       if (activeCell && (tablePtr->flags & CURSOR_ON) &&
-           (originY+bd+cy < height) &&
-           (originX+cx+bd-(tablePtr->insertWidth/2) >= 0)) {
-         /* make sure it will fit in the box */
-         maxW = MAX(0, originY+bd+cy);
-         maxH = MIN(ch, height-maxW);
-         Tk_Fill3DRectangle(tkwin, window, tablePtr->insertBg,
-                            x+originX+cx+bd-(tablePtr->insertWidth/2),
-                            y+maxW, tablePtr->insertWidth,
-                            maxH, 0, TK_RELIEF_FLAT);
-       }
-      }
-
-    ImageUsed:
-      /* Draw the 3d border on the pixmap correctly offset */
-      if (tablePtr->borderWidth) {
-       switch (tablePtr->drawMode) {
-       case DRAW_MODE_SLOW:
-       case DRAW_MODE_TK_COMPAT:
-         Tk_Draw3DRectangle(tkwin, window, tagPtr->bg,
-                            x, y, width, height, bd, tagPtr->relief);
-         break;
-       case DRAW_MODE_FAST:
-         /*
-         ** choose the GCs to get the best approximation
-         ** to the desired drawing style
-         */
-         switch(tagPtr->relief) {
-         case TK_RELIEF_FLAT:
-           topGc = bottomGc = Tk_3DBorderGC(tkwin, tagPtr->bg, TK_3D_FLAT_GC);
+       case CMD_REREAD:
+           if (objc != 2) {
+               Tcl_WrongNumArgs(interp, 2, objv, NULL);
+               result = TCL_ERROR;
+           } else if ((tablePtr->flags & HAS_ACTIVE) &&
+                   !(tablePtr->flags & ACTIVE_DISABLED) &&
+                   tablePtr->state != STATE_DISABLED) {
+               TableGetActiveBuf(tablePtr);
+               TableRefresh(tablePtr, tablePtr->activeRow,
+                       tablePtr->activeCol, CELL|INV_FORCE);
+           }
            break;
-         case TK_RELIEF_RAISED:
-         case TK_RELIEF_RIDGE:
-           topGc    = Tk_3DBorderGC(tkwin, tagPtr->bg, TK_3D_LIGHT_GC);
-           bottomGc = Tk_3DBorderGC(tkwin, tagPtr->bg, TK_3D_DARK_GC);
+
+       case CMD_SCAN:
+           result = Table_ScanCmd(clientData, interp, objc, objv);
            break;
-         default: /* TK_RELIEF_SUNKEN TK_RELIEF_GROOVE */
-           bottomGc = Tk_3DBorderGC(tkwin, tagPtr->bg, TK_3D_LIGHT_GC);
-           topGc    = Tk_3DBorderGC(tkwin, tagPtr->bg, TK_3D_DARK_GC);
+
+       case CMD_SEE:
+           if (objc != 3) {
+               Tcl_WrongNumArgs(interp, 2, objv, "index");
+               result = TCL_ERROR;
+           } else if (TableGetIndexObj(tablePtr, objv[2],
+                   &row, &col) == TCL_ERROR) {
+               result = TCL_ERROR;
+           } else {
+               /* Adjust from user to master coords */
+               row -= tablePtr->rowOffset;
+               col -= tablePtr->colOffset;
+               if (!TableCellVCoords(tablePtr, row, col, &i, &i, &i, &i, 1)) {
+                   tablePtr->topRow  = row-1;
+                   tablePtr->leftCol = col-1;
+                   TableAdjustParams(tablePtr);
+               }
+           }
            break;
-         }
-       
-         /* draw a line with single pixel width */
-         rect[0].x = x + width - 1;
-         rect[0].y = y;
-         rect[1].y = height - 1;
-         rect[2].x = -width + 1;
-         XDrawLines(display, window, bottomGc, rect, 3, CoordModePrevious);
-         rect[0].x = x;
-         rect[0].y = y + height - 1;
-         rect[1].y = -height + 1;
-         rect[2].x = width - 1;
-         XDrawLines(display, window, topGc, rect, 3, CoordModePrevious);
-         break;
-       case DRAW_MODE_SINGLE:
-         topGc = Tk_3DBorderGC(tkwin, tagPtr->bg, TK_3D_DARK_GC);
-         /* draw a line with single pixel width */
-         rect[0].x = x;
-         rect[0].y = y + height - 1;
-         rect[1].y = -height + 1;
-         rect[2].x = width - 1;
-         XDrawLines(display, window, topGc, rect, 3, CoordModePrevious);
-         break;
-       }
-      }
-
-      /* delete the tag structure */
-      ckfree((char *) (tagPtr));
-      if (textLayout && !activeCell) {
-       Tk_FreeTextLayout(textLayout);
-       textLayout = NULL;
-      }
-    }
-  }
-#ifdef _WIN32
-  Tk_FreePixmap(display, clipWind);
-#endif
 
-  /* Take care of removing embedded windows that are no longer in view */
-  TableUndisplay(tablePtr);
-
-  /* copy over and delete the pixmap if we are in slow mode */
-  if (tablePtr->drawMode == DRAW_MODE_SLOW) {
-    /* Get a default valued GC */
-    TableGetGc(display, window, &(tablePtr->defaultTag), &tagGc);
-    XCopyArea(display, window, Tk_WindowId(tkwin), tagGc, 0, 0,
-             invalidWidth, invalidHeight, invalidX, invalidY);
-    Tk_FreePixmap(display, window);
-    window = Tk_WindowId(tkwin);
-  }
-
-  /* 
-   * if we have got to the end of the table, 
-   * clear the area after the last row/col
-   */
-  TableCellCoords(tablePtr, tablePtr->rows-1, tablePtr->cols-1,
-                 &x, &y, &width, &height);
-
-  /* This should occur before moving pixmap, but this simplifies things
-   *
-   * Could use Tk_Fill3DRectangle instead of XFillRectangle
-   * for best compatibility, and XClearArea could be used on Unix
-   * for best speed, so this is the compromise w/o #ifdef's
-   */
-  if (x+width < invalidX+invalidWidth) {
-    XFillRectangle(display, window,
-                  Tk_3DBorderGC(tkwin, tablePtr->defaultTag.bg,
-                                TK_3D_FLAT_GC), x+width, invalidY,
-                  invalidX+invalidWidth-x-width, invalidHeight);
-  }
-
-  if (y+height < invalidY+invalidHeight) {
-    XFillRectangle(display, window,
-                  Tk_3DBorderGC(tkwin, tablePtr->defaultTag.bg,
-                                TK_3D_FLAT_GC), invalidX, y+height,
-                  invalidWidth, invalidY+invalidHeight-y-height);
-  }
-
-  if (tagGc != NULL) {
-    TableFreeGc(display, tagGc);
-  }
-  TableRedrawHighlight(tablePtr);
-  /* 
-   * Free the hash table used to cache evaluations.
-   */
-  Tcl_DeleteHashTable(colTagsCache);
-  ckfree((char *) (colTagsCache));
-}
+       case CMD_SELECTION:
+           if (objc < 3) {
+               Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?");
+               result = TCL_ERROR;
+               break;
+           }
+           if (Tcl_GetIndexFromObj(interp, objv[2], selCmdNames,
+                   "selection option", 0, &cmdIndex) != TCL_OK) {
+               result = TCL_ERROR;
+               break;
+           }
+           switch ((enum selCommand) cmdIndex) {
+               case CMD_SEL_ANCHOR:
+                   result = Table_SelAnchorCmd(clientData, interp,
+                           objc, objv);
+                   break;
+               case CMD_SEL_CLEAR:
+                   result = Table_SelClearCmd(clientData, interp, objc, objv);
+                   break;
+               case CMD_SEL_INCLUDES:
+                   result = Table_SelIncludesCmd(clientData, interp,
+                           objc, objv);
+                   break;
+               case CMD_SEL_PRESENT: {
+                   Tcl_HashSearch search;
+
+                   Tcl_SetBooleanObj(resultPtr,
+                           (Tcl_FirstHashEntry(tablePtr->selCells, &search)
+                                   != NULL));
+                   break;
+               }
+               case CMD_SEL_SET:
+                   result = Table_SelSetCmd(clientData, interp, objc, objv);
+                   break;
+           }
+           break;
 
-/* 
- *----------------------------------------------------------------------
- *
- * TableInvalidate --
- *     Invalidates a rectangle and adds it to the total invalid rectangle
- *     waiting to be redrawn.  If the INV_FORCE flag bit is set,
- *     it does an update instantly else waits until Tk is idle.
- *
- * Results:
- *     Will schedule table (re)display.
- *
- * Side effects:
- *     None
- *
- *----------------------------------------------------------------------
- */
-void
-TableInvalidate(Table * tablePtr, int x, int y,
-               int width, int height, int flags)
-{
-  register int hl = tablePtr->highlightWidth;
-  register Tk_Window tkwin = tablePtr->tkwin;
-
-  /* make sure that the window hasn't been destroyed already */
-  /* avoid allocating 0 sized pixmaps which would be fatal */
-  /* and check if rectangle is even on the screen */
-  if ((tkwin == NULL) || (width <= 0) || (height <= 0)
-      || (x > Tk_Width(tkwin)) || (y > Tk_Height(tkwin))) return;
-
-  /* If not even mapped, wait for the remap to redraw all */
-  if (!Tk_IsMapped(tkwin)) {
-    tablePtr->flags |= REDRAW_ON_MAP;
-    return;
-  }
-
-  /* if no pending updates then replace the rectangle,
-   * otherwise find the bounding rectangle */
-  if ((flags & INV_HIGHLIGHT) &&
-      (x < hl || y < hl || x+width >= Tk_Width(tkwin)-hl ||
-      y+height >= Tk_Height(tkwin)-hl)) {
-    tablePtr->flags |= REDRAW_BORDER;
-  }
-
-  if (tablePtr->flags & REDRAW_PENDING) {
-    tablePtr->invalidWidth  = MAX(tablePtr->invalidX+tablePtr->invalidWidth,
-                                 x + width);
-    tablePtr->invalidHeight = MAX(tablePtr->invalidY+tablePtr->invalidHeight,
-                                 y + height);
-    if (tablePtr->invalidX > x) tablePtr->invalidX = x;
-    if (tablePtr->invalidY > y) tablePtr->invalidY = y;
-    tablePtr->invalidWidth  -= tablePtr->invalidX;
-    tablePtr->invalidHeight -= tablePtr->invalidY;
-    /* are we forcing this update out */
-    if (flags & INV_FORCE) {
-      Tcl_CancelIdleCall(TableDisplay, (ClientData) tablePtr);
-      TableDisplay((ClientData) tablePtr);
-    }
-  } else {
-    tablePtr->invalidX = x;
-    tablePtr->invalidY = y;
-    tablePtr->invalidWidth = width;
-    tablePtr->invalidHeight = height;
-    if (flags & INV_FORCE) {
-      TableDisplay((ClientData) tablePtr);
-    } else {
-      tablePtr->flags |= REDRAW_PENDING;
-      Tcl_DoWhenIdle(TableDisplay, (ClientData) tablePtr);
+       case CMD_SET:
+           result = Table_SetCmd(clientData, interp, objc, objv);
+           break;
+
+       case CMD_SPANS:
+           result = Table_SpanCmd(clientData, interp, objc, objv);
+           break;
+
+       case CMD_TAG:
+           result = Table_TagCmd(clientData, interp, objc, objv);
+           break;
+
+       case CMD_VALIDATE:
+           if (objc != 3) {
+               Tcl_WrongNumArgs(interp, 2, objv, "index");
+               result = TCL_ERROR;
+           } else if (TableGetIndexObj(tablePtr, objv[2],
+                   &row, &col) == TCL_ERROR) {
+               result = TCL_ERROR;
+           } else {
+               i = tablePtr->validate;
+               tablePtr->validate = 1;
+               result = TableValidateChange(tablePtr, row, col, (char *) NULL,
+                       (char *) NULL, -1);
+               tablePtr->validate = i;
+               Tcl_SetBooleanObj(resultPtr, (result == TCL_OK));
+               result = TCL_OK;
+           }
+           break;
+
+       case CMD_VERSION:
+           if (objc != 2) {
+               Tcl_WrongNumArgs(interp, 2, objv, NULL);
+               result = TCL_ERROR;
+           } else {
+               Tcl_SetStringObj(resultPtr, TBL_VERSION, -1);
+           }
+           break;
+
+       case CMD_WINDOW:
+           result = Table_WindowCmd(clientData, interp, objc, objv);
+           break;
+
+       case CMD_XVIEW:
+       case CMD_YVIEW:
+           result = Table_ViewCmd(clientData, interp, objc, objv);
+           break;
     }
-  }
+
+    done:
+    Tcl_Release((ClientData) tablePtr);
+    return result;
 }
 
-/* 
+/*
  *----------------------------------------------------------------------
  *
- * TableFlashEvent --
- *     Called when the flash timer goes off.
+ * TableDestroy --
+ *     This procedure is invoked by Tcl_EventuallyFree
+ *     to clean up the internal structure of a table at a safe time
+ *     (when no-one is using it anymore).
  *
  * Results:
- *     Decrements all the entries in the hash table and invalidates
- *     any cells that expire, deleting them from the table.  If the
- *     table is now empty, stops the timer, else reenables it.
+ *     None.
  *
  * Side effects:
- *     None.
+ *     Everything associated with the table is freed up (hopefully).
  *
  *----------------------------------------------------------------------
  */
 static void
-TableFlashEvent(ClientData clientdata)
+TableDestroy(ClientData clientdata)
 {
-  Table *tablePtr = (Table *) clientdata;
-  Tcl_HashEntry *entryPtr;
-  Tcl_HashSearch search;
-  int entries, count, row, col;
-
-  entries = 0;
-  for (entryPtr = Tcl_FirstHashEntry(tablePtr->flashCells, &search);
-       entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
-    count = (int) Tcl_GetHashValue(entryPtr);
-    if (--count <= 0) {
-      /* get the cell address and invalidate that region only */
-      TableParseArrayIndex(&row, &col,
-                          Tcl_GetHashKey(tablePtr->flashCells, entryPtr));
-
-      /* delete the entry from the table */
-      Tcl_DeleteHashEntry(entryPtr);
-
-      TableRefresh(tablePtr, row-tablePtr->rowOffset,
-                  col-tablePtr->colOffset, CELL|INV_FORCE);
-    } else {
-      Tcl_SetHashValue(entryPtr, (ClientData) count);
-      entries++;
+    register Table *tablePtr = (Table *) clientdata;
+    Tcl_HashEntry *entryPtr;
+    Tcl_HashSearch search;
+
+    /* These may be repetitive from DestroyNotify, but it doesn't hurt */
+    /* cancel any pending update or timer */
+    if (tablePtr->flags & REDRAW_PENDING) {
+       Tcl_CancelIdleCall(TableDisplay, (ClientData) tablePtr);
+       tablePtr->flags &= ~REDRAW_PENDING;
+    }
+    Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
+    Tcl_DeleteTimerHandler(tablePtr->flashTimer);
+
+    /* delete the variable trace */
+    if (tablePtr->arrayVar != NULL) {
+       Tcl_UntraceVar(tablePtr->interp, tablePtr->arrayVar,
+               TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY,
+               (Tcl_VarTraceProc *)TableVarProc, (ClientData) tablePtr);
+    }
+
+    /* free the int arrays */
+    if (tablePtr->colPixels) ckfree((char *) tablePtr->colPixels);
+    if (tablePtr->rowPixels) ckfree((char *) tablePtr->rowPixels);
+    if (tablePtr->colStarts) ckfree((char *) tablePtr->colStarts);
+    if (tablePtr->rowStarts) ckfree((char *) tablePtr->rowStarts);
+
+    /* delete cached active tag and string */
+    if (tablePtr->activeTagPtr) ckfree((char *) tablePtr->activeTagPtr);
+    if (tablePtr->activeBuf != NULL) ckfree(tablePtr->activeBuf);
+
+    /* delete the cache, row, column and cell style hash tables */
+    Tcl_DeleteHashTable(tablePtr->cache);
+    ckfree((char *) (tablePtr->cache));
+    Tcl_DeleteHashTable(tablePtr->rowStyles);
+    ckfree((char *) (tablePtr->rowStyles));
+    Tcl_DeleteHashTable(tablePtr->colStyles);
+    ckfree((char *) (tablePtr->colStyles));
+    Tcl_DeleteHashTable(tablePtr->cellStyles);
+    ckfree((char *) (tablePtr->cellStyles));
+    Tcl_DeleteHashTable(tablePtr->flashCells);
+    ckfree((char *) (tablePtr->flashCells));
+    Tcl_DeleteHashTable(tablePtr->selCells);
+    ckfree((char *) (tablePtr->selCells));
+    Tcl_DeleteHashTable(tablePtr->colWidths);
+    ckfree((char *) (tablePtr->colWidths));
+    Tcl_DeleteHashTable(tablePtr->rowHeights);
+    ckfree((char *) (tablePtr->rowHeights));
+#ifdef PROCS
+    Tcl_DeleteHashTable(tablePtr->inProc);
+    ckfree((char *) (tablePtr->inProc));
+#endif
+    if (tablePtr->spanTbl) {
+       for (entryPtr = Tcl_FirstHashEntry(tablePtr->spanTbl, &search);
+            entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
+           ckfree((char *) Tcl_GetHashValue(entryPtr));
+       }
+       Tcl_DeleteHashTable(tablePtr->spanTbl);
+       ckfree((char *) (tablePtr->spanTbl));
+       Tcl_DeleteHashTable(tablePtr->spanAffTbl);
+       ckfree((char *) (tablePtr->spanAffTbl));
+    }
+
+    /* Now free up all the tag information */
+    for (entryPtr = Tcl_FirstHashEntry(tablePtr->tagTable, &search);
+        entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
+       TableCleanupTag(tablePtr, (TableTag *) Tcl_GetHashValue(entryPtr));
+       ckfree((char *) Tcl_GetHashValue(entryPtr));
+    }
+    /* free up the stuff in the default tag */
+    TableCleanupTag(tablePtr, &(tablePtr->defaultTag));
+    /* And delete the actual hash table */
+    Tcl_DeleteHashTable(tablePtr->tagTable);
+    ckfree((char *) (tablePtr->tagTable));
+    ckfree((char *) (tablePtr->tagPrios));
+    ckfree((char *) (tablePtr->tagPrioNames));
+
+    /* Now free up all the embedded window info */
+    for (entryPtr = Tcl_FirstHashEntry(tablePtr->winTable, &search);
+        entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
+       EmbWinDelete(tablePtr, (TableEmbWindow *) Tcl_GetHashValue(entryPtr));
     }
-  }
-
-  /* do I need to restart the timer */
-  if (entries && tablePtr->flashMode)
-    tablePtr->flashTimer = Tcl_CreateTimerHandler(250, TableFlashEvent,
-                                                 (ClientData) tablePtr);
-  else
-    tablePtr->flashTimer = 0;
+    /* And delete the actual hash table */
+    Tcl_DeleteHashTable(tablePtr->winTable);
+    ckfree((char *) (tablePtr->winTable));
+
+    /* free the configuration options in the widget */
+    Tk_FreeOptions(tableSpecs, (char *) tablePtr, tablePtr->display, 0);
+
+    /* and free the widget memory at last! */
+    ckfree((char *) (tablePtr));
 }
 
-/* 
+/*
  *----------------------------------------------------------------------
  *
- * TableAddFlash --
- *     Adds a flash on cell row,col (real coords) with the default timeout
- *     if flashing is enabled and flashtime > 0.
+ * TableConfigure --
+ *     This procedure is called to process an objc/objv list, plus
+ *     the Tk option database, in order to configure (or reconfigure)
+ *     a table widget.
  *
  * Results:
- *     Cell will flash.
+ *     The return value is a standard Tcl result.  If TCL_ERROR is
+ *     returned, then interp result contains an error message.
  *
  * Side effects:
- *     Will start flash timer if it didn't exist.
+ *     Configuration information, such as colors, border width, etc.
+ *     get set for tablePtr; old resources get freed, if there were any.
+ *     Certain values might be constrained.
  *
  *----------------------------------------------------------------------
  */
-static void
-TableAddFlash(Table *tablePtr, int row, int col)
+static int
+TableConfigure(interp, tablePtr, objc, objv, flags, forceUpdate)
+     Tcl_Interp *interp;       /* Used for error reporting. */
+     register Table *tablePtr; /* Information about widget;  may or may
+                                * not already have values for some fields. */
+     int objc;                 /* Number of arguments. */
+     Tcl_Obj *CONST objv[];    /* Argument objects. */
+     int flags;                        /* Flags to pass to Tk_ConfigureWidget. */
+     int forceUpdate;          /* Whether to force an update - required
+                                * for initial configuration */
 {
-  char buf[INDEX_BUFSIZE];
-  int dummy;
-  Tcl_HashEntry *entryPtr;
+    Tcl_HashSearch search;
+    int oldUse, oldCaching, oldExport, oldTitleRows, oldTitleCols;
+    int result = TCL_OK;
+    char *oldVar = NULL, **argv;
+    Tcl_DString error;
+    Tk_FontMetrics fm;
+
+    oldExport  = tablePtr->exportSelection;
+    oldCaching = tablePtr->caching;
+    oldUse     = tablePtr->useCmd;
+    oldTitleRows       = tablePtr->titleRows;
+    oldTitleCols       = tablePtr->titleCols;
+    if (tablePtr->arrayVar != NULL) {
+       oldVar = ckalloc(strlen(tablePtr->arrayVar) + 1);
+       strcpy(oldVar, tablePtr->arrayVar);
+    }
+
+    /* Do the configuration */
+    argv = StringifyObjects(objc, objv);
+    result = Tk_ConfigureWidget(interp, tablePtr->tkwin, tableSpecs,
+           objc, argv, (char *) tablePtr, flags);
+    ckfree((char *) argv);
+    if (result != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    Tcl_DStringInit(&error);
+
+    /* Any time we configure, reevaluate what our data source is */
+    tablePtr->dataSource = DATA_NONE;
+    if (tablePtr->caching) {
+       tablePtr->dataSource |= DATA_CACHE;
+    }
+    if (tablePtr->command && tablePtr->useCmd) {
+       tablePtr->dataSource |= DATA_COMMAND;
+    } else if (tablePtr->arrayVar) {
+       tablePtr->dataSource |= DATA_ARRAY;
+    }
+
+    /* Check to see if the array variable was changed */
+    if (strcmp((tablePtr->arrayVar ? tablePtr->arrayVar : ""),
+           (oldVar ? oldVar : ""))) {
+       /* only do the following if arrayVar is our data source */
+       if (tablePtr->dataSource & DATA_ARRAY) {
+           /*
+            * ensure that the cache will flush later
+            * so it gets the new values
+            */
+           oldCaching = !(tablePtr->caching);
+       }
+       /* remove the trace on the old array variable if there was one */
+       if (oldVar != NULL)
+           Tcl_UntraceVar(interp, oldVar,
+                   TCL_TRACE_WRITES|TCL_TRACE_UNSETS|TCL_GLOBAL_ONLY,
+                   (Tcl_VarTraceProc *)TableVarProc, (ClientData) tablePtr);
+       /* Check whether variable is an array and trace it if it is */
+       if (tablePtr->arrayVar != NULL) {
+           /* does the variable exist as an array? */
+           if (Tcl_SetVar2(interp, tablePtr->arrayVar, TEST_KEY, "",
+                   TCL_GLOBAL_ONLY) == NULL) {
+               Tcl_DStringAppend(&error, "invalid variable value \"", -1);
+               Tcl_DStringAppend(&error, tablePtr->arrayVar, -1);
+               Tcl_DStringAppend(&error, "\": could not be made an array",
+                       -1);
+               ckfree(tablePtr->arrayVar);
+               tablePtr->arrayVar = NULL;
+               tablePtr->dataSource &= ~DATA_ARRAY;
+               result = TCL_ERROR;
+           } else {
+               Tcl_UnsetVar2(interp, tablePtr->arrayVar, TEST_KEY,
+                       TCL_GLOBAL_ONLY);
+               /* remove the effect of the evaluation */
+               /* set a trace on the variable */
+               Tcl_TraceVar(interp, tablePtr->arrayVar,
+                       TCL_TRACE_WRITES|TCL_TRACE_UNSETS|TCL_GLOBAL_ONLY,
+                       (Tcl_VarTraceProc *)TableVarProc,
+                       (ClientData) tablePtr);
+
+               /* only do the following if arrayVar is our data source */
+               if (tablePtr->dataSource & DATA_ARRAY) {
+                   /* get the current value of the selection */
+                   TableGetActiveBuf(tablePtr);
+               }
+           }
+       }
+    }
+
+    /* Free oldVar if it was allocated */
+    if (oldVar != NULL) ckfree(oldVar);
+
+    if ((tablePtr->command && tablePtr->useCmd && !oldUse) ||
+       (tablePtr->arrayVar && !(tablePtr->useCmd) && oldUse)) {
+       /*
+        * Our effective data source changed, so flush and
+        * retrieve new active buffer
+        */
+       Tcl_DeleteHashTable(tablePtr->cache);
+       Tcl_InitHashTable(tablePtr->cache, TCL_STRING_KEYS);
+       TableGetActiveBuf(tablePtr);
+       forceUpdate = 1;
+    } else if (oldCaching != tablePtr->caching) {
+       /*
+        * Caching changed, so just clear the cache for safety
+        */
+       Tcl_DeleteHashTable(tablePtr->cache);
+       Tcl_InitHashTable(tablePtr->cache, TCL_STRING_KEYS);
+       forceUpdate = 1;
+    }
+
+    /*
+     * Set up the default column width and row height
+     */
+    Tk_GetFontMetrics(tablePtr->defaultTag.tkfont, &fm);
+    tablePtr->charWidth  = Tk_TextWidth(tablePtr->defaultTag.tkfont, "0", 1);
+    tablePtr->charHeight = fm.linespace + 2;
 
-  if (!tablePtr->flashMode || tablePtr->flashTime < 1)
-    return;
+    if (tablePtr->insertWidth <= 0) {
+       tablePtr->insertWidth = 2;
+    }
+    if (tablePtr->insertBorderWidth > tablePtr->insertWidth/2) {
+       tablePtr->insertBorderWidth = tablePtr->insertWidth/2;
+    }
+    tablePtr->highlightWidth = MAX(0,tablePtr->highlightWidth);
+
+    /*
+     * Ensure that certain values are within proper constraints
+     */
+    tablePtr->rows             = MAX(1, tablePtr->rows);
+    tablePtr->cols             = MAX(1, tablePtr->cols);
+    tablePtr->padX             = MAX(0, tablePtr->padX);
+    tablePtr->padY             = MAX(0, tablePtr->padY);
+    tablePtr->ipadX            = MAX(0, tablePtr->ipadX);
+    tablePtr->ipadY            = MAX(0, tablePtr->ipadY);
+    tablePtr->maxReqCols       = MAX(0, tablePtr->maxReqCols);
+    tablePtr->maxReqRows       = MAX(0, tablePtr->maxReqRows);
+    CONSTRAIN(tablePtr->titleRows, 0, tablePtr->rows);
+    CONSTRAIN(tablePtr->titleCols, 0, tablePtr->cols);
+
+    /*
+     * Handle change of default border style
+     * The default borderwidth must be >= 0.
+     */
+    if (tablePtr->drawMode & (DRAW_MODE_SINGLE|DRAW_MODE_FAST)) {
+       /*
+        * When drawing fast or single, the border must be <= 1.
+        * We have to do this after the normal configuration
+        * to base the borders off the first value given.
+        */
+       tablePtr->defaultTag.bd[0]      = MIN(1, tablePtr->defaultTag.bd[0]);
+       tablePtr->defaultTag.borders    = 1;
+       ckfree((char *) tablePtr->defaultTag.borderStr);
+       tablePtr->defaultTag.borderStr  = (char *) ckalloc(2);
+       strcpy(tablePtr->defaultTag.borderStr,
+               tablePtr->defaultTag.bd[0] ? "1" : "0");
+    }
 
-  /* create the array index in user coords */
-  TableMakeArrayIndex(row+tablePtr->rowOffset, col+tablePtr->colOffset, buf);
+    /*
+     * Claim the selection if we've suddenly started exporting it and
+     * there is a selection to export.
+     */
+    if (tablePtr->exportSelection && !oldExport &&
+       (Tcl_FirstHashEntry(tablePtr->selCells, &search) != NULL)) {
+       Tk_OwnSelection(tablePtr->tkwin, XA_PRIMARY, TableLostSelection,
+               (ClientData) tablePtr);
+    }
 
-  /* add the flash to the hash table */
-  entryPtr = Tcl_CreateHashEntry(tablePtr->flashCells, buf, &dummy);
-  Tcl_SetHashValue(entryPtr, tablePtr->flashTime);
+    if ((tablePtr->titleRows < oldTitleRows) ||
+       (tablePtr->titleCols < oldTitleCols)) {
+       /*
+        * Prevent odd movement due to new possible topleft index
+        */
+       if (tablePtr->titleRows < oldTitleRows)
+           tablePtr->topRow -= oldTitleRows - tablePtr->titleRows;
+       if (tablePtr->titleCols < oldTitleCols)
+           tablePtr->leftCol -= oldTitleCols - tablePtr->titleCols;
+       /*
+        * If our title area shrank, we need to check that the items
+        * within the new title area don't try to span outside it.
+        */
+       TableSpanSanCheck(tablePtr);
+    }
 
-  /* now set the timer if it's not already going and invalidate the area */
-  if (tablePtr->flashTimer == NULL)
-    tablePtr->flashTimer = Tcl_CreateTimerHandler(250, TableFlashEvent,
-                                                 (ClientData) tablePtr);
+    /*
+     * Only do the full reconfigure if absolutely necessary
+     */
+    if (!forceUpdate) {
+       int i, dummy;
+       for (i = 0; i < objc-1; i += 2) {
+           if (Tcl_GetIndexFromObj(NULL, objv[i], updateOpts, "", 0, &dummy)
+                   == TCL_OK) {
+               forceUpdate = 1;
+               break;
+           }
+       }
+    }
+    if (forceUpdate) {
+       /* 
+        * Calculate the row and column starts 
+        * Adjust the top left corner of the internal display 
+        */
+       TableAdjustParams(tablePtr);
+       /* reset the cursor */
+       TableConfigCursor(tablePtr);
+       /* set up the background colour in the window */
+       Tk_SetBackgroundFromBorder(tablePtr->tkwin, tablePtr->defaultTag.bg);
+       /* set the geometry and border */
+       TableGeometryRequest(tablePtr);
+       Tk_SetInternalBorder(tablePtr->tkwin, tablePtr->highlightWidth);
+       /* invalidate the whole table */
+       TableInvalidateAll(tablePtr, INV_HIGHLIGHT);
+    }
+    /*
+     * FIX this is goofy because the result could be munged by other
+     * functions.  Could be improved.
+     */
+    Tcl_ResetResult(interp);
+    if (result == TCL_ERROR) {
+       Tcl_AddErrorInfo(interp, "\t(configuring table widget)");
+       Tcl_DStringResult(interp, &error);
+    }
+    Tcl_DStringFree(&error);
+    return result;
 }
 
 /*
- *----------------------------------------------------------------------
+ *--------------------------------------------------------------
  *
- * TableSetActiveIndex --
- *     Sets the "active" index of the associated array to the current
- *     value of the active buffer.
+ * TableEventProc --
+ *     This procedure is invoked by the Tk dispatcher for various
+ *     events on tables.
  *
  * Results:
  *     None.
  *
  * Side effects:
- *     Traces on the array can cause side effects.
+ *     When the window gets deleted, internal structures get
+ *     cleaned up.  When it gets exposed, it is redisplayed.
  *
- *----------------------------------------------------------------------
+ *--------------------------------------------------------------
  */
 static void
-TableSetActiveIndex(register Table *tablePtr)
+TableEventProc(clientData, eventPtr)
+    ClientData clientData;     /* Information about window. */
+    XEvent *eventPtr;          /* Information about event. */
 {
-  if (tablePtr->arrayVar) {
-    tablePtr->flags |= SET_ACTIVE;
-    Tcl_SetVar2(tablePtr->interp, tablePtr->arrayVar, "active",
-               tablePtr->activeBuf, TCL_GLOBAL_ONLY);
-    tablePtr->flags &= ~SET_ACTIVE;
-  }
+    Table *tablePtr = (Table *) clientData;
+    int row, col;
+
+    switch (eventPtr->type) {
+       case MotionNotify:
+           if (!(tablePtr->resize & SEL_NONE)
+                   && (tablePtr->bdcursor != None) &&
+                   TableAtBorder(tablePtr, eventPtr->xmotion.x,
+                           eventPtr->xmotion.y, &row, &col) &&
+                   ((row>=0 && (tablePtr->resize & SEL_ROW)) ||
+                           (col>=0 && (tablePtr->resize & SEL_COL)))) {
+               /*
+                * The bordercursor is defined and we meet the criteria for
+                * being over a border.  Set the cursor to border if not
+                * already done.
+                */
+               if (!(tablePtr->flags & OVER_BORDER)) {
+                   tablePtr->flags |= OVER_BORDER;
+                   Tk_DefineCursor(tablePtr->tkwin, tablePtr->bdcursor);
+               }
+           } else if (tablePtr->flags & OVER_BORDER) {
+               tablePtr->flags &= ~OVER_BORDER;
+               if (tablePtr->cursor != None) {
+                   Tk_DefineCursor(tablePtr->tkwin, tablePtr->cursor);
+               } else {
+                   Tk_UndefineCursor(tablePtr->tkwin);
+               }
+           }
+           break;
+
+       case Expose:
+           TableInvalidate(tablePtr, eventPtr->xexpose.x, eventPtr->xexpose.y,
+                   eventPtr->xexpose.width, eventPtr->xexpose.height,
+                   INV_HIGHLIGHT);
+           break;
+
+       case DestroyNotify:
+           /* remove the command from the interpreter */
+           if (tablePtr->tkwin != NULL) {
+               tablePtr->tkwin = NULL;
+               Tcl_DeleteCommandFromToken(tablePtr->interp,
+                       tablePtr->widgetCmd);
+           }
+
+           /* cancel any pending update or timer */
+           if (tablePtr->flags & REDRAW_PENDING) {
+               Tcl_CancelIdleCall(TableDisplay, (ClientData) tablePtr);
+               tablePtr->flags &= ~REDRAW_PENDING;
+           }
+           Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
+           Tcl_DeleteTimerHandler(tablePtr->flashTimer);
+
+           Tcl_EventuallyFree((ClientData) tablePtr,
+                   (Tcl_FreeProc *) TableDestroy);
+           break;
+
+       case MapNotify: /* redraw table when remapped if it changed */
+           if (tablePtr->flags & REDRAW_ON_MAP) {
+               tablePtr->flags &= ~REDRAW_ON_MAP;
+               Tcl_Preserve((ClientData) tablePtr);
+               TableAdjustParams(tablePtr);
+               TableInvalidateAll(tablePtr, INV_HIGHLIGHT);
+               Tcl_Release((ClientData) tablePtr);
+           }
+           break;
+
+       case ConfigureNotify:
+           Tcl_Preserve((ClientData) tablePtr);
+           TableAdjustParams(tablePtr);
+           TableInvalidateAll(tablePtr, INV_HIGHLIGHT);
+           Tcl_Release((ClientData) tablePtr);
+           break;
+
+       case FocusIn:
+       case FocusOut:
+           if (eventPtr->xfocus.detail != NotifyInferior) {
+               tablePtr->flags |= REDRAW_BORDER;
+               if (eventPtr->type == FocusOut) {
+                   tablePtr->flags &= ~HAS_FOCUS;
+               } else {
+                   tablePtr->flags |= HAS_FOCUS;
+               }
+               TableRedrawHighlight(tablePtr);
+               /* cancel the timer */
+               TableConfigCursor(tablePtr);
+           }
+           break;
+    }
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TableGetActiveBuf --
- *     Get the current selection into the buffer and mark it as unedited.
- *     Set the position to the end of the string.
+ * TableCmdDeletedProc --
+ *
+ *     This procedure is invoked when a widget command is deleted.  If
+ *     the widget isn't already in the process of being destroyed,
+ *     this command destroys it.
  *
  * Results:
  *     None.
  *
  * Side effects:
- *     tablePtr->activeBuf will change.
+ *     The widget is destroyed.
  *
  *----------------------------------------------------------------------
  */
 static void
-TableGetActiveBuf(register Table *tablePtr)
+TableCmdDeletedProc(ClientData clientData)
 {
-  char *data = "";
+    Table *tablePtr = (Table *) clientData;
+    Tk_Window tkwin;
 
-  if (tablePtr->flags & HAS_ACTIVE)
-    data = TableGetCellValue(tablePtr, tablePtr->activeRow+tablePtr->rowOffset,
-                            tablePtr->activeCol+tablePtr->colOffset);
+    /*
+     * This procedure could be invoked either because the window was
+     * destroyed and the command was then deleted (in which case tkwin
+     * is NULL) or because the command was deleted, and then this procedure
+     * destroys the widget.
+     */
 
-  if (strcmp(tablePtr->activeBuf, data) == 0) {
-    /* this forced SetActiveIndex is necessary if we change array vars and
-     * they happen to have these cells equal, we won't properly set the
-     * active index for the new array var unless we do this here */
-    TableSetActiveIndex(tablePtr);
-    return;
-  }
-  /* is the buffer long enough */
-  tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf, strlen(data)+1);
-  strcpy(tablePtr->activeBuf, data);
-  TableGetIcursor(tablePtr, "end", (int *)0);
-  tablePtr->flags &= ~TEXT_CHANGED;
-  TableSetActiveIndex(tablePtr);
+    if (tablePtr->tkwin != NULL) {
+       tkwin = tablePtr->tkwin;
+       tablePtr->tkwin = NULL;
+       Tk_DestroyWindow(tkwin);
+    }
 }
 
 /* 
  *----------------------------------------------------------------------
  *
- * TableVarProc --
- *     This is the trace procedure associated with the Tcl array.  No
- *     validation will occur here because this only triggers when the
- *     array value is directly set, and we can't maintain the old value.
+ * TableRedrawHighlight --
+ *     Redraws just the highlight for the window
  *
  * Results:
- *     Invalidates changed cell.
+ *     None.
  *
  * Side effects:
- *     Creates/Updates entry in the cache if we are caching.
+ *     None
  *
  *----------------------------------------------------------------------
  */
-static char *
-TableVarProc(clientData, interp, name, index, flags)
-    ClientData clientData;     /* Information about table. */
-    Tcl_Interp *interp;                /* Interpreter containing variable. */
-    char *name;                        /* Not used. */
-    char *index;               /* Not used. */
-    int flags;                 /* Information about what happened. */
+static void
+TableRedrawHighlight(Table *tablePtr)
 {
-  Table *tablePtr = (Table *) clientData;
-  int dummy, row, col, update = 1;
-
-  /* This is redundant, as the name should always == arrayVar */
-  name = tablePtr->arrayVar;
-
-  /* is this the whole var being destroyed or just one cell being deleted */
-  if ((flags & TCL_TRACE_UNSETS) && index == NULL) {
-    /* if this isn't the interpreter being destroyed reinstate the trace */
-    if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
-      Tcl_SetVar2(interp, name, TEST_KEY, "", TCL_GLOBAL_ONLY);
-      Tcl_UnsetVar2(interp, name, TEST_KEY, TCL_GLOBAL_ONLY);
-      Tcl_ResetResult(interp);
-
-      /* set a trace on the variable */
-      Tcl_TraceVar(interp, name,
-                  TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY,
-                  (Tcl_VarTraceProc *)TableVarProc, (ClientData) tablePtr);
-
-      /* only do the following if arrayVar is our data source */
-      if (tablePtr->dataSource & DATA_ARRAY) {
-       /* clear the selection buffer */
-       TableGetActiveBuf(tablePtr);
-       /* flush any cache */
-       TableFlushCache(tablePtr);
-       /* and invalidate the table */
-       TableInvalidateAll(tablePtr, 0);
-      }
-    }
-    return (char *) NULL;
-  }
-  /* only continue if arrayVar is our data source */
-  if (!(tablePtr->dataSource & DATA_ARRAY)) {
-    return (char *) NULL;
-  }
-  /* get the cell address and invalidate that region only.
-   * Make sure that it is a valid cell address. */
-  if (strcmp("active", index) == 0) {
-    if (tablePtr->flags & SET_ACTIVE) {
-      /* If we are already setting the active cell, the update
-       * will occur in other code */
-      update = 0;
-    } else {
-      /* modified TableGetActiveBuf */
-      char *data = "";
-
-      row = tablePtr->activeRow;
-      col = tablePtr->activeCol;
-      if (tablePtr->flags & HAS_ACTIVE)
-       data = Tcl_GetVar2(interp, name, index, TCL_GLOBAL_ONLY);
-      if (!data) data = "";
-
-      if (strcmp(tablePtr->activeBuf, data) == 0) {
-       return (char *) NULL;
-      }
-      tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf,
-                                             strlen(data)+1);
-      strcpy(tablePtr->activeBuf, data);
-      /* set cursor to the last char */
-      TableGetIcursor(tablePtr, "end", (int *)0);
-      tablePtr->flags |= TEXT_CHANGED;
-    }
-  } else if (TableParseArrayIndex(&row, &col, index) == 2) {
-    char buf[INDEX_BUFSIZE];
-    /* Make sure it won't trigger on array(2,3extrastuff) */
-    TableMakeArrayIndex(row, col, buf);
-    if (strcmp(buf, index)) {
-      return (char *) NULL;
-    }
-    if (tablePtr->caching) {
-      Tcl_HashEntry *entryPtr;
-      char *val, *data = NULL;
-
-      data = Tcl_GetVar2(interp, name, index, TCL_GLOBAL_ONLY);
-      if (!data) data = "";
-      val = (char *)ckalloc(strlen(data)+1);
-      strcpy(val, data);
-      entryPtr = Tcl_CreateHashEntry(tablePtr->cache, buf, &dummy);
-      Tcl_SetHashValue(entryPtr, val);
-    }
-    /* convert index to real coords */
-    row -= tablePtr->rowOffset;
-    col -= tablePtr->colOffset;
-    /* did the active cell just update */
-    if (row == tablePtr->activeRow && col == tablePtr->activeCol)
-      TableGetActiveBuf(tablePtr);
-    /* Flash the cell */
-    TableAddFlash(tablePtr, row, col);
-  } else {
-    return (char *) NULL;
-  }
-
-  if (update) TableRefresh(tablePtr, row, col, CELL);
-
-  return (char *) NULL;
+    if ((tablePtr->flags & REDRAW_BORDER) && tablePtr->highlightWidth > 0) {
+       GC gc = Tk_GCForColor((tablePtr->flags & HAS_FOCUS)
+               ? tablePtr->highlightColorPtr : tablePtr->highlightBgColorPtr,
+               Tk_WindowId(tablePtr->tkwin));
+       Tk_DrawFocusHighlight(tablePtr->tkwin, gc, tablePtr->highlightWidth,
+               Tk_WindowId(tablePtr->tkwin));
+    }
+    tablePtr->flags &= ~REDRAW_BORDER;
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TableGeometryRequest --
- *     This procedure is invoked to request a new geometry from Tk.
+ * TableRefresh --
+ *     Refreshes an area of the table based on the mode.
+ *     row,col in real coords (0-based)
  *
  * Results:
- *     None.
+ *     Will cause redraw for visible cells
  *
  * Side effects:
- *     Geometry information is updated and a new requested size is
- *     registered for the widget.  Internal border info is also set.
+ *     None.
  *
  *----------------------------------------------------------------------
  */
-static void
-TableGeometryRequest(tablePtr)
-     register Table *tablePtr;
+void
+TableRefresh(register Table *tablePtr, int row, int col, int mode)
 {
-  int x, y;
-
-  /* Do the geometry request
-   * If -width #cols was not specified or it is greater than the real
-   * number of cols, use maxWidth as a lower bound, with the other lower
-   * bound being the upper bound of the window's user-set width and the
-   * value of -maxwidth set by the programmer
-   * Vice versa for rows/height
-   */
-  x = MIN((tablePtr->maxReqCols==0 || tablePtr->maxReqCols > tablePtr->cols) ?
-          tablePtr->maxWidth : tablePtr->colStarts[tablePtr->maxReqCols],
-         tablePtr->maxReqWidth) + 2*tablePtr->highlightWidth;
-  y = MIN((tablePtr->maxReqRows==0 || tablePtr->maxReqRows > tablePtr->rows) ?
-          tablePtr->maxHeight : tablePtr->rowStarts[tablePtr->maxReqRows],
-         tablePtr->maxReqHeight) + 2*tablePtr->highlightWidth;
-  Tk_GeometryRequest(tablePtr->tkwin, x, y);
-}
+    int x, y, w, h;
 
-/*
+    if ((row < 0) || (col < 0)) {
+       /*
+        * Invalid coords passed in.  This can happen when the "active" cell
+        * is refreshed, but doesn't really exist (row==-1 && col==-1).
+        */
+       return;
+    }
+    if (mode & CELL) {
+       if (TableCellVCoords(tablePtr, row, col, &x, &y, &w, &h, 0)) {
+           TableInvalidate(tablePtr, x, y, w, h, mode);
+       }
+    } else if (mode & ROW) {
+       /* get the position of the leftmost cell in the row */
+       if ((mode & INV_FILL) && row < tablePtr->topRow) {
+           /* Invalidate whole table */
+           TableInvalidateAll(tablePtr, mode);
+       } else if (TableCellVCoords(tablePtr, row, tablePtr->leftCol,
+               &x, &y, &w, &h, 0)) {
+           /* Invalidate from this row, maybe to end */
+           TableInvalidate(tablePtr, 0, y, Tk_Width(tablePtr->tkwin),
+                   (mode&INV_FILL)?Tk_Height(tablePtr->tkwin):h, mode);
+       }
+    } else if (mode & COL) {
+       /* get the position of the topmost cell on the column */
+       if ((mode & INV_FILL) && col < tablePtr->leftCol) {
+           /* Invalidate whole table */
+           TableInvalidateAll(tablePtr, mode);
+       } else if (TableCellVCoords(tablePtr, tablePtr->topRow, col,
+               &x, &y, &w, &h, 0)) {
+           /* Invalidate from this column, maybe to end */
+           TableInvalidate(tablePtr, x, 0,
+                   (mode&INV_FILL)?Tk_Width(tablePtr->tkwin):w,
+                   Tk_Height(tablePtr->tkwin), mode);
+       }
+    }
+}
+
+/* 
  *----------------------------------------------------------------------
  *
- * TableAdjustActive --
- *     This procedure is called by AdjustParams and CMD_ACTIVATE to
- *     move the active cell.
+ * TableGetGc --
+ *     Gets a GC corresponding to the tag structure passed.
  *
  * Results:
- *     Old and new active cell indices will be invalidated.
+ *     Returns usable GC.
  *
  * Side effects:
- *     If the old active cell index was edited, it will be saved.
- *     The active buffer will be updated.
+ *     None
  *
  *----------------------------------------------------------------------
  */
 static void
-TableAdjustActive(tablePtr)
-     register Table *tablePtr;         /* Widget record for table */
+TableGetGc(Display *display, Drawable d, TableTag *tagPtr, GC *tagGc)
 {
-  if (tablePtr->flags & HAS_ACTIVE) {
-    /* make sure the active cell has a reasonable real index */
-    tablePtr->activeRow = MAX(0, MIN(tablePtr->activeRow, tablePtr->rows-1));
-    tablePtr->activeCol = MAX(0, MIN(tablePtr->activeCol, tablePtr->cols-1));
-  }
-
-  /*
-   * now check the new value of active cell against the original,
-   * If it changed, invalidate the area, else leave it alone
-   */
-  if (tablePtr->oldActRow != tablePtr->activeRow ||
-      tablePtr->oldActCol != tablePtr->activeCol) {
-    int x, y, width, height;
-    /* put the value back in the cell */
-    if (tablePtr->oldActRow >= 0 && tablePtr->oldActCol >= 0) {
-      /* 
-       * Set the value of the old active cell to the active buffer
-       * SetCellValue will check if the value actually changed
-       */
-      if (tablePtr->flags & TEXT_CHANGED) {
-       /* WARNING an outside trace will be triggered here and if it
-        * calls something that causes TableAdjustParams to be called
-        * again, we are in data consistency trouble */
-       /* HACK - turn TEXT_CHANGED off now to possibly avoid the
-        * above data inconsistency problem.  */
-       tablePtr->flags &= ~TEXT_CHANGED;
-       TableSetCellValue(tablePtr, tablePtr->oldActRow+tablePtr->rowOffset,
-                         tablePtr->oldActCol+tablePtr->colOffset,
-                         tablePtr->activeBuf);
-      }
-      /* invalidate the old active cell */
-      TableCellCoords(tablePtr, tablePtr->oldActRow, tablePtr->oldActCol,
-                      &x, &y, &width, &height);
-      TableInvalidate(tablePtr, x, y, width, height, 0);
+    XGCValues gcValues;
+    gcValues.foreground = Tk_3DBorderColor(tagPtr->fg)->pixel;
+    gcValues.background = Tk_3DBorderColor(tagPtr->bg)->pixel;
+    gcValues.font = Tk_FontId(tagPtr->tkfont);
+    if (*tagGc == NULL) {
+       gcValues.graphics_exposures = False;
+       *tagGc = XCreateGC(display, d,
+               GCForeground|GCBackground|GCFont|GCGraphicsExposures,
+               &gcValues);
+    } else {
+       XChangeGC(display, *tagGc, GCForeground|GCBackground|GCFont,
+               &gcValues);
     }
+}
 
-    /* get the new value of the active cell into buffer */
-    TableGetActiveBuf(tablePtr);
+#define TableFreeGc    XFreeGC
 
-    /* invalidate the new active cell */
-    TableCellCoords(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
-                   &x, &y, &width, &height);
-    TableInvalidate(tablePtr, x, y, width, height, 0);
-    /* set the old active row/col for the next time this function is called */
-    tablePtr->oldActRow = tablePtr->activeRow;
-    tablePtr->oldActCol = tablePtr->activeCol;
-  }
+/*
+ *--------------------------------------------------------------
+ *
+ * TableUndisplay --
+ *     This procedure removes the contents of a table window
+ *     that have been moved offscreen.
+ *
+ * Results:
+ *     Embedded windows can be unmapped.
+ *
+ * Side effects:
+ *     Information disappears from the screen.
+ *
+ *--------------------------------------------------------------
+ */
+static void
+TableUndisplay(register Table *tablePtr)
+{
+    register int *seen = tablePtr->seen;
+    int row, col;
+
+    /* We need to find out the true last cell, not considering spans */
+    tablePtr->flags |= AVOID_SPANS;
+    TableGetLastCell(tablePtr, &row, &col);
+    tablePtr->flags &= ~AVOID_SPANS;
+
+    if (seen[0] != -1) {
+       if (seen[0] < tablePtr->topRow) {
+           /* Remove now hidden rows */
+           EmbWinUnmap(tablePtr, seen[0], MIN(seen[2],tablePtr->topRow-1),
+                   seen[1], seen[3]);
+           /* Also account for the title area */
+           EmbWinUnmap(tablePtr, seen[0], MIN(seen[2],tablePtr->topRow-1),
+                   0, tablePtr->titleCols-1);
+       }
+       if (seen[1] < tablePtr->leftCol) {
+           /* Remove now hidden cols */
+           EmbWinUnmap(tablePtr, seen[0], seen[2],
+                   seen[1], MAX(seen[3],tablePtr->leftCol-1));
+           /* Also account for the title area */
+           EmbWinUnmap(tablePtr, 0, tablePtr->titleRows-1,
+                   seen[1], MAX(seen[3],tablePtr->leftCol-1));
+       }
+       if (seen[2] > row) {
+           /* Remove now off-screen rows */
+           EmbWinUnmap(tablePtr, MAX(seen[0],row+1), seen[2],
+                   seen[1], seen[3]);
+           /* Also account for the title area */
+           EmbWinUnmap(tablePtr, MAX(seen[0],row+1), seen[2],
+                   0, tablePtr->titleCols-1);
+       }
+       if (seen[3] > col) {
+           /* Remove now off-screen cols */
+           EmbWinUnmap(tablePtr, seen[0], seen[2],
+                   MAX(seen[1],col+1), seen[3]);
+           /* Also account for the title area */
+           EmbWinUnmap(tablePtr, 0, tablePtr->titleRows-1,
+                   MAX(seen[1],col+1), seen[3]);
+       }
+    }
+    seen[0] = tablePtr->topRow;
+    seen[1] = tablePtr->leftCol;
+    seen[2] = row;
+    seen[3] = col;
 }
 
+#ifdef MAC_TCL
+#define NO_XSETCLIP
+#endif
 /*
- *----------------------------------------------------------------------
+ *--------------------------------------------------------------
  *
- * TableAdjustParams --
- *     Calculate the row and column starts.  Adjusts the topleft corner
- *     variable to keep it within the screen range, out of the titles
- *     and keep the screen full make sure the selected cell is in the
- *     visible area checks to see if the top left cell has changed at
- *     all and invalidates the table if it has.
+ * TableDisplay --
+ *     This procedure redraws the contents of a table window.
+ *     The conditional code in this function is due to these factors:
+ *             o Lack of XSetClipRectangles on Macintosh
+ *             o Use of alternative routine for Windows
  *
  * Results:
  *     None.
  *
- * Side Effects:
- *     Number of rows can change if -rowstretchmode == fill.
- *     topRow && leftCol can change to fit display.
- *     activeRow/Col can change to ensure it is a valid cell.
+ * Side effects:
+ *     Information appears on the screen.
  *
- *----------------------------------------------------------------------
+ *--------------------------------------------------------------
  */
 static void
-TableAdjustParams(register Table *tablePtr)
+TableDisplay(ClientData clientdata)
 {
-  int topRow, leftCol, row, col, total, i, value, x, y, width, height;
-  int w, h, bd, hl, recalc = 0;
-  int diff, unpreset, lastUnpreset, pad, lastPad, numPixels;
-  int defColWidth, defRowHeight;
-  Tcl_HashEntry *entryPtr;
-
-  /* cache the borderwidth (doubled) for many upcoming calculations */
-  bd = 2*tablePtr->borderWidth;
-  hl = tablePtr->highlightWidth;
-  w = Tk_Width(tablePtr->tkwin)-2*hl;
-  h = Tk_Height(tablePtr->tkwin)-2*hl;
-
-  /* account for whether defColWidth is in chars (>=0) or pixels (<0) */
-  /* bd is added in here for convenience */
-  if (tablePtr->defColWidth > 0)
-    defColWidth = tablePtr->charWidth * tablePtr->defColWidth + bd;
-  else
-    defColWidth = -(tablePtr->defColWidth) + bd;
-  if (tablePtr->defRowHeight > 0)
-    defRowHeight = tablePtr->charHeight * tablePtr->defRowHeight + bd;
-  else
-    defRowHeight = -(tablePtr->defRowHeight) + bd;
-
-  /* Set up the arrays to hold the col pixels and starts */
-  if (tablePtr->colPixels) ckfree((char *) tablePtr->colPixels);
-  tablePtr->colPixels = (int *) ckalloc(tablePtr->cols * sizeof(int));
-  if (tablePtr->colStarts) ckfree((char *) tablePtr->colStarts);
-  tablePtr->colStarts = (int *) ckalloc((tablePtr->cols+1) * sizeof(int));
-
-  /* get all the preset columns and set their widths */
-  lastUnpreset = 0;
-  numPixels = 0;
-  unpreset = 0;
-  for (i = 0; i < tablePtr->cols; i++) {
-    if ((entryPtr = Tcl_FindHashEntry(tablePtr->colWidths,
-                                     (char *) i)) == NULL) {
-      tablePtr->colPixels[i] = -1;
-      unpreset++;
-      lastUnpreset = i;
-    } else {
-      value = (int) Tcl_GetHashValue(entryPtr);
-      if (value <= 0) {
-       tablePtr->colPixels[i] = -value + bd;
-      } else {
-       tablePtr->colPixels[i] = value * tablePtr->charWidth + bd;
-      }
-      numPixels += tablePtr->colPixels[i];
-    }
-  }
-
-  /* work out how much to pad each col depending on the mode */
-  diff = w-numPixels-(unpreset*defColWidth);
-  total = 0;
-  /* now do the padding and calculate the column starts */
-  /* diff lower than 0 means we can't see the entire set of columns,
-   * thus no special stretching will occur & we optimize the calculation */
-  if (diff <= 0) {
-    for (i = 0; i < tablePtr->cols; i++) {
-      if (tablePtr->colPixels[i] == -1)
-       tablePtr->colPixels[i] = defColWidth;
-      tablePtr->colStarts[i] = total;
-      total += tablePtr->colPixels[i];
-    }
-  } else {
-    switch(tablePtr->colStretch) {
-    case STRETCH_MODE_NONE:
-      pad = 0;
-      lastPad = 0;
-      break;
-    case STRETCH_MODE_UNSET:
-      if (unpreset == 0) {
-       pad = 0;
-       lastPad = 0;
-      } else {
-       pad = diff / unpreset;
-       lastPad = diff - pad * (unpreset - 1);
-      }
-      break;
-    case STRETCH_MODE_LAST:
-      pad = 0;
-      lastPad = diff;
-      lastUnpreset = tablePtr->cols - 1;
-      break;
-    default:   /* STRETCH_MODE_ALL, but also FILL for cols */
-      pad = diff / tablePtr->cols;
-      /* force it to be applied to the last column too */
-      lastUnpreset = tablePtr->cols - 1;
-      lastPad = diff - pad * lastUnpreset;
-    }
+    register Table *tablePtr = (Table *) clientdata;
+    Tk_Window tkwin = tablePtr->tkwin;
+    Display *display = tablePtr->display;
+    Drawable window;
+#ifdef NO_XSETCLIP
+    Drawable clipWind;
+#elif !defined(WIN32)
+    XRectangle clipRect;
+#endif
+    int rowFrom, rowTo, colFrom, colTo,
+       invalidX, invalidY, invalidWidth, invalidHeight,
+       x, y, width, height, itemX, itemY, itemW, itemH,
+       row, col, urow, ucol, hrow=0, hcol=0, cx, cy, cw, ch, borders, bd[6],
+       numBytes, new, boundW, boundH, maxW, maxH, cellType,
+       originX, originY, activeCell, shouldInvert, ipadx, ipady, padx, pady;
+    GC tagGc = NULL, topGc, bottomGc;
+    char *string = NULL;
+    char buf[INDEX_BUFSIZE];
+    TableTag *tagPtr = NULL, *titlePtr, *selPtr, *activePtr, *flashPtr,
+       *rowPtr, *colPtr;
+    Tcl_HashEntry *entryPtr;
+    static XPoint rect[3] = { {0, 0}, {0, 0}, {0, 0} };
+    Tcl_HashTable *colTagsCache = NULL;
+    Tcl_HashTable *drawnCache = NULL;
+    Tk_TextLayout textLayout = NULL;
+    TableEmbWindow *ewPtr;
 
-    for (i = 0; i < tablePtr->cols; i++) {
-      if (tablePtr->colPixels[i] == -1) {
-       tablePtr->colPixels[i] = defColWidth
-         + ((i==lastUnpreset)?lastPad:pad);
-      } else if (tablePtr->colStretch == STRETCH_MODE_ALL) {
-       tablePtr->colPixels[i] += (i==lastUnpreset)?lastPad:pad;
-      }
-      tablePtr->colStarts[i] = total;
-      total += tablePtr->colPixels[i];
+    tablePtr->flags &= ~REDRAW_PENDING;
+    if ((tkwin == NULL) || !Tk_IsMapped(tkwin)) {
+       return;
     }
-  }
-  tablePtr->colStarts[i] = tablePtr->maxWidth = total;
-
-  /*
-   * The 'do' loop is only necessary for rows because of FILL mode
-   */
-  do {
-    /* Set up the arrays to hold the row pixels and starts */
-    /* FIX - this can be moved outside 'do' if you check >row size */
-    if (tablePtr->rowPixels) ckfree((char *) tablePtr->rowPixels);
-    tablePtr->rowPixels = (int *) ckalloc(tablePtr->rows * sizeof(int));
 
-    /* get all the preset rows and set their heights */
-    lastUnpreset = 0;
-    numPixels = 0;
-    unpreset = 0;
-    for (i = 0; i < tablePtr->rows; i++) {
-      if ((entryPtr = Tcl_FindHashEntry(tablePtr->rowHeights,
-                                       (char *) i)) == NULL) {
-       tablePtr->rowPixels[i] = -1;
-       unpreset++;
-       lastUnpreset = i;
-      } else {
-       value = (int) Tcl_GetHashValue(entryPtr);
-       if (value <= 0) {
-         tablePtr->rowPixels[i] = -value + bd;
-       } else {
-         tablePtr->rowPixels[i] = value * tablePtr->charHeight + bd;
-       }
-       numPixels += tablePtr->rowPixels[i];
-      }
-    }
+    boundW = Tk_Width(tkwin) - tablePtr->highlightWidth;
+    boundH = Tk_Height(tkwin) - tablePtr->highlightWidth;
 
-    /* work out how much to pad each row depending on the mode */
-    diff = h-numPixels-(unpreset*defRowHeight);
-    switch(tablePtr->rowStretch) {
-    case STRETCH_MODE_NONE:
-      pad = 0;
-      lastPad = 0;
-      break;
-    case STRETCH_MODE_UNSET:
-      if (unpreset == 0)  {
-       pad = 0;
-       lastPad = 0;
-      } else {
-       pad = MAX(0,diff) / unpreset;
-       lastPad = MAX(0,diff) - pad * (unpreset - 1);
-      }
-      break;
-    case STRETCH_MODE_LAST:
-      pad = 0;
-      lastPad = MAX(0,diff);
-      /* force it to be applied to the last column too */
-      lastUnpreset = tablePtr->rows - 1;
-      break;
-    case STRETCH_MODE_FILL:
-      pad = 0;
-      lastPad = diff;
-      if (diff && !recalc) {
-       tablePtr->rows += (diff/defRowHeight);
-       if (diff < 0 && tablePtr->rows < 0)
-         tablePtr->rows = 0;
-       lastUnpreset = tablePtr->rows - 1;
-       recalc = 1;
-       continue;
-      } else {
-       lastUnpreset = tablePtr->rows - 1;
-       recalc = 0;
-      }
-      break;
-    default:   /* STRETCH_MODE_ALL */
-      pad = MAX(0,diff) / tablePtr->rows;
-      /* force it to be applied to the last column too */
-      lastUnpreset = tablePtr->rows - 1;
-      lastPad = MAX(0,diff) - pad * lastUnpreset;
-    }
-  } while (recalc);
-
-  if (tablePtr->rowStarts) ckfree((char *) tablePtr->rowStarts);
-  tablePtr->rowStarts = (int *) ckalloc((tablePtr->rows+1)*sizeof(int));
-  /* now do the padding and calculate the row starts */
-  total = 0;
-  for (i = 0; i < tablePtr->rows; i++) {
-    if (tablePtr->rowPixels[i] == -1) {
-      tablePtr->rowPixels[i] = defRowHeight
-       + ((i==lastUnpreset)?lastPad:pad);
-    } else if (tablePtr->rowStretch == STRETCH_MODE_ALL) {
-      tablePtr->rowPixels[i] += (i==lastUnpreset)?lastPad:pad;
+    /* Constrain drawable to not include highlight borders */
+    invalidX = MAX(tablePtr->highlightWidth, tablePtr->invalidX);
+    invalidY = MAX(tablePtr->highlightWidth, tablePtr->invalidY);
+    invalidWidth  = MIN(tablePtr->invalidWidth, MAX(1, boundW-invalidX));
+    invalidHeight = MIN(tablePtr->invalidHeight, MAX(1, boundH-invalidY));
+
+    ipadx = tablePtr->ipadX;
+    ipady = tablePtr->ipadY;
+    padx  = tablePtr->padX;
+    pady  = tablePtr->padY;
+
+    /* 
+     * if we are using the slow drawing mode with a pixmap 
+     * create the pixmap and adjust x && y for offset in pixmap
+     */
+    if (tablePtr->drawMode == DRAW_MODE_SLOW) {
+       window = Tk_GetPixmap(display, Tk_WindowId(tkwin),
+               invalidWidth, invalidHeight, Tk_Depth(tkwin));
+    } else {
+       window = Tk_WindowId(tkwin);
     }
-    /* calculate the start of each row */
-    tablePtr->rowStarts[i] = total;
-    total += tablePtr->rowPixels[i];
-  }
-  tablePtr->rowStarts[i] = tablePtr->maxHeight = total;
-
-  /* make sure the top row and col have reasonable real indices */
-  tablePtr->topRow = topRow =
-    MAX(tablePtr->titleRows, MIN(tablePtr->topRow, tablePtr->rows-1));
-  tablePtr->leftCol = leftCol =
-    MAX(tablePtr->titleCols, MIN(tablePtr->leftCol, tablePtr->cols-1));
-
-  /* If we dont have the info, dont bother to fix up the other parameters */
-  if (Tk_WindowId(tablePtr->tkwin) == None) {
-    tablePtr->oldTopRow = tablePtr->oldLeftCol = -1;
-    return;
-  }
-
-  w += hl;
-  h += hl;
-  /* 
-   * If we use this value of topRow, will we fill the window?
-   * if not, decrease it until we will, or until it gets to titleRows 
-   * make sure we don't cut off the bottom row
-   */
-  for (; topRow > tablePtr->titleRows; topRow--)
-    if ((tablePtr->maxHeight-(tablePtr->rowStarts[topRow-1] -
-                             tablePtr->rowStarts[tablePtr->titleRows])) > h)
-      break;
-  /* 
-   * If we use this value of topCol, will we fill the window?
-   * if not, decrease it until we will, or until it gets to titleCols 
-   * make sure we don't cut off the left column
-   */
-  for (; leftCol > tablePtr->titleCols; leftCol--)
-    if ((tablePtr->maxWidth-(tablePtr->colStarts[leftCol-1] -
-                            tablePtr->colStarts[tablePtr->titleCols])) > w)
-      break;
-
-  tablePtr->topRow = topRow;
-  tablePtr->leftCol = leftCol;
-
-  /* Now work out where the bottom right for scrollbar update
-   * and testing for one last stretch */
-  TableGetLastCell(tablePtr, &row, &col);
-  TableCellVCoords(tablePtr, row, col, &x, &y, &width, &height, 0);
-
-  /*
-   * Do we have scrollbars, if so, calculate and call the TCL functions In
-   * order to get the scrollbar to be completely full when the whole screen
-   * is shown and there are titles, we have to arrange for the scrollbar
-   * range to be 0 -> rows-titleRows etc.  This leads to the position
-   * setting methods, toprow and leftcol, being relative to the titles, not
-   * absolute row and column numbers.
-   */
-  if (tablePtr->yScrollCmd != NULL || tablePtr->xScrollCmd != NULL) {
-    Tcl_Interp *interp = tablePtr->interp;
-    char buf[INDEX_BUFSIZE];
-    double first, last;
+#ifdef NO_XSETCLIP
+    clipWind = Tk_GetPixmap(display, window,
+           invalidWidth, invalidHeight, Tk_Depth(tkwin));
+#endif
+
+    /* set up the permanent tag styles */
+    entryPtr   = Tcl_FindHashEntry(tablePtr->tagTable, "title");
+    titlePtr   = (TableTag *) Tcl_GetHashValue(entryPtr);
+    entryPtr   = Tcl_FindHashEntry(tablePtr->tagTable, "sel");
+    selPtr     = (TableTag *) Tcl_GetHashValue(entryPtr);
+    entryPtr   = Tcl_FindHashEntry(tablePtr->tagTable, "active");
+    activePtr  = (TableTag *) Tcl_GetHashValue(entryPtr);
+    entryPtr   = Tcl_FindHashEntry(tablePtr->tagTable, "flash");
+    flashPtr   = (TableTag *) Tcl_GetHashValue(entryPtr);
+
+    /* We need to find out the true cell span, not considering spans */
+    tablePtr->flags |= AVOID_SPANS;
+    /* find out the cells represented by the invalid region */
+    TableWhatCell(tablePtr, invalidX, invalidY, &rowFrom, &colFrom);
+    TableWhatCell(tablePtr, invalidX+invalidWidth-1,
+           invalidY+invalidHeight-1, &rowTo, &colTo);
+    tablePtr->flags &= ~AVOID_SPANS;
+
+#ifdef DEBUG
+    tcl_dprintf(tablePtr->interp, "%d,%d => %d,%d",
+           rowFrom+tablePtr->rowOffset, colFrom+tablePtr->colOffset,
+           rowTo+tablePtr->rowOffset, colTo+tablePtr->colOffset);
+#endif
+
+    /* 
+     * Initialize colTagsCache hash table to cache column tag names.
+     */
+    colTagsCache = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
+    Tcl_InitHashTable(colTagsCache, TCL_ONE_WORD_KEYS);
+    /* 
+     * Initialize drawnCache hash table to cache drawn cells.
+     * This is necessary to prevent spanning cells being drawn multiple times.
+     */
+    drawnCache = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
+    Tcl_InitHashTable(drawnCache, TCL_STRING_KEYS);
 
     /*
-     * We must hold onto the interpreter because the data referred to at
-     * tablePtr might be freed as a result of the call to Tcl_VarEval.
+     * Create the tag here.  This will actually create a JoinTag
+     * That will handle the priority management of merging for us.
+     * We only need one allocated, and we'll reset it for each cell.
      */
-    Tcl_Preserve((ClientData) interp);
-
-    /* Do we have a Y-scrollbar and rows to scroll? */
-    if (tablePtr->yScrollCmd != NULL) {
-      if (row < tablePtr->titleRows) {
-       first = 0;
-       last  = 1;
-      } else {
-       diff = tablePtr->rowStarts[tablePtr->titleRows];
-       last = (double) (tablePtr->rowStarts[tablePtr->rows]-diff);
-       first = (tablePtr->rowStarts[topRow]-diff) / last;
-       last  = (height+tablePtr->rowStarts[row]-diff) / last;
-      }
-      sprintf(buf, " %g %g", first, last);
-      if (Tcl_VarEval(interp, tablePtr->yScrollCmd,
-                     buf, (char *) NULL) != TCL_OK) {
-       Tcl_AddErrorInfo(interp,
-               "\n    (vertical scrolling command executed by table)");
-       Tcl_BackgroundError(interp);
-      }
+    tagPtr = TableNewTag(tablePtr);
+
+    /* Cycle through the cells and display them */
+    for (row = rowFrom; row <= rowTo; row++) {
+       /* 
+        * are we in the 'dead zone' between the
+        * title rows and the first displayed row 
+        */
+       if (row < tablePtr->topRow && row >= tablePtr->titleRows) {
+           row = tablePtr->topRow;
+       }
+
+       /* Cache the row in user terms */
+       urow = row+tablePtr->rowOffset;
+
+       /* Get the row tag once for all iterations of col */
+       rowPtr = FindRowColTag(tablePtr, urow, ROW);
+
+       for (col = colFrom; col <= colTo; col++) {
+           activeCell = 0;
+           /* 
+            * Adjust to first viewable column if we are in the 'dead zone'
+            * between the title cols and the first displayed column.
+            */
+           if (col < tablePtr->leftCol && col >= tablePtr->titleCols) {
+               col = tablePtr->leftCol;
+           }
+
+           /*
+            * Get the coordinates for the cell before possible rearrangement
+            * of row,col due to spanning cells
+            */
+           cellType = TableCellCoords(tablePtr, row, col,
+                   &x, &y, &width, &height);
+           if (cellType == CELL_HIDDEN) {
+               /*
+                * width,height holds the real start row,col of the span.
+                * Put the use cell ref into a buffer for the hash lookups.
+                */
+               TableMakeArrayIndex(width, height, buf);
+               Tcl_CreateHashEntry(drawnCache, buf, &new);
+               if (!new) {
+                   /* Not new in the entry, so it's already drawn */
+                   continue;
+               }
+               hrow = row; hcol = col;
+               row = width-tablePtr->rowOffset;
+               col = height-tablePtr->colOffset;
+               TableCellVCoords(tablePtr, row, col,
+                       &x, &y, &width, &height, 0);
+               /* We have to adjust the coords back onto the visual display */
+               urow = row+tablePtr->rowOffset;
+               rowPtr = FindRowColTag(tablePtr, urow, ROW);
+           }
+
+           /* Constrain drawn size to the visual boundaries */
+           if (width > boundW-x)       { width  = boundW-x; }
+           if (height > boundH-y)      { height = boundH-y; }
+
+           /* Cache the col in user terms */
+           ucol = col+tablePtr->colOffset;
+
+           /* put the use cell ref into a buffer for the hash lookups */
+           TableMakeArrayIndex(urow, ucol, buf);
+           if (cellType != CELL_HIDDEN) {
+               Tcl_CreateHashEntry(drawnCache, buf, &new);
+           }
+
+           /*
+            * Make sure we start with a clean tag (set to table defaults).
+            */
+           TableResetTag(tablePtr, tagPtr);
+
+           /*
+            * Check to see if we have an embedded window in this cell.
+            */
+           entryPtr = Tcl_FindHashEntry(tablePtr->winTable, buf);
+           if (entryPtr != NULL) {
+               ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
+
+               if (ewPtr->tkwin != NULL) {
+                   /* Display embedded window instead of text */
+
+                   /* if active, make it disabled to avoid
+                    * unnecessary editing */
+                   if ((tablePtr->flags & HAS_ACTIVE)
+                           && row == tablePtr->activeRow
+                           && col == tablePtr->activeCol) {
+                       tablePtr->flags |= ACTIVE_DISABLED;
+                   }
+
+                   /*
+                    * The EmbWinDisplay function may modify values in
+                    * tagPtr, so reference those after this call.
+                    */
+                   EmbWinDisplay(tablePtr, window, ewPtr, tagPtr,
+                           x, y, width, height);
+
+                   if (tablePtr->drawMode == DRAW_MODE_SLOW) {
+                       /* Correctly adjust x && y with the offset */
+                       x -= invalidX;
+                       y -= invalidY;
+                   }
+
+                   Tk_Fill3DRectangle(tkwin, window, tagPtr->bg, x, y, width,
+                           height, 0, TK_RELIEF_FLAT);
+
+                   /* border width for cell should now be properly set */
+                   borders = TableGetTagBorders(tagPtr, &bd[0], &bd[1],
+                           &bd[2], &bd[3]);
+                   bd[4] = (bd[0] + bd[1])/2;
+                   bd[5] = (bd[2] + bd[3])/2;
+
+                   goto DrawBorder;
+               }
+           }
+
+           if (tablePtr->drawMode == DRAW_MODE_SLOW) {
+               /* Correctly adjust x && y with the offset */
+               x -= invalidX;
+               y -= invalidY;
+           }
+
+           shouldInvert = 0;
+           /*
+            * Get the combined tag structure for the cell.
+            * First clear out a new tag structure that we will build in
+            * then add tags as we realize they belong.
+            *
+            * Tags have their own priorities which TableMergeTag will
+            * take into account when merging tags.
+            */
+
+           /*
+            * Merge colPtr if it exists
+            * let's see if we have the value cached already
+            * if not, run the findColTag routine and cache the value
+            */
+           entryPtr = Tcl_CreateHashEntry(colTagsCache, (char *)ucol, &new);
+           if (new) {
+               colPtr = FindRowColTag(tablePtr, ucol, COL);
+               Tcl_SetHashValue(entryPtr, colPtr);
+           } else {
+               colPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
+           }
+           if (colPtr != (TableTag *) NULL) {
+               TableMergeTag(tablePtr, tagPtr, colPtr);
+           }
+           /* Merge rowPtr if it exists */
+           if (rowPtr != (TableTag *) NULL) {
+               TableMergeTag(tablePtr, tagPtr, rowPtr);
+           }
+           /* Am I in the titles */
+           if (row < tablePtr->titleRows || col < tablePtr->titleCols) {
+               TableMergeTag(tablePtr, tagPtr, titlePtr);
+           }
+           /* Does this have a cell tag */
+           entryPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf);
+           if (entryPtr != NULL) {
+               TableMergeTag(tablePtr, tagPtr,
+                       (TableTag *) Tcl_GetHashValue(entryPtr));
+           }
+           /* is this cell active? */
+           if ((tablePtr->flags & HAS_ACTIVE) &&
+                   (tablePtr->state == STATE_NORMAL) &&
+                   row == tablePtr->activeRow && col == tablePtr->activeCol) {
+               if (tagPtr->state == STATE_DISABLED) {
+                   tablePtr->flags |= ACTIVE_DISABLED;
+               } else {
+                   TableMergeTag(tablePtr, tagPtr, activePtr);
+                   activeCell = 1;
+                   tablePtr->flags &= ~ACTIVE_DISABLED;
+               }
+           }
+           /* is this cell selected? */
+           if (Tcl_FindHashEntry(tablePtr->selCells, buf) != NULL) {
+               if (tablePtr->invertSelected && !activeCell) {
+                   shouldInvert = 1;
+               } else {
+                   TableMergeTag(tablePtr, tagPtr, selPtr);
+               }
+           }
+           /* if flash mode is on, is this cell flashing? */
+           if (tablePtr->flashMode &&
+                   Tcl_FindHashEntry(tablePtr->flashCells, buf) != NULL) {
+               TableMergeTag(tablePtr, tagPtr, flashPtr);
+           }
+
+           if (shouldInvert) {
+               TableInvertTag(tagPtr);
+           }
+
+           /*
+            * Borders for cell should now be properly set
+            */
+           borders = TableGetTagBorders(tagPtr, &bd[0], &bd[1],
+                   &bd[2], &bd[3]);
+           bd[4] = (bd[0] + bd[1])/2;
+           bd[5] = (bd[2] + bd[3])/2;
+
+           /*
+            * First fill in a blank rectangle.
+            */
+           Tk_Fill3DRectangle(tkwin, window, tagPtr->bg,
+                   x, y, width, height, 0, TK_RELIEF_FLAT);
+
+           /*
+            * Correct the dimensions to enforce padding constraints
+            */
+           width  -= bd[0] + bd[1] + (2 * padx);
+           height -= bd[2] + bd[3] + (2 * pady);
+
+           /*
+            * If an image is in the tag, draw it
+            */
+           if (tagPtr->image != NULL) {
+               Tk_SizeOfImage(tagPtr->image, &itemW, &itemH);
+               /* Handle anchoring of image in cell space */
+               switch (tagPtr->anchor) {
+                   case TK_ANCHOR_NW:
+                   case TK_ANCHOR_W:
+                   case TK_ANCHOR_SW:          /* western position */
+                       originX = itemX = 0;
+                       break;
+                   case TK_ANCHOR_N:
+                   case TK_ANCHOR_S:
+                   case TK_ANCHOR_CENTER:      /* centered position */
+                       itemX   = MAX(0, (itemW - width) / 2);
+                       originX = MAX(0, (width - itemW) / 2);
+                       break;
+                   default:                    /* eastern position */
+                       itemX   = MAX(0, itemW - width);
+                       originX = MAX(0, width - itemW);
+               }
+               switch (tagPtr->anchor) {
+                   case TK_ANCHOR_N:
+                   case TK_ANCHOR_NE:
+                   case TK_ANCHOR_NW:          /* northern position */
+                       originY = itemY = 0;
+                       break;
+                   case TK_ANCHOR_W:
+                   case TK_ANCHOR_E:
+                   case TK_ANCHOR_CENTER:      /* centered position */
+                       itemY   = MAX(0, (itemH - height) / 2);
+                       originY = MAX(0, (height - itemH) / 2);
+                       break;
+                   default:                    /* southern position */
+                       itemY   = MAX(0, itemH - height);
+                       originY = MAX(0, height - itemH);
+               }
+               Tk_RedrawImage(tagPtr->image, itemX, itemY,
+                       MIN(itemW, width-originX), MIN(itemH, height-originY),
+                       window, x + originX + bd[0] + padx,
+                       y + originY + bd[2] + pady);
+               /*
+                * If we don't want to display the text as well, then jump.
+                */
+               if (tagPtr->showtext == 0) {
+                   /*
+                    * Re-Correct the dimensions before border drawing
+                    */
+                   width  += bd[0] + bd[1] + (2 * padx);
+                   height += bd[2] + bd[3] + (2 * pady);
+                   goto DrawBorder;
+               }
+           }
+
+           /*
+            * Get the GC for this particular blend of tags.
+            * This creates the GC if it never existed, otherwise it
+            * modifies the one we have, so we only need the one
+            */
+           TableGetGc(display, window, tagPtr, &tagGc);
+
+           /* if this is the active cell, use the buffer */
+           if (activeCell) {
+               string = tablePtr->activeBuf;
+           } else {
+               /* Is there a value in the cell? If so, draw it  */
+               string = TableGetCellValue(tablePtr, urow, ucol);
+           }
+
+#ifdef TCL_UTF_MAX
+           /*
+            * We have to use strlen here because otherwise it stops
+            * at the first \x00 unicode char it finds (!= '\0'),
+            * although there can be more to the string than that
+            */
+           numBytes = Tcl_NumUtfChars(string, strlen(string));
+#else
+           numBytes = strlen(string);
+#endif
+
+           /* If there is a string, show it */
+           if (activeCell || numBytes) {
+               /* get the dimensions of the string */
+               textLayout = Tk_ComputeTextLayout(tagPtr->tkfont,
+                       string, numBytes,
+                       (tagPtr->wrap > 0) ? width : 0, tagPtr->justify,
+                       (tagPtr->multiline > 0) ? 0 : TK_IGNORE_NEWLINES,
+                       &itemW, &itemH);
+
+               /*
+                * Set the origin coordinates of the string to draw using
+                * the anchor.  origin represents the (x,y) coordinate of
+                * the lower left corner of the text box, relative to the
+                * internal (inside the border) window
+                */
+
+               /* set the X origin first */
+               switch (tagPtr->anchor) {
+                   case TK_ANCHOR_NW:
+                   case TK_ANCHOR_W:
+                   case TK_ANCHOR_SW:          /* western position */
+                       originX = ipadx;
+                       break;
+                   case TK_ANCHOR_N:
+                   case TK_ANCHOR_S:
+                   case TK_ANCHOR_CENTER:      /* centered position */
+                       originX = (width - itemW) / 2;
+                       break;
+                   default:                    /* eastern position */
+                       originX = width - itemW - ipadx;
+               }
+
+               /* then set the Y origin */
+               switch (tagPtr->anchor) {
+                   case TK_ANCHOR_N:
+                   case TK_ANCHOR_NE:
+                   case TK_ANCHOR_NW:          /* northern position */
+                       originY = ipady;
+                       break;
+                   case TK_ANCHOR_W:
+                   case TK_ANCHOR_E:
+                   case TK_ANCHOR_CENTER:      /* centered position */
+                       originY = (height - itemH) / 2;
+                       break;
+                   default:                    /* southern position */
+                       originY = height - itemH - ipady;
+               }
+
+               /*
+                * If this is the active cell and we are editing,
+                * ensure that the cursor will be displayed
+                */
+               if (activeCell) {
+                   Tk_CharBbox(textLayout, tablePtr->icursor,
+                           &cx, &cy, &cw, &ch);
+                   /* we have to fudge with maxW because of odd width
+                    * determination for newlines at the end of a line */
+                   maxW = width - tablePtr->insertWidth
+                       - (cx + MIN(tablePtr->charWidth, cw));
+                   maxH = height - (cy + ch);
+                   if (originX < bd[0] - cx) {
+                       /* cursor off cell to the left */
+                       /* use western positioning to cet cursor at left
+                        * with slight variation to show some text */
+                       originX = bd[0] - cx
+                           + MIN(cx, width - tablePtr->insertWidth);
+                   } else if (originX > maxW) {
+                       /* cursor off cell to the right */
+                       /* use eastern positioning to cet cursor at right */
+                       originX = maxW;
+                   }
+                   if (originY < bd[2] - cy) {
+                       /* cursor before top of cell */
+                       /* use northern positioning to cet cursor at top */
+                       originY = bd[2] - cy;
+                   } else if (originY > maxH) {
+                       /* cursor beyond bottom of cell */
+                       /* use southern positioning to cet cursor at bottom */
+                       originY = maxH;
+                   }
+                   tablePtr->activeTagPtr      = tagPtr;
+                   tablePtr->activeX           = originX;
+                   tablePtr->activeY           = originY;
+               }
+
+               /*
+                * Use a clip rectangle only if necessary as it means
+                * updating the GC in the server which slows everything down.
+                * We can't fudge the width or height, just in case the user
+                * wanted empty pad space.
+                */
+               if ((originX < 0) || (originY < 0) ||
+                       (originX+itemW > width) || (originY+itemH > height)) {
+                   /*
+                    * The text wants to overflow the boundaries of the
+                    * displayed cell, so we must clip in some way
+                    */
+#ifdef NO_XSETCLIP
+                   /*
+                    * This code is basically for the Macintosh.
+                    * Copy the the current contents of the cell into the
+                    * clipped window area.  This keeps any fg/bg and image
+                    * data intact.
+                    */
+                   XCopyArea(display, window, clipWind, tagGc, x, y,
+                           width + bd[0] + bd[1] + (2 * padx),
+                           height + bd[2] + bd[3] + (2 * pady), 0, 0);
+                   /*
+                    * Now draw into the cell space on the special window.
+                    * Don't use x,y base offset for clipWind.
+                    */
+                   Tk_DrawTextLayout(display, clipWind, tagGc, textLayout,
+                           0 + originX + bd[0] + padx,
+                           0 + originY + bd[2] + pady, 0, -1);
+                   /*
+                    * Now copy back only the area that we want the
+                    * text to be drawn on.
+                    */
+                   XCopyArea(display, clipWind, window, tagGc,
+                           bd[0] + padx, bd[2] + pady,
+                           width, height, x + bd[0] + padx, y + bd[2] + pady);
+#elif defined(WIN32)
+                   /*
+                    * This is evil, evil evil! but the XCopyArea
+                    * doesn't work in all cases - Michael Teske.
+                    * The general structure follows the comments below.
+                    */
+                   TkWinDrawable *twdPtr = (TkWinDrawable *) window;
+                   HDC dc = GetDC(twdPtr->window.handle);
+                   HRGN clipR;
+
+                   clipR = CreateRectRgn(x + bd[0] + padx, y + bd[2] + pady,
+                           x + bd[0] + padx + width,
+                           y + bd[2] + pady + height);
+
+                   SelectClipRgn(dc, clipR);
+                   OffsetClipRgn(dc, 0, 0);
+
+                   Tk_DrawTextLayout(display, window, tagGc, textLayout,
+                           x + originX + bd[0] + padx,
+                           y + originY + bd[2] + pady, 0, -1);
+
+                   SelectClipRgn(dc, NULL);
+                   DeleteObject(clipR);
+#else
+                   /*
+                    * Use an X clipping rectangle.  The clipping is the
+                    * rectangle just for the actual text space (to allow
+                    * for empty padding space).
+                    */
+                   clipRect.x = x + bd[0] + padx;
+                   clipRect.y = y + bd[2] + pady;
+                   clipRect.width = width;
+                   clipRect.height = height;
+                   XSetClipRectangles(display, tagGc, 0, 0, &clipRect, 1,
+                           Unsorted);
+                   Tk_DrawTextLayout(display, window, tagGc, textLayout,
+                           x + originX + bd[0] + padx,
+                           y + originY + bd[2] + pady, 0, -1);
+                   XSetClipMask(display, tagGc, None);
+#endif
+               } else {
+                   Tk_DrawTextLayout(display, window, tagGc, textLayout,
+                           x + originX + bd[0] + padx,
+                           y + originY + bd[2] + pady, 0, -1);
+               }
+
+               /* if this is the active cell draw the cursor if it's on.
+                * this ignores clip rectangles. */
+               if (activeCell && (tablePtr->flags & CURSOR_ON) &&
+                       (originY + cy + bd[2] + pady < height) &&
+                       (originX + cx + bd[0] + padx -
+                               (tablePtr->insertWidth / 2) >= 0)) {
+                   /* make sure it will fit in the box */
+                   maxW = MAX(0, originY + cy + bd[2] + pady);
+                   maxH = MIN(ch, height - maxW + bd[2] + pady);
+                   Tk_Fill3DRectangle(tkwin, window, tablePtr->insertBg,
+                           x + originX + cx + bd[0] + padx
+                           - (tablePtr->insertWidth/2),
+                           y + maxW, tablePtr->insertWidth,
+                           maxH, 0, TK_RELIEF_FLAT);
+               }
+           }
+
+           /*
+            * Re-Correct the dimensions before border drawing
+            */
+           width  += bd[0] + bd[1] + (2 * padx);
+           height += bd[2] + bd[3] + (2 * pady);
+
+           DrawBorder:
+           /* Draw the 3d border on the pixmap correctly offset */
+           if (tablePtr->drawMode == DRAW_MODE_SINGLE) {
+               topGc = Tk_3DBorderGC(tkwin, tagPtr->bg, TK_3D_DARK_GC);
+               /* draw a line with single pixel width */
+               rect[0].x = x;
+               rect[0].y = y + height - 1;
+               rect[1].y = -height + 1;
+               rect[2].x = width - 1;
+               XDrawLines(display, window, topGc, rect, 3, CoordModePrevious);
+           } else if (tablePtr->drawMode == DRAW_MODE_FAST) {
+               /*
+                * This depicts a full 1 pixel border.
+                *
+                * Choose the GCs to get the best approximation
+                * to the desired drawing style.
+                */
+               switch(tagPtr->relief) {
+                   case TK_RELIEF_FLAT:
+                       topGc = bottomGc = Tk_3DBorderGC(tkwin, tagPtr->bg,
+                               TK_3D_FLAT_GC);
+                       break;
+                   case TK_RELIEF_RAISED:
+                   case TK_RELIEF_RIDGE:
+                       topGc    = Tk_3DBorderGC(tkwin, tagPtr->bg,
+                               TK_3D_LIGHT_GC);
+                       bottomGc = Tk_3DBorderGC(tkwin, tagPtr->bg,
+                               TK_3D_DARK_GC);
+                       break;
+                   default: /* TK_RELIEF_SUNKEN TK_RELIEF_GROOVE */
+                       bottomGc = Tk_3DBorderGC(tkwin, tagPtr->bg,
+                               TK_3D_LIGHT_GC);
+                       topGc    = Tk_3DBorderGC(tkwin, tagPtr->bg,
+                               TK_3D_DARK_GC);
+                       break;
+               }
+       
+               /* draw a line with single pixel width */
+               rect[0].x = x + width - 1;
+               rect[0].y = y;
+               rect[1].y = height - 1;
+               rect[2].x = -width + 1;
+               XDrawLines(display, window, bottomGc, rect, 3,
+                       CoordModePrevious);
+               rect[0].x = x;
+               rect[0].y = y + height - 1;
+               rect[1].y = -height + 1;
+               rect[2].x = width - 1;
+               XDrawLines(display, window, topGc, rect, 3,
+                       CoordModePrevious);
+           } else {
+               if (borders > 1) {
+                   if (bd[0]) {
+                       Tk_3DVerticalBevel(tkwin, window, tagPtr->bg,
+                               x, y, bd[0], height,
+                               1 /* left side */, tagPtr->relief);
+                   }
+                   if (bd[1]) {
+                       Tk_3DVerticalBevel(tkwin, window, tagPtr->bg,
+                               x + width - bd[1], y, bd[1], height,
+                               0 /* right side */, tagPtr->relief);
+                   }
+                   if ((borders == 4) && bd[2]) {
+                       Tk_3DHorizontalBevel(tkwin, window, tagPtr->bg,
+                               x, y, width, bd[2],
+                               1, 1, 1 /* top */, tagPtr->relief);
+                   }
+                   if ((borders == 4) && bd[3]) {
+                       Tk_3DHorizontalBevel(tkwin, window, tagPtr->bg,
+                               x, y + height - bd[3], width, bd[3],
+                               0, 0, 0 /* bottom */, tagPtr->relief);
+                   }
+               } else if (borders == 1) {
+                   Tk_Draw3DRectangle(tkwin, window, tagPtr->bg, x, y,
+                           width, height, bd[0], tagPtr->relief);
+               }
+           }
+
+           /* clean up the necessaries */
+           if (tagPtr == tablePtr->activeTagPtr) {
+               /*
+                * This means it was the activeCell with text displayed.
+                * We buffer the active tag for the 'activate' command.
+                */
+               tablePtr->activeTagPtr = TableNewTag(NULL);
+               memcpy((VOID *) tablePtr->activeTagPtr,
+                       (VOID *) tagPtr, sizeof(TableTag));
+           }
+           if (textLayout) {
+               Tk_FreeTextLayout(textLayout);
+               textLayout = NULL;
+           }
+           if (cellType == CELL_HIDDEN) {
+               /* the last cell was a hidden one,
+                * rework row stuff back to normal */
+               row = hrow; col = hcol;
+               urow = row+tablePtr->rowOffset;
+               rowPtr = FindRowColTag(tablePtr, urow, ROW);
+           }
+       }
     }
-    /* Do we have a X-scrollbar and cols to scroll? */
-    if (tablePtr->xScrollCmd != NULL) {
-      if (col < tablePtr->titleCols) {
-       first = 0;
-       last  = 1;
-      } else {
-       diff = tablePtr->colStarts[tablePtr->titleCols];
-       last = (double) (tablePtr->colStarts[tablePtr->cols]-diff);
-       first = (tablePtr->colStarts[leftCol]-diff) / last;
-       last  = (width+tablePtr->colStarts[col]-diff) / last;
-      }
-      sprintf(buf, " %g %g", first, last);
-      if (Tcl_VarEval(interp, tablePtr->xScrollCmd,
-                     buf, (char *) NULL) != TCL_OK) {
-       Tcl_AddErrorInfo(interp,
-               "\n    (horizontal scrolling command executed by table)");
-       Tcl_BackgroundError(interp);
-      }
+    ckfree((char *) tagPtr);
+#ifdef NO_XSETCLIP
+    Tk_FreePixmap(display, clipWind);
+#endif
+
+    /* Take care of removing embedded windows that are no longer in view */
+    TableUndisplay(tablePtr);
+
+    /* copy over and delete the pixmap if we are in slow mode */
+    if (tablePtr->drawMode == DRAW_MODE_SLOW) {
+       /* Get a default valued GC */
+       TableGetGc(display, window, &(tablePtr->defaultTag), &tagGc);
+       XCopyArea(display, window, Tk_WindowId(tkwin), tagGc, 0, 0,
+               invalidWidth, invalidHeight, invalidX, invalidY);
+       Tk_FreePixmap(display, window);
+       window = Tk_WindowId(tkwin);
     }
 
-    Tcl_Release((ClientData) interp);
-  }
+    /* 
+     * If we are at the end of the table, clear the area after the last
+     * row/col.  We discount spans here because we just need the coords
+     * for the area that would be the last physical cell.
+     */
+    tablePtr->flags |= AVOID_SPANS;
+    TableCellCoords(tablePtr, tablePtr->rows-1, tablePtr->cols-1,
+           &x, &y, &width, &height);
+    tablePtr->flags &= ~AVOID_SPANS;
+
+    /* This should occur before moving pixmap, but this simplifies things
+     *
+     * Could use Tk_Fill3DRectangle instead of XFillRectangle
+     * for best compatibility, and XClearArea could be used on Unix
+     * for best speed, so this is the compromise w/o #ifdef's
+     */
+    if (x+width < invalidX+invalidWidth) {
+       XFillRectangle(display, window,
+               Tk_3DBorderGC(tkwin, tablePtr->defaultTag.bg,
+                       TK_3D_FLAT_GC), x+width, invalidY,
+               invalidX+invalidWidth-x-width, invalidHeight);
+    }
 
-  /* Adjust the last row/col to fill empty space if it is visible */
-  /* do this after setting the scrollbars to not upset its calculations */
-  if (row == tablePtr->rows-1 && tablePtr->rowStretch != STRETCH_MODE_NONE) {
-    diff = h-(y+height);
-    if (diff > 0) {
-      tablePtr->rowPixels[tablePtr->rows-1] += diff;
-      tablePtr->rowStarts[tablePtr->rows] += diff;
+    if (y+height < invalidY+invalidHeight) {
+       XFillRectangle(display, window,
+               Tk_3DBorderGC(tkwin, tablePtr->defaultTag.bg,
+                       TK_3D_FLAT_GC), invalidX, y+height,
+               invalidWidth, invalidY+invalidHeight-y-height);
     }
-  }
-  if (col == tablePtr->cols-1 && tablePtr->colStretch != STRETCH_MODE_NONE) {
-    diff = w-(x+width);
-    if (diff > 0) {
-      tablePtr->colPixels[tablePtr->cols-1] += diff;
-      tablePtr->colStarts[tablePtr->cols] += diff;
+
+    if (tagGc != NULL) {
+       TableFreeGc(display, tagGc);
     }
-  }
-
-  TableAdjustActive(tablePtr);
-
-  /*
-   * now check the new value of topleft cell against the originals,
-   * If they changed, invalidate the area, else leave it alone
-   */
-  if (tablePtr->topRow != tablePtr->oldTopRow ||
-      tablePtr->leftCol != tablePtr->oldLeftCol) {
-    /* set the old top row/col for the next time this function is called */
-    tablePtr->oldTopRow = tablePtr->topRow;
-    tablePtr->oldLeftCol = tablePtr->leftCol;
-    /* only the upper corner title cells wouldn't change */
-    TableInvalidateAll(tablePtr, 0);
-  }
+    TableRedrawHighlight(tablePtr);
+    /* 
+     * Free the hash table used to cache evaluations.
+     */
+    Tcl_DeleteHashTable(colTagsCache);
+    ckfree((char *) (colTagsCache));
+    Tcl_DeleteHashTable(drawnCache);
+    ckfree((char *) (drawnCache));
 }
 
-/*
+/* 
  *----------------------------------------------------------------------
  *
- * TableCursorEvent --
- *     Toggle the cursor status.  Equivalent to EntryBlinkProc.
+ * TableInvalidate --
+ *     Invalidates a rectangle and adds it to the total invalid rectangle
+ *     waiting to be redrawn.  If the INV_FORCE flag bit is set,
+ *     it does an update instantly else waits until Tk is idle.
  *
  * Results:
- *     None.
+ *     Will schedule table (re)display.
  *
  * Side effects:
- *     The cursor will be switched off/on.
+ *     None
  *
  *----------------------------------------------------------------------
  */
-static void
-TableCursorEvent(ClientData clientData)
+void
+TableInvalidate(Table * tablePtr, int x, int y,
+               int w, int h, int flags)
 {
-  register Table *tablePtr = (Table *) clientData;
+    Tk_Window tkwin = tablePtr->tkwin;
+    int hl     = tablePtr->highlightWidth;
+    int height = Tk_Height(tkwin);
+    int width  = Tk_Width(tkwin);
 
-  if (!(tablePtr->flags & HAS_FOCUS) || (tablePtr->insertOffTime == 0)) {
-    return;
-  }
+    /*
+     * Make sure that the window hasn't been destroyed already.
+     * Avoid allocating 0 sized pixmaps which would be fatal,
+     * and check if rectangle is even on the screen.
+     */
+    if ((tkwin == NULL)
+           || (w <= 0) || (h <= 0) || (x > width) || (y > height)) {
+       return;
+    }
 
-  if (tablePtr->cursorTimer != NULL)
-    Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
+    /* If not even mapped, wait for the remap to redraw all */
+    if (!Tk_IsMapped(tkwin)) {
+       tablePtr->flags |= REDRAW_ON_MAP;
+       return;
+    }
 
-  tablePtr->cursorTimer =
-    Tcl_CreateTimerHandler((tablePtr->flags & CURSOR_ON) ?
-                          tablePtr->insertOffTime : tablePtr->insertOnTime,
-                          TableCursorEvent, (ClientData) tablePtr);
-  /* Toggle the cursor */
-  tablePtr->flags ^= CURSOR_ON;
+    /*
+     * If no pending updates exist, then replace the rectangle.
+     * Otherwise find the bounding rectangle.
+     */
+    if ((flags & INV_HIGHLIGHT) &&
+           (x < hl || y < hl || x+w >= width-hl || y+h >= height-hl)) {
+       tablePtr->flags |= REDRAW_BORDER;
+    }
 
-  /* invalidate the cell */
-  TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
-              CELL|INV_FORCE);
+    if (tablePtr->flags & REDRAW_PENDING) {
+       tablePtr->invalidWidth = MAX(x + w,
+               tablePtr->invalidX+tablePtr->invalidWidth);
+       tablePtr->invalidHeight = MAX(y + h,
+               tablePtr->invalidY+tablePtr->invalidHeight);
+       if (tablePtr->invalidX > x) tablePtr->invalidX = x;
+       if (tablePtr->invalidY > y) tablePtr->invalidY = y;
+       tablePtr->invalidWidth  -= tablePtr->invalidX;
+       tablePtr->invalidHeight -= tablePtr->invalidY;
+       /* Do we want to force this update out? */
+       if (flags & INV_FORCE) {
+           Tcl_CancelIdleCall(TableDisplay, (ClientData) tablePtr);
+           TableDisplay((ClientData) tablePtr);
+       }
+    } else {
+       tablePtr->invalidX = x;
+       tablePtr->invalidY = y;
+       tablePtr->invalidWidth = w;
+       tablePtr->invalidHeight = h;
+       if (flags & INV_FORCE) {
+           TableDisplay((ClientData) tablePtr);
+       } else {
+           tablePtr->flags |= REDRAW_PENDING;
+           Tcl_DoWhenIdle(TableDisplay, (ClientData) tablePtr);
+       }
+    }
 }
 
-/*
+/* 
  *----------------------------------------------------------------------
  *
- * TableConfigCursor --
- *     Configures the timer depending on the state of the table.
- *     Equivalent to EntryFocusProc.
+ * TableFlashEvent --
+ *     Called when the flash timer goes off.
  *
  * Results:
- *     None.
+ *     Decrements all the entries in the hash table and invalidates
+ *     any cells that expire, deleting them from the table.  If the
+ *     table is now empty, stops the timer, else reenables it.
  *
  * Side effects:
- *     The cursor will be switched off/on.
+ *     None.
  *
  *----------------------------------------------------------------------
  */
 static void
-TableConfigCursor(register Table *tablePtr)
+TableFlashEvent(ClientData clientdata)
 {
-  /* to get a cursor, we have to have focus and allow edits */
-  if ((tablePtr->flags & HAS_FOCUS) && !(tablePtr->flags & ACTIVE_DISABLED) &&
-      (tablePtr->state == STATE_NORMAL)) {
-    /* turn the cursor on */
-    if (!(tablePtr->flags & CURSOR_ON)) {
-      tablePtr->flags |= CURSOR_ON;
-    }
+    Table *tablePtr = (Table *) clientdata;
+    Tcl_HashEntry *entryPtr;
+    Tcl_HashSearch search;
+    int entries, count, row, col;
 
-    /* set up the first timer */
-    if (tablePtr->insertOffTime != 0) {
-      /* make sure nothing existed */
-      Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
-      tablePtr->cursorTimer = Tcl_CreateTimerHandler(tablePtr->insertOnTime,
-                                                    TableCursorEvent,
-                                                    (ClientData) tablePtr);
-    }
+    entries = 0;
+    for (entryPtr = Tcl_FirstHashEntry(tablePtr->flashCells, &search);
+        entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
+       count = (int) Tcl_GetHashValue(entryPtr);
+       if (--count <= 0) {
+           /* get the cell address and invalidate that region only */
+           TableParseArrayIndex(&row, &col,
+                   Tcl_GetHashKey(tablePtr->flashCells, entryPtr));
 
-  } else {
-    /* turn the cursor off */
-    if ((tablePtr->flags & CURSOR_ON)) {
-      tablePtr->flags &= ~CURSOR_ON;
-    }
+           /* delete the entry from the table */
+           Tcl_DeleteHashEntry(entryPtr);
 
-    /* and disable the timer */
-    if (tablePtr->cursorTimer != NULL)
-      Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
-    tablePtr->cursorTimer = NULL;
-  }
+           TableRefresh(tablePtr, row-tablePtr->rowOffset,
+                   col-tablePtr->colOffset, CELL);
+       } else {
+           Tcl_SetHashValue(entryPtr, (ClientData) count);
+           entries++;
+       }
+    }
 
-  /* Invalidate the selection window to show or hide the cursor */
-  TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
-              CELL|INV_FORCE);
+    /* do I need to restart the timer */
+    if (entries && tablePtr->flashMode) {
+       tablePtr->flashTimer = Tcl_CreateTimerHandler(250, TableFlashEvent,
+               (ClientData) tablePtr);
+    } else {
+       tablePtr->flashTimer = 0;
+    }
 }
 
-/*
+/* 
  *----------------------------------------------------------------------
  *
- * TableFetchSelection --
- *     This procedure is called back by Tk when the selection is
- *     requested by someone.  It returns part or all of the selection
- *     in a buffer provided by the caller.
+ * TableAddFlash --
+ *     Adds a flash on cell row,col (real coords) with the default timeout
+ *     if flashing is enabled and flashtime > 0.
  *
  * Results:
- *     The return value is the number of non-NULL bytes stored
- *     at buffer.  Buffer is filled (or partially filled) with a
- *     NULL-terminated string containing part or all of the selection,
- *     as given by offset and maxBytes.
+ *     Cell will flash.
  *
  * Side effects:
- *     None.
+ *     Will start flash timer if it didn't exist.
  *
  *----------------------------------------------------------------------
  */
-static int
-TableFetchSelection(clientData, offset, buffer, maxBytes)
-    ClientData clientData;             /* Information about table widget. */
-    int offset;                                /* Offset within selection of first
-                                        * character to be returned. */
-    char *buffer;                      /* Location in which to place
-                                        * selection. */
-    int maxBytes;                      /* Maximum number of bytes to place
-                                        * at buffer, not including terminating
-                                        * NULL character. */
+void
+TableAddFlash(Table *tablePtr, int row, int col)
 {
-  register Table *tablePtr = (Table *) clientData;
-  Tcl_Interp *interp = tablePtr->interp;
-  char *value, *data, *rowsep = tablePtr->rowSep, *colsep = tablePtr->colSep;
-  Tcl_DString selection;
-  Tcl_HashEntry *entryPtr;
-  Tcl_HashSearch search;
-  int length, count, lastrow=0, needcs=0, r, c, listArgc, rslen=0, cslen=0;
-  int numcols, numrows;
-  char **listArgv;
-
-  /* if we are not exporting the selection || we have no data source, return */
-  if (!tablePtr->exportSelection ||
-      (tablePtr->dataSource == DATA_NONE)) {
-    return -1;
-  }
-
-  /* First get a sorted list of the selected elements */
-  Tcl_DStringInit(&selection);
-  for (entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
-       entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
-    Tcl_DStringAppendElement(&selection,
-                            Tcl_GetHashKey(tablePtr->selCells, entryPtr));
-  }
-  value = TableCellSort(tablePtr, Tcl_DStringValue(&selection));
-  Tcl_DStringFree(&selection);
-
-  if (value == NULL ||
-      Tcl_SplitList(interp, value, &listArgc, &listArgv) != TCL_OK) {
-    return -1;
-  }
-  ckfree(value);
-
-  Tcl_DStringInit(&selection);
-  rslen = (rowsep?(strlen(rowsep)):0);
-  cslen = (colsep?(strlen(colsep)):0);
-  numrows = numcols = 0;
-  for (count = 0; count < listArgc; count++) {
-    TableParseArrayIndex(&r, &c, listArgv[count]);
-    if (count) {
-      if (lastrow != r) {
-       lastrow = r;
-       needcs = 0;
-       if (rslen) {
-         Tcl_DStringAppend(&selection, rowsep, rslen);
-       } else {
-         Tcl_DStringEndSublist(&selection);
-         Tcl_DStringStartSublist(&selection);
-       }
-       ++numrows;
-      } else {
-       if (++needcs > numcols)
-         numcols = needcs;
-      }
-    } else {
-      lastrow = r;
-      needcs = 0;
-      if (!rslen)
-       Tcl_DStringStartSublist(&selection);
-    }
-    data = TableGetCellValue(tablePtr, r, c);
-    if (cslen) {
-      if (needcs)
-       Tcl_DStringAppend(&selection, colsep, cslen);
-      Tcl_DStringAppend(&selection, data, -1);
-    } else {
-      Tcl_DStringAppendElement(&selection, data);
-    }
-  }
-  if (!rslen && count)
-    Tcl_DStringEndSublist(&selection);
-  ckfree((char *) listArgv);
+    char buf[INDEX_BUFSIZE];
+    int dummy;
+    Tcl_HashEntry *entryPtr;
 
-  if (tablePtr->selCmd != NULL) {
-    Tcl_DString script;
-    Tcl_DStringInit(&script);
-    ExpandPercents(tablePtr, tablePtr->selCmd, numrows+1, numcols+1,
-                  Tcl_DStringValue(&selection), (char *) NULL,
-                  listArgc, &script, CMD_ACTIVATE);
-    if (Tcl_GlobalEval(interp, Tcl_DStringValue(&script)) == TCL_ERROR) {
-      Tcl_AddErrorInfo(interp,
-                      "\n    (error in table selection command)");
-      Tcl_BackgroundError(interp);
-      Tcl_DStringFree(&script);
-      Tcl_DStringFree(&selection);
-      return -1;
-    } else {
-      Tcl_DStringGetResult(interp, &selection);
+    if (!tablePtr->flashMode || tablePtr->flashTime < 1) {
+       return;
     }
-    Tcl_DStringFree(&script);
-  }
 
-  length = Tcl_DStringLength(&selection);
+    /* create the array index in user coords */
+    TableMakeArrayIndex(row+tablePtr->rowOffset, col+tablePtr->colOffset, buf);
 
-  if (length == 0)
-    return -1;
+    /* add the flash to the hash table */
+    entryPtr = Tcl_CreateHashEntry(tablePtr->flashCells, buf, &dummy);
+    Tcl_SetHashValue(entryPtr, tablePtr->flashTime);
 
-  /* Copy the requested portion of the selection to the buffer. */
-  count = length - offset;
-  if (count <= 0) {
-    count = 0;
-  } else {
-    if (count > maxBytes) {
-      count = maxBytes;
+    /* now set the timer if it's not already going and invalidate the area */
+    if (tablePtr->flashTimer == NULL) {
+       tablePtr->flashTimer = Tcl_CreateTimerHandler(250, TableFlashEvent,
+               (ClientData) tablePtr);
     }
-    memcpy((VOID *) buffer, (VOID *) (Tcl_DStringValue(&selection) + offset),
-          (size_t) count);
-  }
-  buffer[count] = '\0';
-  Tcl_DStringFree(&selection);
-  return count;
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TableLostSelection --
- *     This procedure is called back by Tk when the selection is
- *     grabbed away from a table widget.
+ * TableSetActiveIndex --
+ *     Sets the "active" index of the associated array to the current
+ *     value of the active buffer.
  *
  * Results:
  *     None.
  *
  * Side effects:
- *     The existing selection is unhighlighted, and the window is
- *     marked as not containing a selection.
+ *     Traces on the array can cause side effects.
  *
  *----------------------------------------------------------------------
  */
-static void
-TableLostSelection(clientData)
-    ClientData clientData;     /* Information about table widget. */
+void
+TableSetActiveIndex(register Table *tablePtr)
 {
-  register Table *tablePtr = (Table *) clientData;
-
-  if (tablePtr->exportSelection) {
-    Tcl_HashEntry *entryPtr;
-    Tcl_HashSearch search;
-    int row, col;
-
-    /* Same as SEL CLEAR ALL */
-    for (entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
-        entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
-      TableParseArrayIndex(&row, &col,
-                          Tcl_GetHashKey(tablePtr->selCells,entryPtr));
-      Tcl_DeleteHashEntry(entryPtr);
-      TableRefresh(tablePtr, row-tablePtr->rowOffset,
-                  col-tablePtr->colOffset, CELL);
+    if (tablePtr->arrayVar) {
+       tablePtr->flags |= SET_ACTIVE;
+       Tcl_SetVar2(tablePtr->interp, tablePtr->arrayVar, "active",
+               tablePtr->activeBuf, TCL_GLOBAL_ONLY);
+       tablePtr->flags &= ~SET_ACTIVE;
     }
-  }
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TableRestrictProc --
- *     A Tk_RestrictProc used by TableValidateChange to eliminate any
- *     extra key input events in the event queue that
- *     have a serial number no less than a given value.
+ * TableGetActiveBuf --
+ *     Get the current selection into the buffer and mark it as unedited.
+ *     Set the position to the end of the string.
  *
  * Results:
- *     Returns either TK_DISCARD_EVENT or TK_DEFER_EVENT.
- *
- * Side effects:
  *     None.
  *
- *----------------------------------------------------------------------
- */
-static Tk_RestrictAction
-TableRestrictProc(serial, eventPtr)
-    ClientData serial;
-    XEvent *eventPtr;
-{
-  if ((eventPtr->type == KeyRelease || eventPtr->type == KeyPress) &&
-      ((eventPtr->xany.serial-(unsigned int)serial) > 0)) {
-    return TK_DEFER_EVENT;
-  } else {
-    return TK_PROCESS_EVENT;
-  }
-}
-
-/*
- *--------------------------------------------------------------
- *
- * TableValidateChange --
- *     This procedure is invoked when any character is added or
- *     removed from the table widget, or a set has triggered validation.
- *
- * Results:
- *     TCL_OK    if the validatecommand accepts the new string,
- *     TCL_BREAK if the validatecommand rejects the new string,
- *      TCL_ERROR if any problems occured with validatecommand.
- *
- * Side effects:
- *      The insertion/deletion may be aborted, and the
- *      validatecommand might turn itself off (if an error
- *      or loop condition arises).
- *
- *--------------------------------------------------------------
- */
-static int
-TableValidateChange(tablePtr, r, c, old, new, index)
-     register Table *tablePtr; /* Table that needs validation. */
-     int r, c;                 /* row,col index of cell in user coords */
-     char *old;                        /* current value of cell */
-     char *new;                        /* potential new value of cell */
-     int index;                        /* index of insert/delete, -1 otherwise */
-{
-  register Tcl_Interp *interp = tablePtr->interp;
-  int code, bool;
-  Tk_RestrictProc *restrict;
-  ClientData cdata;
-  Tcl_DString script;
-    
-  if (tablePtr->valCmd == NULL || tablePtr->validate == 0) {
-    return TCL_OK;
-  }
-
-  /* Magic code to make this bit of code synchronous in the face of
-   * possible new key events */
-  XSync(tablePtr->display, False);
-  restrict = Tk_RestrictEvents(TableRestrictProc, (ClientData)
-                              NextRequest(tablePtr->display), &cdata);
-
-  /*
-   * If we're already validating, then we're hitting a loop condition
-   * Return and set validate to 0 to disallow further validations
-   * and prevent current validation from finishing
-   */
-  if (tablePtr->flags & VALIDATING) {
-    tablePtr->validate = 0;
-    return TCL_OK;
-  }
-  tablePtr->flags |= VALIDATING;
-
-  /* Now form command string and run through the -validatecommand */
-  Tcl_DStringInit(&script);
-  ExpandPercents(tablePtr, tablePtr->valCmd, r, c, old, new, index, &script,
-                CMD_VALIDATE);
-  code = Tcl_GlobalEval(tablePtr->interp, Tcl_DStringValue(&script));
-  Tcl_DStringFree(&script);
-
-  if (code != TCL_OK && code != TCL_RETURN) {
-    Tcl_AddErrorInfo(interp, "\n\t(in validation command executed by table)");
-    Tk_BackgroundError(interp);
-    code = TCL_ERROR;
-  } else if (Tcl_GetBoolean(interp, Tcl_GetStringResult(interp),
-                           &bool) != TCL_OK) {
-    Tcl_AddErrorInfo(interp, "\n\tboolean not returned by validation command");
-    Tk_BackgroundError(interp);
-    code = TCL_ERROR;
-  } else {
-    if (bool)
-      code = TCL_OK;
-    else
-      code = TCL_BREAK;
-  }
-  Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
-
-  /*
-   * If ->validate has become VALIDATE_NONE during the validation,
-   * it means that a loop condition almost occured.  Do not allow
-   * this validation result to finish.
-   */
-  if (tablePtr->validate == 0) {
-    code = TCL_ERROR;
-  }
-
-  /* If validate will return ERROR, then disallow further validations */
-  if (code == TCL_ERROR) {
-    tablePtr->validate = 0;
-  }
-
-  Tk_RestrictEvents(restrict, cdata, &cdata);
-  tablePtr->flags &= ~VALIDATING;
-
-  return code;
-}
-
-/*
- *--------------------------------------------------------------
- *
- * ExpandPercents --
- *     Given a command and an event, produce a new command
- *     by replacing % constructs in the original command
- *     with information from the X event.
- *
- * Results:
- *     The new expanded command is appended to the dynamic string
- *     given by dsPtr.
- *
  * Side effects:
- *     None.
+ *     tablePtr->activeBuf will change.
  *
- *--------------------------------------------------------------
+ *----------------------------------------------------------------------
  */
 void
-ExpandPercents(tablePtr, before, r, c, old, new, index, dsPtr, cmdType)
-     Table *tablePtr;          /* Table that needs validation. */
-     char *before;             /* Command containing percent
-                                * expressions to be replaced. */
-     int r, c;                 /* row,col index of cell */
-     char *old;                 /* current value of cell */
-     char *new;                 /* potential new value of cell */
-     int index;                 /* index of insert/delete */
-     Tcl_DString *dsPtr;        /* Dynamic string in which to append
-                                * new command. */
-     int cmdType;              /* type of command to make %-subs for */
+TableGetActiveBuf(register Table *tablePtr)
 {
-  int spaceNeeded, cvtFlags;   /* Used to substitute string as proper Tcl
-                                * list element. */
-  int number, length;
-  char *string;
-  char buf[INDEX_BUFSIZE];
-
-  /* This returns the static value of the string as set in the array */
-  if (old == NULL && cmdType == CMD_VALIDATE) {
-    old = TableGetCellValue(tablePtr, r, c);
-  }
-
-  while (1) {
-    /*
-     * Find everything up to the next % character and append it
-     * to the result string.
-     */
-    for (string = before; (*string != 0) && (*string != '%'); string++) {
-      /* Empty loop body. */
-    }
-    if (string != before) {
-      Tcl_DStringAppend(dsPtr, before, string-before);
-      before = string;
-    }
-    if (*before == 0) break;
-
-    /* There's a percent sequence here.  Process it. */
-    number = 0;
-    string = "??";
-    /* cmdType independent substitutions */
-    switch (before[1]) {
-    case 'c':
-      number = c;
-      goto doNumber;
-    case 'C': /* index of cell */
-      TableMakeArrayIndex(r, c, buf);
-      string = buf;
-      goto doString;
-    case 'r':
-      number = r;
-      goto doNumber;
-    case 'i': /* index of cursor OR |number| of cells selected */
-      number = index;
-      goto doNumber;
-    case 's': /* Current cell value */
-      string = old;
-      goto doString;
-    case 'S': /* Potential new value of cell */
-      string = (new?new:old);
-      goto doString;
-    case 'W': /* widget name */
-      string = Tk_PathName(tablePtr->tkwin);
-      goto doString;
-    default:
-      buf[0] = before[1];
-      buf[1] = '\0';
-      string = buf;
-      goto doString;
+    char *data = "";
+
+    if (tablePtr->flags & HAS_ACTIVE) {
+       data = TableGetCellValue(tablePtr,
+               tablePtr->activeRow+tablePtr->rowOffset,
+               tablePtr->activeCol+tablePtr->colOffset);
     }
-       
-  doNumber:
-    sprintf(buf, "%d", number);
-    string = buf;
-
-  doString:
-    spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
-    length = Tcl_DStringLength(dsPtr);
-    Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
-    spaceNeeded = Tcl_ConvertElement(string, Tcl_DStringValue(dsPtr) + length,
-                                    cvtFlags | TCL_DONT_USE_BRACES);
-    Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
-    before += 2;
-  }
-  Tcl_DStringAppend(dsPtr, "", 1);
+
+    if (STREQ(tablePtr->activeBuf, data)) {
+       /* this forced SetActiveIndex is necessary if we change array vars and
+        * they happen to have these cells equal, we won't properly set the
+        * active index for the new array var unless we do this here */
+       TableSetActiveIndex(tablePtr);
+       return;
+    }
+    /* is the buffer long enough */
+    tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf,
+           strlen(data)+1);
+    strcpy(tablePtr->activeBuf, data);
+    TableGetIcursor(tablePtr, "end", (int *)0);
+    tablePtr->flags &= ~TEXT_CHANGED;
+    TableSetActiveIndex(tablePtr);
 }
 
-/*
+/* 
  *----------------------------------------------------------------------
  *
- * TableDeleteChars --
- *     Remove one or more characters from an table widget.
+ * TableVarProc --
+ *     This is the trace procedure associated with the Tcl array.  No
+ *     validation will occur here because this only triggers when the
+ *     array value is directly set, and we can't maintain the old value.
  *
  * Results:
- *     None.
+ *     Invalidates changed cell.
  *
  * Side effects:
- *     Memory gets freed, the table gets modified and (eventually)
- *     redisplayed.
+ *     Creates/Updates entry in the cache if we are caching.
  *
  *----------------------------------------------------------------------
  */
-static void
-TableDeleteChars(tablePtr, index, count)
-    register Table *tablePtr;  /* Table widget to modify. */
-    int index;                 /* Index of first character to delete. */
-    int count;                 /* How many characters to delete. */
+static char *
+TableVarProc(clientData, interp, name, index, flags)
+     ClientData clientData;    /* Information about table. */
+     Tcl_Interp *interp;               /* Interpreter containing variable. */
+     char *name;                       /* Not used. */
+     char *index;              /* Not used. */
+     int flags;                        /* Information about what happened. */
 {
-  int x, y, width, height;
-#if (TK_MINOR_VERSION > 0)
-  int byteIndex, byteCount, newByteCount, numBytes, numChars;
-  char *new, *string;
-
-  string = tablePtr->activeBuf;
-  numBytes = strlen(string);
-  numChars = Tcl_NumUtfChars(string, numBytes);
-  if ((index + count) > numChars) {
-    count = numChars - index;
-  }
-  if (count <= 0) {
-    return;
-  }
-
-  byteIndex = Tcl_UtfAtIndex(string, index) - string;
-  byteCount = Tcl_UtfAtIndex(string + byteIndex, count) - (string + byteIndex);
-
-  newByteCount = numBytes + 1 - byteCount;
-  new = (char *) ckalloc((unsigned) newByteCount);
-  memcpy(new, string, (size_t) byteIndex);
-  strcpy(new + byteIndex, string + byteIndex + byteCount);
-#else
-  int oldlen;
-  char *new;
-
-  /* this gets the length of the string, as well as ensuring that
-   * the cursor isn't beyond the end char */
-  TableGetIcursor(tablePtr, "end", &oldlen);
-
-  if ((index+count) > oldlen)
-    count = oldlen-index;
-  if (count <= 0)
-    return;
-
-  new = (char *) ckalloc((unsigned)(oldlen-count+1));
-  strncpy(new, tablePtr->activeBuf, (size_t) index);
-  strcpy(new+index, tablePtr->activeBuf+index+count);
-  /* make sure this string is null terminated */
-  new[oldlen-count] = '\0';
-#endif
-  /* This prevents deletes on BREAK or validation error. */
-  if (tablePtr->validate &&
-      TableValidateChange(tablePtr, tablePtr->activeRow+tablePtr->rowOffset,
-                         tablePtr->activeCol+tablePtr->colOffset,
-                         tablePtr->activeBuf, new, index) != TCL_OK) {
-    ckfree(new);
-    return;
-  }
-
-  ckfree(tablePtr->activeBuf);
-  tablePtr->activeBuf = new;
-
-  /* mark the text as changed */
-  tablePtr->flags |= TEXT_CHANGED;
-
-  if (tablePtr->icursor >= index) {
-    if (tablePtr->icursor >= (index+count)) {
-      tablePtr->icursor -= count;
+    Table *tablePtr = (Table *) clientData;
+    int dummy, row, col, update = 1;
+
+    /* This is redundant, as the name should always == arrayVar */
+    name = tablePtr->arrayVar;
+
+    /* is this the whole var being destroyed or just one cell being deleted */
+    if ((flags & TCL_TRACE_UNSETS) && index == NULL) {
+       /* if this isn't the interpreter being destroyed reinstate the trace */
+       if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
+           Tcl_SetVar2(interp, name, TEST_KEY, "", TCL_GLOBAL_ONLY);
+           Tcl_UnsetVar2(interp, name, TEST_KEY, TCL_GLOBAL_ONLY);
+           Tcl_ResetResult(interp);
+
+           /* set a trace on the variable */
+           Tcl_TraceVar(interp, name,
+                   TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY,
+                   (Tcl_VarTraceProc *)TableVarProc, (ClientData) tablePtr);
+
+           /* only do the following if arrayVar is our data source */
+           if (tablePtr->dataSource & DATA_ARRAY) {
+               /* clear the selection buffer */
+               TableGetActiveBuf(tablePtr);
+               /* flush any cache */
+               Tcl_DeleteHashTable(tablePtr->cache);
+               Tcl_InitHashTable(tablePtr->cache, TCL_STRING_KEYS);
+               /* and invalidate the table */
+               TableInvalidateAll(tablePtr, 0);
+           }
+       }
+       return (char *)NULL;
+    }
+    /* only continue if arrayVar is our data source */
+    if (!(tablePtr->dataSource & DATA_ARRAY)) {
+       return (char *)NULL;
+    }
+    /* get the cell address and invalidate that region only.
+     * Make sure that it is a valid cell address. */
+    if (STREQ("active", index)) {
+       if (tablePtr->flags & SET_ACTIVE) {
+           /* If we are already setting the active cell, the update
+            * will occur in other code */
+           update = 0;
+       } else {
+           /* modified TableGetActiveBuf */
+           char *data = "";
+
+           row = tablePtr->activeRow;
+           col = tablePtr->activeCol;
+           if (tablePtr->flags & HAS_ACTIVE)
+               data = Tcl_GetVar2(interp, name, index, TCL_GLOBAL_ONLY);
+           if (!data) data = "";
+
+           if (STREQ(tablePtr->activeBuf, data)) {
+               return (char *)NULL;
+           }
+           tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf,
+                   strlen(data)+1);
+           strcpy(tablePtr->activeBuf, data);
+           /* set cursor to the last char */
+           TableGetIcursor(tablePtr, "end", (int *)0);
+           tablePtr->flags |= TEXT_CHANGED;
+       }
+    } else if (TableParseArrayIndex(&row, &col, index) == 2) {
+       char buf[INDEX_BUFSIZE];
+       /* Make sure it won't trigger on array(2,3extrastuff) */
+       TableMakeArrayIndex(row, col, buf);
+       if (strcmp(buf, index)) {
+           return (char *)NULL;
+       }
+       if (tablePtr->caching) {
+           Tcl_HashEntry *entryPtr;
+           char *val, *data = NULL;
+
+           data = Tcl_GetVar2(interp, name, index, TCL_GLOBAL_ONLY);
+           if (!data) data = "";
+           val = (char *)ckalloc(strlen(data)+1);
+           strcpy(val, data);
+           entryPtr = Tcl_CreateHashEntry(tablePtr->cache, buf, &dummy);
+           Tcl_SetHashValue(entryPtr, val);
+       }
+       /* convert index to real coords */
+       row -= tablePtr->rowOffset;
+       col -= tablePtr->colOffset;
+       /* did the active cell just update */
+       if (row == tablePtr->activeRow && col == tablePtr->activeCol) {
+           TableGetActiveBuf(tablePtr);
+       }
+       /* Flash the cell */
+       TableAddFlash(tablePtr, row, col);
     } else {
-      tablePtr->icursor = index;
+       return (char *)NULL;
     }
-  }
 
-  TableSetActiveIndex(tablePtr);
+    if (update) {
+       TableRefresh(tablePtr, row, col, CELL);
+    }
 
-  TableCellCoords(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
-                  &x, &y, &width, &height);
-  TableInvalidate(tablePtr, x, y, width, height, INV_FORCE);
+    return (char *)NULL;
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TableInsertChars --
- *     Add new characters to the active cell of a table widget.
+ * TableGeometryRequest --
+ *     This procedure is invoked to request a new geometry from Tk.
  *
  * Results:
  *     None.
  *
  * Side effects:
- *     New information gets added to tablePtr; it will be redisplayed
- *     soon, but not necessarily immediately.
+ *     Geometry information is updated and a new requested size is
+ *     registered for the widget.  Internal border info is also set.
  *
  *----------------------------------------------------------------------
  */
-static void
-TableInsertChars(tablePtr, index, value)
-    register Table *tablePtr;  /* Table that is to get the new elements. */
-    int index;                 /* Add the new elements before this element. */
-    char *value;               /* New characters to add (NULL-terminated
-                                * string). */
+void
+TableGeometryRequest(tablePtr)
+     register Table *tablePtr;
 {
-#if (TK_MINOR_VERSION > 0)
-  int x, y, width, height, oldlen;
-  int byteIndex, byteCount;
-  char *new, *string;
-
-  string = tablePtr->activeBuf;
-  byteIndex = Tcl_UtfAtIndex(string, index) - string;
-  byteCount = strlen(value);
-  if (byteCount == 0) {
-    return;
-  }
-
-  /* Is this an autoclear and this is the first update */
-  /* Note that this clears without validating */
-  if (tablePtr->autoClear && !(tablePtr->flags & TEXT_CHANGED)) {
-    /* set the buffer to be empty */
-    tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf, 1);
-    tablePtr->activeBuf[0] = '\0';
-    /* the insert position now has to be 0 */
-    index = 0;
-  }
-
-  oldlen = strlen(string);
-  new = (char *) ckalloc((unsigned)(oldlen + byteCount + 1));
-  memcpy(new, string, (size_t) byteIndex);
-  strcpy(new + byteIndex, value);
-  strcpy(new + byteIndex + byteCount, string + byteIndex);
-
-  /* validate potential new active buffer */
-  /* This prevents inserts on either BREAK or validation error. */
-  if (tablePtr->validate &&
-      TableValidateChange(tablePtr, tablePtr->activeRow+tablePtr->rowOffset,
-                         tablePtr->activeCol+tablePtr->colOffset,
-                         tablePtr->activeBuf, new, byteIndex) != TCL_OK) {
-    ckfree(new);
-    return;
-  }
-
-  /*
-   * The following construction is used because inserting improperly
-   * formed UTF-8 sequences between other improperly formed UTF-8
-   * sequences could result in actually forming valid UTF-8 sequences;
-   * the number of characters added may not be Tcl_NumUtfChars(string, -1),
-   * because of context.  The actual number of characters added is how
-   * many characters were are in the string now minus the number that
-   * used to be there.
-   */
-
-  if (tablePtr->icursor >= index) {
-    tablePtr->icursor += Tcl_NumUtfChars(new, oldlen+byteCount)
-      - Tcl_NumUtfChars(tablePtr->activeBuf, oldlen);
-  }
-
-  ckfree(string);
-  tablePtr->activeBuf = new;
-
-#else
-  int x, y, width, height, oldlen, newlen;
-  char *new;
-
-  newlen = strlen(value);
-  if (newlen == 0) return;
-
-  /* Is this an autoclear and this is the first update */
-  /* Note that this clears without validating */
-  if (tablePtr->autoClear && !(tablePtr->flags & TEXT_CHANGED)) {
-    /* set the buffer to be empty */
-    tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf, 1);
-    tablePtr->activeBuf[0] = '\0';
-    /* the insert position now has to be 0 */
-    index = 0;
-  }
-  oldlen = strlen(tablePtr->activeBuf);
-  /* get the buffer to at least the right length */
-  new = (char *) ckalloc((unsigned)(oldlen+newlen+1));
-  strncpy(new, tablePtr->activeBuf, (size_t) index);
-  strcpy(new+index, value);
-  strcpy(new+index+newlen, (tablePtr->activeBuf)+index);
-  /* make sure this string is null terminated */
-  new[oldlen+newlen] = '\0';
-
-  /* validate potential new active buffer */
-  /* This prevents inserts on either BREAK or validation error. */
-  if (tablePtr->validate &&
-      TableValidateChange(tablePtr, tablePtr->activeRow+tablePtr->rowOffset,
-                         tablePtr->activeCol+tablePtr->colOffset,
-                         tablePtr->activeBuf, new, index) != TCL_OK) {
-    ckfree(new);
-    return;
-  }
-  ckfree(tablePtr->activeBuf);
-  tablePtr->activeBuf = new;
-
-  if (tablePtr->icursor >= index) {
-    tablePtr->icursor += newlen;
-  }
-#endif
-
-  /* mark the text as changed */
-  tablePtr->flags |= TEXT_CHANGED;
-
-  TableSetActiveIndex(tablePtr);
-
-  TableCellCoords(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
-                  &x, &y, &width, &height);
-  TableInvalidate(tablePtr, x, y, width, height, INV_FORCE);
+    int x, y;
+
+    /* Do the geometry request
+     * If -width #cols was not specified or it is greater than the real
+     * number of cols, use maxWidth as a lower bound, with the other lower
+     * bound being the upper bound of the window's user-set width and the
+     * value of -maxwidth set by the programmer
+     * Vice versa for rows/height
+     */
+    x = MIN((tablePtr->maxReqCols==0 || tablePtr->maxReqCols > tablePtr->cols)?
+           tablePtr->maxWidth : tablePtr->colStarts[tablePtr->maxReqCols],
+           tablePtr->maxReqWidth) + 2*tablePtr->highlightWidth;
+    y = MIN((tablePtr->maxReqRows==0 || tablePtr->maxReqRows > tablePtr->rows)?
+           tablePtr->maxHeight : tablePtr->rowStarts[tablePtr->maxReqRows],
+           tablePtr->maxReqHeight) + 2*tablePtr->highlightWidth;
+    Tk_GeometryRequest(tablePtr->tkwin, x, y);
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TableModifyRCaux --
- *     Helper function that does the core work of moving rows/cols
- *     and associated tags.
+ * TableAdjustActive --
+ *     This procedure is called by AdjustParams and CMD_ACTIVATE to
+ *     move the active cell.
  *
  * Results:
- *     None.
+ *     Old and new active cell indices will be invalidated.
  *
  * Side effects:
- *     Moves cell data and possibly tag data
+ *     If the old active cell index was edited, it will be saved.
+ *     The active buffer will be updated.
  *
  *----------------------------------------------------------------------
  */
-static void
-TableModifyRCaux(tablePtr, type, which, movetag, tagTblPtr, dimTblPtr,
-                offset, from, to, lo, hi, check)
-    Table *tablePtr;   /* Information about text widget. */
-    int type;          /* insert (CMD_INSERT) | delete (CMD_DELETE) */
-    int which;         /* rows (MOD_ROWS) or cols (MOD_COLS) */
-    int movetag;       /* whether tags are supposed to be moved */
-    Tcl_HashTable *tagTblPtr, *dimTblPtr; /* Pointers to the row/col tags
-                                          * and width/height tags */
-    int offset;                /* appropriate offset */
-    int from, to;      /* the from and to row/col */
-    int lo, hi;                /* the lo and hi col/row */
-    int check;         /* the boundary check for shifting items */
+void
+TableAdjustActive(tablePtr)
+     register Table *tablePtr;         /* Widget record for table */
 {
-  int j, dummy;
-  char buf[INDEX_BUFSIZE], buf1[INDEX_BUFSIZE];
-  Tcl_HashEntry *entryPtr, *newPtr;
-
-  /* move row/col style && width/height here */
-  if (movetag) {
-    if ((entryPtr=Tcl_FindHashEntry(tagTblPtr, (char *)from)) != NULL) {
-      Tcl_DeleteHashEntry(entryPtr);
-    }
-    if ((entryPtr=Tcl_FindHashEntry(dimTblPtr, (char *)from-offset)) != NULL) {
-      Tcl_DeleteHashEntry(entryPtr);
-    }
-    if (!check) {
-      if ((entryPtr=Tcl_FindHashEntry(tagTblPtr, (char *)to)) != NULL) {
-       newPtr = Tcl_CreateHashEntry(tagTblPtr, (char *)from, &dummy);
-       Tcl_SetHashValue(newPtr, Tcl_GetHashValue(entryPtr));
-       Tcl_DeleteHashEntry(entryPtr);
-      }
-      if ((entryPtr=Tcl_FindHashEntry(dimTblPtr, (char *)to-offset)) != NULL) {
-       newPtr = Tcl_CreateHashEntry(dimTblPtr, (char *)from-offset, &dummy);
-       Tcl_SetHashValue(newPtr, Tcl_GetHashValue(entryPtr));
-       Tcl_DeleteHashEntry(entryPtr);
-      }
+    if (tablePtr->flags & HAS_ACTIVE) {
+       /*
+        * Make sure the active cell has a reasonable real index
+        */
+       CONSTRAIN(tablePtr->activeRow, 0, tablePtr->rows-1);
+       CONSTRAIN(tablePtr->activeCol, 0, tablePtr->cols-1);
     }
-  }
-  for (j = lo; j <= hi; j++) {
-    if (which == MOD_COLS) {
-      TableMakeArrayIndex(j, from, buf);
-      TableMakeArrayIndex(j, to, buf1);
-      TableSetCellValue(tablePtr, j, from, check ? "" :
-                       TableGetCellValue(tablePtr, j, to));
-    } else {
-      TableMakeArrayIndex(from, j, buf);
-      TableMakeArrayIndex(to, j, buf1);
-      TableSetCellValue(tablePtr, from, j, check ? "" :
-                       TableGetCellValue(tablePtr, to, j));
+
+    /*
+     * Check the new value of active cell against the original,
+     * Only invalidate if it changed.
+     */
+    if (tablePtr->oldActRow == tablePtr->activeRow &&
+           tablePtr->oldActCol == tablePtr->activeCol) {
+       return;
     }
-    /* move cell style here */
-    if (movetag) {
-      if ((entryPtr=Tcl_FindHashEntry(tablePtr->cellStyles,buf)) != NULL) {
-       Tcl_DeleteHashEntry(entryPtr);
-      }
-      if (!check &&
-         (entryPtr=Tcl_FindHashEntry(tablePtr->cellStyles,buf1))!=NULL) {
-       newPtr = Tcl_CreateHashEntry(tablePtr->cellStyles, buf, &dummy);
-       Tcl_SetHashValue(newPtr, Tcl_GetHashValue(entryPtr));
-       Tcl_DeleteHashEntry(entryPtr);
-      }
+
+    if (tablePtr->oldActRow >= 0 && tablePtr->oldActCol >= 0) {
+       /* 
+        * Set the value of the old active cell to the active buffer
+        * SetCellValue will check if the value actually changed
+        */
+       if (tablePtr->flags & TEXT_CHANGED) {
+           /* WARNING an outside trace will be triggered here and if it
+            * calls something that causes TableAdjustParams to be called
+            * again, we are in data consistency trouble */
+           /* HACK - turn TEXT_CHANGED off now to possibly avoid the
+            * above data inconsistency problem.  */
+           tablePtr->flags &= ~TEXT_CHANGED;
+           TableSetCellValue(tablePtr,
+                   tablePtr->oldActRow + tablePtr->rowOffset,
+                   tablePtr->oldActCol + tablePtr->colOffset,
+                   tablePtr->activeBuf);
+       }
+       /*
+        * Invalidate the old active cell
+        */
+       TableRefresh(tablePtr, tablePtr->oldActRow, tablePtr->oldActCol, CELL);
     }
-  }
+
+    /*
+     * Store the new active cell value into the active buffer
+     */
+    TableGetActiveBuf(tablePtr);
+
+    /*
+     * Invalidate the new active cell
+     */
+    TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol, CELL);
+
+    /*
+     * Cache the old active row/col for the next time this is called
+     */
+    tablePtr->oldActRow = tablePtr->activeRow;
+    tablePtr->oldActCol = tablePtr->activeCol;
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TableModifyRC --
- *     Modify rows/cols of the table (insert or delete)
+ * TableAdjustParams --
+ *     Calculate the row and column starts.  Adjusts the topleft corner
+ *     variable to keep it within the screen range, out of the titles
+ *     and keep the screen full make sure the selected cell is in the
+ *     visible area checks to see if the top left cell has changed at
+ *     all and invalidates the table if it has.
  *
  * Results:
  *     None.
  *
- * Side effects:
- *     Modifies associated row/col data
+ * Side Effects:
+ *     Number of rows can change if -rowstretchmode == fill.
+ *     topRow && leftCol can change to fit display.
+ *     activeRow/Col can change to ensure it is a valid cell.
  *
  *----------------------------------------------------------------------
  */
-static int
-TableModifyRC(tablePtr, interp, type, which, argc, argv)
-    Table *tablePtr;           /* Information about text widget. */
-    Tcl_Interp *interp;                /* Current interpreter. */
-    int type;                  /* insert (CMD_INSERT) | delete (CMD_DELETE) */
-    int which;                 /* rows (MOD_ROWS) or cols (MOD_COLS) */
-    int argc;                  /* Number of arguments. */
-    char **argv;               /* Argument strings. */
+void
+TableAdjustParams(register Table *tablePtr)
 {
-  int i, first, lo, hi, idx, c, argsLeft, x, y, offset;
-  int maxrow, maxcol, maxkey, minkey, movetitle, movetag, movedim;
-  size_t length;
-  char *arg;
-  Tcl_HashTable *tagTblPtr, *dimTblPtr;
-  Tcl_HashSearch search;
-  int *dimPtr;
-
-  movetitle    = 1;
-  movetag      = 1;
-  movedim      = 1;
-  maxcol       = tablePtr->cols-1+tablePtr->colOffset;
-  maxrow       = tablePtr->rows-1+tablePtr->rowOffset;
-  for (i = 3; i < argc; i++) {
-    arg = argv[i];
-    if (arg[0] != '-') {
-      break;
-    }
-    length = strlen(arg);
-    if (length < 2) {
-    badSwitch:
-      Tcl_AppendResult(interp, "bad switch \"", arg,
-                      "\": must be -cols, -keeptitles, -holddimensions, ",
-                      "-holdtags, -rows, or --",
-                      (char *) NULL);
-      return TCL_ERROR;
-    }
-    c = arg[1];
-    if ((c == 'h') && (length > 5) &&
-       (strncmp(argv[i], "-holddimensions", length) == 0)) {
-      movedim = 0;
-    } else if ((c == 'h') && (length > 5) &&
-              (strncmp(argv[i], "-holdtags", length) == 0)) {
-      movetag = 0;
-    } else if ((c == 'k') && (strncmp(argv[i], "-keeptitles", length) == 0)) {
-      movetitle = 0;
-    } else if ((c == 'c') && (strncmp(argv[i], "-cols", length) == 0)) {
-      if (i >= (argc-1)) {
-       Tcl_SetResult(interp, "no value given for \"-cols\" option",
-                     TCL_STATIC);
-       return TCL_ERROR;
-      }
-      if (Tcl_GetInt(interp, argv[++i], &maxcol) != TCL_OK) {
-       return TCL_ERROR;
-      }
-      maxcol = MAX(maxcol, tablePtr->titleCols+tablePtr->colOffset);
-    } else if ((c == 'r') && (strncmp(argv[i], "-rows", length) == 0)) {
-      if (i >= (argc-1)) {
-       Tcl_SetResult(interp, "no value given for \"-rows\" option",
-                     TCL_STATIC);
-       return TCL_ERROR;
-      }
-      if (Tcl_GetInt(interp, argv[++i], &maxrow) != TCL_OK) {
-       return TCL_ERROR;
-      }
-      maxrow = MAX(maxrow, tablePtr->titleRows+tablePtr->rowOffset);
-    } else if ((c == '-') && (strncmp(argv[i], "--", length) == 0)) {
-      i++;
-      break;
-    } else {
-      goto badSwitch;
-    }
-  }
-  argsLeft = argc - i;
-  if (argsLeft != 1 && argsLeft != 2) {
-    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                    (type == CMD_DELETE) ? " delete" : " insert",
-                    (which == MOD_COLS) ? " cols" : " rows",
-                    " ?switches? index ?count?\"", (char *) NULL);
-    return TCL_ERROR;
-  }
-
-  c = 1;
-  if (strcmp(argv[i], "end") == 0) {
-    /* allow "end" to be specified as an index */
-    idx = (which == MOD_COLS) ? maxcol : maxrow;
-  } else if (Tcl_GetInt(interp, argv[i], &idx) != TCL_OK) {
-    return TCL_ERROR;
-  }
-  if (argsLeft == 2 && Tcl_GetInt(interp, argv[++i], &c) != TCL_OK) {
-    return TCL_ERROR;
-  }
-  if (tablePtr->state == STATE_DISABLED || c == 0)
-    return TCL_OK;
+    int topRow, leftCol, row, col, total, i, value, x, y, width, height,
+       w, h, hl, px, py, recalc, bd[4],
+       diff, unpreset, lastUnpreset, pad, lastPad, numPixels,
+       defColWidth, defRowHeight;
+    Tcl_HashEntry *entryPtr;
 
-  if (which == MOD_COLS) {
-    maxkey     = maxcol;
-    minkey     = tablePtr->colOffset+tablePtr->titleCols;
-    lo         = tablePtr->rowOffset+(movetitle?0:tablePtr->titleRows);
-    hi         = maxrow;
-    offset     = tablePtr->colOffset;
-    tagTblPtr  = tablePtr->colStyles;
-    dimTblPtr  = tablePtr->colWidths;
-    dimPtr     = &(tablePtr->cols);
-  } else {
-    maxkey     = maxrow;
-    minkey     = tablePtr->rowOffset+tablePtr->titleRows;
-    lo         = tablePtr->colOffset+(movetitle?0:tablePtr->titleCols);
-    hi         = maxcol;
-    offset     = tablePtr->rowOffset;
-    tagTblPtr  = tablePtr->rowStyles;
-    dimTblPtr  = tablePtr->rowHeights;
-    dimPtr     = &(tablePtr->rows);
-  }
-
-  if (type == CMD_DELETE) {
-    /* Handle row/col deletion */
-    first = MAX(MIN(idx,idx+c), minkey);
-    /* (index = i && count = 1) == (index = i && count = -1) */
-    if (c < 0) {
-      /* if the count is negative, make sure that the col count will delete
-       * no greater than the original index */
-      c = idx-first;
-      first++;
-    }
-    if (movedim) {
-      *dimPtr -= c;
-    }
-    for (i = first; i <= maxkey; i++) {
-      TableModifyRCaux(tablePtr, type, which, movetag, tagTblPtr,
-                      dimTblPtr, offset, i, i+c, lo, hi, ((i+c)>maxkey));
-    }
-  } else {
-    /* Handle row/col insertion */
-    first = MAX(MIN(idx, maxkey), minkey);
-    /* +count means insert after index, -count means insert before index */
-    if (c < 0) {
-      c = -c;
+    /*
+     * Cache some values for many upcoming calculations
+     */
+    hl = tablePtr->highlightWidth;
+    w  = Tk_Width(tablePtr->tkwin) - (2 * hl);
+    h  = Tk_Height(tablePtr->tkwin) - (2 * hl);
+    TableGetTagBorders(&(tablePtr->defaultTag),
+           &bd[0], &bd[1], &bd[2], &bd[3]);
+    px = bd[0] + bd[1] + (2 * tablePtr->padX);
+    py = bd[2] + bd[3] + (2 * tablePtr->padY);
+
+    /*
+     * Account for whether default dimensions are in chars (>0) or
+     * pixels (<=0).  Border and Pad space is added in here for convenience.
+     *
+     * When a value in pixels is specified, we take that exact amount,
+     * not adding in padding.
+     */
+    if (tablePtr->defColWidth > 0) {
+       defColWidth = tablePtr->charWidth * tablePtr->defColWidth + px;
     } else {
-      first++;
+       defColWidth = -(tablePtr->defColWidth);
     }
-    if (movedim) {
-      maxkey += c;
-      *dimPtr += c;
+    if (tablePtr->defRowHeight > 0) {
+       defRowHeight = tablePtr->charHeight * tablePtr->defRowHeight + py;
+    } else {
+       defRowHeight = -(tablePtr->defRowHeight);
     }
-    for (i = maxkey; i >= first; i--) {
-      /* move row/col style && width/height here */
-      TableModifyRCaux(tablePtr, type, which, movetag, tagTblPtr,
-                      dimTblPtr, offset, i, i-c, lo, hi, ((i-c)<first));
+
+    /*
+     * Set up the arrays to hold the col pixels and starts.
+     * ckrealloc was fixed in 8.2.1 to handle NULLs, so we can't rely on it.
+     */
+    if (tablePtr->colPixels) ckfree((char *) tablePtr->colPixels);
+    tablePtr->colPixels = (int *) ckalloc(tablePtr->cols * sizeof(int));
+    if (tablePtr->colStarts) ckfree((char *) tablePtr->colStarts);
+    tablePtr->colStarts = (int *) ckalloc((tablePtr->cols+1) * sizeof(int));
+
+    /*
+     * Get all the preset columns and set their widths
+     */
+    lastUnpreset = 0;
+    numPixels = 0;
+    unpreset = 0;
+    for (i = 0; i < tablePtr->cols; i++) {
+       entryPtr = Tcl_FindHashEntry(tablePtr->colWidths, (char *) i);
+       if (entryPtr == NULL) {
+           tablePtr->colPixels[i] = -1;
+           unpreset++;
+           lastUnpreset = i;
+       } else {
+           value = (int) Tcl_GetHashValue(entryPtr);
+           if (value > 0) {
+               tablePtr->colPixels[i] = value * tablePtr->charWidth + px;
+           } else {
+               /*
+                * When a value in pixels is specified, we take that exact
+                * amount, not adding in pad or border values.
+                */
+               tablePtr->colPixels[i] = -value;
+           }
+           numPixels += tablePtr->colPixels[i];
+       }
     }
-  }
-  if (Tcl_FirstHashEntry(tablePtr->selCells, &search) != NULL) {
-    /* clear selection - forceful, but effective */
-    Tcl_DeleteHashTable(tablePtr->selCells);
-    ckfree((char *) (tablePtr->selCells));
-    tablePtr->selCells = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
-    Tcl_InitHashTable(tablePtr->selCells, TCL_STRING_KEYS);
-  }
-
-  TableAdjustParams(tablePtr);
-  if (which == MOD_COLS) {
-    TableCellCoords(tablePtr, 0, first, &x, &y, &offset, &offset);
-    TableInvalidate(tablePtr, x, y,
-                   Tk_Width(tablePtr->tkwin)-x,
-                   Tk_Height(tablePtr->tkwin), 0);
-  } else {
-    TableCellCoords(tablePtr, first, 0, &x, &y, &offset, &offset);
-    TableInvalidate(tablePtr, x, y,
-                   Tk_Width(tablePtr->tkwin),
-                   Tk_Height(tablePtr->tkwin)-y, 0);
-  }
-  return TCL_OK;
-}
 
-/*
- *--------------------------------------------------------------
- *
- * TableWidgetCmd --
- *     This procedure is invoked to process the Tcl command
- *     that corresponds to a widget managed by this module.
- *     See the user documentation for details on what it does.
- *
- * Results:
- *     A standard Tcl result.
- *
- * Side effects:
- *     See the user documentation.
- *
- *--------------------------------------------------------------
- */
-static int
-TableWidgetCmd(clientData, interp, argc, argv)
-     ClientData clientData;            /* Information about listbox widget. */
-     Tcl_Interp *interp;               /* Current interpreter. */
-     int argc;                         /* Number of arguments. */
-     char **argv;                      /* Argument strings. */
-{
-  Tcl_HashEntry *entryPtr;
-  Tcl_HashSearch search;
-  Tcl_HashTable *hashTablePtr;
-  int result, retval, sub_retval, row, col, x, y;
-  int i, width, height, dummy, key, value, posn, offset;
-  char buf1[INDEX_BUFSIZE], buf2[INDEX_BUFSIZE];
-
-  Table *tablePtr = (Table *) clientData;
-
-  if (argc < 2) {
-    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                    " option ?arg arg ...?\"", (char *) NULL);
-    return TCL_ERROR;
-  }
-  Tcl_Preserve(clientData);
-
-  result = TCL_OK;
-  /* parse the first parameter */
-  retval = Cmd_Parse(interp, main_cmds, argv[1]);
-
-  /* Switch on the parameter value */
-  switch (retval) {
-  case 0:
-    /* error, the return string is already set up */
-    result = TCL_ERROR;
-    break;
-
-  case CMD_ACTIVATE:
-    if (argc != 3) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " activate index\"", (char *)NULL);
-      result = TCL_ERROR;
-    } else if (TableGetIndex(tablePtr, argv[2], &row, &col) == TCL_ERROR) {
-      result = TCL_ERROR;
-    } else {
-      /* convert to valid active index in real coords */
-      row -= tablePtr->rowOffset;
-      col -= tablePtr->colOffset;
-      /* we do this regardless, to avoid cell commit problems */
-      if ((tablePtr->flags & HAS_ACTIVE) &&
-         (tablePtr->flags & TEXT_CHANGED)) {
-       tablePtr->flags &= ~TEXT_CHANGED;
-       TableSetCellValue(tablePtr, tablePtr->activeRow+tablePtr->rowOffset,
-                         tablePtr->activeCol+tablePtr->colOffset,
-                         tablePtr->activeBuf);
-      }
-      if (row != tablePtr->activeRow || col != tablePtr->activeCol) {
-       if (tablePtr->flags & HAS_ACTIVE) {
-         TableMakeArrayIndex(tablePtr->activeRow+tablePtr->rowOffset,
-                             tablePtr->activeCol+tablePtr->colOffset, buf1);
-       } else {
-         buf1[0] = '\0';
-       }
-       tablePtr->flags |= HAS_ACTIVE;
-       tablePtr->flags &= ~ACTIVE_DISABLED;
-       tablePtr->activeRow = row;
-       tablePtr->activeCol = col;
-       if (tablePtr->activeLayout) {
-         Tk_FreeTextLayout(tablePtr->activeLayout);
-         tablePtr->activeLayout = NULL;
-       }
-       TableAdjustActive(tablePtr);
-       TableConfigCursor(tablePtr);
-       if (!(tablePtr->flags & BROWSE_CMD) && tablePtr->browseCmd != NULL) {
-         Tcl_DString script;
-         tablePtr->flags |= BROWSE_CMD;
-         row = tablePtr->activeRow+tablePtr->rowOffset;
-         col = tablePtr->activeCol+tablePtr->colOffset;
-         TableMakeArrayIndex(row, col, buf2);
-         Tcl_DStringInit(&script);
-         ExpandPercents(tablePtr, tablePtr->browseCmd, row, col, buf1, buf2,
-                        tablePtr->icursor, &script, CMD_ACTIVATE);
-         result = Tcl_GlobalEval(interp, Tcl_DStringValue(&script));
-         if (result == TCL_OK || result == TCL_RETURN)
-           Tcl_ResetResult(interp);
-         Tcl_DStringFree(&script);
-         tablePtr->flags &= ~BROWSE_CMD;
+    /*
+     * Work out how much to pad each col depending on the mode.
+     */
+    diff  = w - numPixels - (unpreset * defColWidth);
+    total = 0;
+
+    /*
+     * Now do the padding and calculate the column starts.
+     * Diff lower than 0 means we can't see the entire set of columns,
+     * thus no special stretching will occur & we optimize the calculation.
+     */
+    if (diff <= 0) {
+       for (i = 0; i < tablePtr->cols; i++) {
+           if (tablePtr->colPixels[i] == -1) {
+               tablePtr->colPixels[i] = defColWidth;
+           }
+           tablePtr->colStarts[i] = total;
+           total += tablePtr->colPixels[i];
        }
-      } else if ((tablePtr->activeLayout != NULL) &&
-                !(tablePtr->flags & ACTIVE_DISABLED) && argv[2][0] == '@' &&
-                TableCellVCoords(tablePtr, row, col, &x, &y,
-                                 &dummy, &dummy, 0)) {
-       /* we are clicking into the same cell */
-       /* If it was activated with @x,y indexing, find the closest char */
-       char *p;
-
-       /* no error checking because GetIndex did it for us */
-       p = argv[2]+1;
-       x = strtol(p, &p, 0) - x - tablePtr->activeX;
-       y = strtol(++p, &p, 0) - y - tablePtr->activeY;
-
-       tablePtr->icursor = Tk_PointToChar(tablePtr->activeLayout, x, y);
-       TableConfigCursor(tablePtr);
-      }
-      tablePtr->flags |= HAS_ACTIVE;
-    }
-    break;     /* ACTIVATE */
-
-  case CMD_BBOX: {
-    /* bounding box of cell(s) */
-    if (argc < 3 || argc > 4) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                       " bbox first ?last?\"", (char *) NULL);
-      result = TCL_ERROR;
-    } else if (TableGetIndex(tablePtr, argv[2], &row, &col) == TCL_ERROR) {
-      result = TCL_ERROR;
-    } else if (argc == 3) {
-      row -= tablePtr->rowOffset; col -= tablePtr->colOffset;
-      if (TableCellVCoords(tablePtr, row, col, &x, &y, &width, &height, 0)) {
-       sprintf(buf1, "%d %d %d %d", x, y, width, height);
-       Tcl_SetResult(interp, buf1, TCL_VOLATILE);
-      }
-    } else if (TableGetIndex(tablePtr, argv[3], &x, &y) == TCL_ERROR) {
-      result = TCL_ERROR;
     } else {
-      int r1, c1, r2, c2, minX = 99999, minY = 99999, maxX = 0, maxY = 0;
-      row -= tablePtr->rowOffset; col -= tablePtr->colOffset;
-      x -= tablePtr->rowOffset; y -= tablePtr->colOffset;
-      r1 = MIN(row,x); r2 = MAX(row,x);
-      c1 = MIN(col,y); c2 = MAX(col,y);
-      key = 0;
-      for (row = r1; row <= r2; row++) {
-       for (col = c1; col <= c2; col++) {
-         if (TableCellVCoords(tablePtr, row, col, &x, &y,
-                              &width, &height, 0)) {
-           /* Get max bounding box */
-           if (x < minX) minX = x;
-           if (y < minY) minY = y;
-           if (x+width > maxX) maxX = x+width;
-           if (y+height > maxY) maxY = y+height;
-           key++;
-         }
-         /* FIX - This could break on else for speed */
-       }
-      }
-      if (key) {
-       sprintf(buf1, "%d %d %d %d", minX, minY, maxX-minX, maxY-minY);
-       Tcl_SetResult(interp, buf1, TCL_VOLATILE);
-      }
-    }
-  }
-  break;       /* BBOX */
-
-  case CMD_BORDER:
-    if (argc > 6) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " border mark|dragto x y ?r|c?\"", (char *) NULL);
-      result = TCL_ERROR;
-      break;
-    }
-    sub_retval = Cmd_Parse(interp, bd_cmds, argv[2]);
-    if (sub_retval == 0 || Tcl_GetInt(interp, argv[3], &x) != TCL_OK ||
-       Tcl_GetInt(interp, argv[4], &y) != TCL_OK) {
-      result = TCL_ERROR;
-      break;
-    }
-    switch (sub_retval) {
-    case BD_MARK:
-      /* Use x && y to determine if we are over a border */
-      value = TableAtBorder(tablePtr, x, y, &row, &col);
-      /* Cache the row && col for use in DRAGTO */
-      tablePtr->scanMarkRow = row;
-      tablePtr->scanMarkCol = col;
-      if (!value) break;
-      TableCellCoords(tablePtr, row, col, &x, &y, &dummy, &dummy);
-      tablePtr->scanMarkX = x;
-      tablePtr->scanMarkY = y;
-      if (argc == 5 || argv[5][0] == 'r') {
-       if (row < 0)
-         buf1[0] = '\0';
-       else
-         sprintf(buf1, "%d", row+tablePtr->rowOffset);
-       Tcl_AppendElement(interp, buf1);
-      }
-      if (argc == 5 || argv[5][0] == 'c') {
-       if (col < 0)
-         buf1[0] = '\0';
-       else
-         sprintf(buf1, "%d", col+tablePtr->colOffset);
-       Tcl_AppendElement(interp, buf1);
-      }
-      break;   /* BORDER MARK */
-    case BD_DRAGTO:
-      /* check to see if we want to resize any borders */
-      if (tablePtr->resize == SEL_NONE) break;
-      row = tablePtr->scanMarkRow;
-      col = tablePtr->scanMarkCol;
-      TableCellCoords(tablePtr, row, col, &width, &height, &dummy, &dummy);
-      key = 0;
-      if (row >= 0 && (tablePtr->resize & SEL_ROW)) {
-       /* row border was active, move it */
-       /* FIX should this be 1 or 2 bds off? */
-       value = y-height-tablePtr->borderWidth;
-       if (value < -1) value = -1;
-       if (value != tablePtr->scanMarkY) {
-         entryPtr = Tcl_CreateHashEntry(tablePtr->rowHeights,
-                                        (char *) row, &dummy);
-         /* -value means rowHeight will be interp'd as pixels, not lines */
-         Tcl_SetHashValue(entryPtr, (ClientData) MIN(0,-value));
-         tablePtr->scanMarkY = value;
-         key++;
+       switch (tablePtr->colStretch) {
+       case STRETCH_MODE_NONE:
+           pad         = 0;
+           lastPad     = 0;
+           break;
+       case STRETCH_MODE_UNSET:
+           if (unpreset == 0) {
+               pad     = 0;
+               lastPad = 0;
+           } else {
+               pad     = diff / unpreset;
+               lastPad = diff - pad * (unpreset - 1);
+           }
+           break;
+       case STRETCH_MODE_LAST:
+           pad         = 0;
+           lastPad     = diff;
+           lastUnpreset = tablePtr->cols - 1;
+           break;
+       default:        /* STRETCH_MODE_ALL, but also FILL for cols */
+           pad         = diff / tablePtr->cols;
+           /* force it to be applied to the last column too */
+           lastUnpreset = tablePtr->cols - 1;
+           lastPad     = diff - pad * lastUnpreset;
        }
-      }
-      if (col >= 0 && (tablePtr->resize & SEL_COL)) {
-       /* col border was active, move it */
-       value = x-width-tablePtr->borderWidth;
-       if (value < -1) value = -1;
-       if (value != tablePtr->scanMarkX) {
-         entryPtr = Tcl_CreateHashEntry(tablePtr->colWidths,
-                                        (char *) col, &dummy);
-         /* -value means colWidth will be interp'd as pixels, not chars */
-         Tcl_SetHashValue(entryPtr, (ClientData) MIN(0,-value));
-         tablePtr->scanMarkX = value;
-         key++;
+
+       for (i = 0; i < tablePtr->cols; i++) {
+           if (tablePtr->colPixels[i] == -1) {
+               tablePtr->colPixels[i] = defColWidth
+                   + ((i == lastUnpreset) ? lastPad : pad);
+           } else if (tablePtr->colStretch == STRETCH_MODE_ALL) {
+               tablePtr->colPixels[i] += (i == lastUnpreset) ? lastPad : pad;
+           }
+           tablePtr->colStarts[i] = total;
+           total += tablePtr->colPixels[i];
        }
-      }
-      /* Only if something changed do we want to update */
-      if (key) {
-       TableAdjustParams(tablePtr);
-       /* Only rerequest geometry if the basis is the #rows &| #cols */
-       if (tablePtr->maxReqCols || tablePtr->maxReqRows)
-         TableGeometryRequest(tablePtr);
-       TableInvalidateAll(tablePtr, 0);
-      }
-      break;   /* BORDER DRAGTO */
-    }
-    break;     /* BORDER */
-
-  case CMD_CGET:
-    if (argc != 3) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " cget option\"", (char *) NULL);
-      result = TCL_ERROR;
-      break;
-    }
-    result = Tk_ConfigureValue(interp, tablePtr->tkwin, TableConfig,
-                              (char *) tablePtr, argv[2], 0);
-    break;     /* CGET */
-
-  case CMD_CLEAR:
-    if (argc < 3 || argc > 5) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " clear option ?first? ?last?\"", (char *) NULL);
-      result = TCL_ERROR;
-      break;
     }
+    tablePtr->colStarts[i] = tablePtr->maxWidth = total;
 
-    sub_retval = Cmd_Parse(interp, clear_cmds, argv[2]);
-    result = TableClear(tablePtr, sub_retval,
-                       (argc>3)?argv[3]:NULL, (argc>4)?argv[4]:NULL);
-    break;     /* CLEAR */
-
-  case CMD_CONFIGURE:
-    switch (argc) {
-    case 2:
-      result = Tk_ConfigureInfo(interp, tablePtr->tkwin, TableConfig,
-                               (char *) tablePtr, (char *) NULL, 0);
-      break;
-    case 3:
-      result = Tk_ConfigureInfo(interp, tablePtr->tkwin, TableConfig,
-                               (char *) tablePtr, argv[2], 0);
-      break;
-    default:
-      result = TableConfigure(interp, tablePtr, argc - 2, argv + 2,
-                             TK_CONFIG_ARGV_ONLY, 0);
-    }
-    break;     /* CONFIGURE */
-
-  case CMD_CURVALUE:
-    /* Get current active cell buffer */
-    if (argc > 3) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"",
-                       argv[0], " curvalue ?<value>?\"", (char *)NULL);
-      result = TCL_ERROR;
-    } else if (tablePtr->flags & HAS_ACTIVE) {
-      if (argc == 3 && strcmp(argv[2], tablePtr->activeBuf)) {
-       key = TCL_OK;
-       /* validate potential new active buffer contents
-        * only accept if validation returns acceptance. */
-       if (tablePtr->validate &&
-           TableValidateChange(tablePtr,
-                               tablePtr->activeRow+tablePtr->rowOffset,
-                               tablePtr->activeCol+tablePtr->colOffset,
-                               tablePtr->activeBuf,
-                               argv[2], tablePtr->icursor) != TCL_OK) {
-         break;
-       }
-       tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf,
-                                               strlen(argv[2])+1);
-       strcpy(tablePtr->activeBuf, argv[2]);
-       /* mark the text as changed */
-       tablePtr->flags |= TEXT_CHANGED;
-       TableSetActiveIndex(tablePtr);
-       /* check for possible adjustment of icursor */
-       TableGetIcursor(tablePtr, "insert", (int *)0);
-       TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
-                    CELL|INV_FORCE);
-       if (key == TCL_ERROR) {
-         result = TCL_ERROR;
-         break;
+    /*
+     * The 'do' loop is only necessary for rows because of FILL mode
+     */
+    recalc = 0;
+    do {
+       /* Set up the arrays to hold the row pixels and starts */
+       /* FIX - this can be moved outside 'do' if you check >row size */
+       if (tablePtr->rowPixels) ckfree((char *) tablePtr->rowPixels);
+       tablePtr->rowPixels = (int *) ckalloc(tablePtr->rows * sizeof(int));
+
+       /* get all the preset rows and set their heights */
+       lastUnpreset    = 0;
+       numPixels       = 0;
+       unpreset        = 0;
+       for (i = 0; i < tablePtr->rows; i++) {
+           entryPtr = Tcl_FindHashEntry(tablePtr->rowHeights, (char *) i);
+           if (entryPtr == NULL) {
+               tablePtr->rowPixels[i] = -1;
+               unpreset++;
+               lastUnpreset = i;
+           } else {
+               value = (int) Tcl_GetHashValue(entryPtr);
+               if (value > 0) {
+                   tablePtr->rowPixels[i] = value * tablePtr->charHeight + py;
+               } else {
+                   /*
+                    * When a value in pixels is specified, we take that exact
+                    * amount, not adding in pad or border values.
+                    */
+                   tablePtr->rowPixels[i] = -value;
+               }
+               numPixels += tablePtr->rowPixels[i];
+           }
        }
-      }
-      Tcl_AppendResult(interp, tablePtr->activeBuf, (char *)NULL);
-    }
-    break;     /* CURVALUE */
-
-  case CMD_CURSELECTION:
-    if ((argc != 2 && argc != 4) ||
-       (argc == 4 && (argv[2][0] == '\0' ||
-                      strncmp(argv[2], "set", strlen(argv[2]))))) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " curselection ?set <value>?\"", (char *)NULL);
-      result = TCL_ERROR;
-      break;
-    }
-    /* make sure there is a data source to accept set */
-    if (argc == 4 && (tablePtr->state == STATE_DISABLED ||
-                     (tablePtr->dataSource == DATA_NONE)))
-      break;
-    for (entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
-        entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
-      if (argc == 2) {
-       Tcl_AppendElement(interp,
-                         Tcl_GetHashKey(tablePtr->selCells, entryPtr));
-      } else {
-       TableParseArrayIndex(&row, &col,
-                            Tcl_GetHashKey(tablePtr->selCells, entryPtr));
-       TableSetCellValue(tablePtr, row, col, argv[3]);
-       row -= tablePtr->rowOffset;
-       col -= tablePtr->colOffset;
-       if (row == tablePtr->activeRow && col == tablePtr->activeCol) {
-         TableGetActiveBuf(tablePtr);
+
+       /* work out how much to pad each row depending on the mode */
+       diff = h - numPixels - (unpreset * defRowHeight);
+       switch(tablePtr->rowStretch) {
+       case STRETCH_MODE_NONE:
+           pad         = 0;
+           lastPad     = 0;
+           break;
+       case STRETCH_MODE_UNSET:
+           if (unpreset == 0)  {
+               pad     = 0;
+               lastPad = 0;
+           } else {
+               pad     = MAX(0,diff) / unpreset;
+               lastPad = MAX(0,diff) - pad * (unpreset - 1);
+           }
+           break;
+       case STRETCH_MODE_LAST:
+           pad         = 0;
+           lastPad     = MAX(0,diff);
+           /* force it to be applied to the last column too */
+           lastUnpreset = tablePtr->rows - 1;
+           break;
+       case STRETCH_MODE_FILL:
+           pad         = 0;
+           lastPad     = diff;
+           if (diff && !recalc) {
+               tablePtr->rows += (diff/defRowHeight);
+               if (diff < 0 && tablePtr->rows <= 0) {
+                   tablePtr->rows = 1;
+               }
+               lastUnpreset = tablePtr->rows - 1;
+               recalc = 1;
+               continue;
+           } else {
+               lastUnpreset = tablePtr->rows - 1;
+               recalc = 0;
+           }
+           break;
+       default:        /* STRETCH_MODE_ALL */
+           pad         = MAX(0,diff) / tablePtr->rows;
+           /* force it to be applied to the last column too */
+           lastUnpreset = tablePtr->rows - 1;
+           lastPad     = MAX(0,diff) - pad * lastUnpreset;
        }
-       TableCellCoords(tablePtr, row, col, &x, &y, &width, &height);
-       TableInvalidate(tablePtr, x, y, width, height, 0);
-      }
-    }
-    if (argc == 2) {
-      Tcl_SetResult(interp,
-                   TableCellSort(tablePtr, Tcl_GetStringResult(interp)),
-                   TCL_DYNAMIC);
-    }
-    break;     /* CURSELECTION */
-
-  case CMD_DELETE:
-    if (argc < 4) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                       " delete option ?switches? arg ?arg?\"",
-                       (char *) NULL);
-      result = TCL_ERROR;
-      break;
-    }
-    sub_retval = Cmd_Parse (interp, mod_cmds, argv[2]);
-    switch (sub_retval) {
-    case 0:
-      result = TCL_ERROR;
-      break;
-    case MOD_ACTIVE:
-      if (argc > 5) {
-       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                        " delete active first ?last?\"", (char *) NULL);
-       result = TCL_ERROR;
-       break;
-      }
-      if (TableGetIcursor(tablePtr, argv[3], &posn) == TCL_ERROR) {
-       result = TCL_ERROR;
-       break;
-      }
-      if (argc == 4) {
-       value = posn+1;
-      } else if (TableGetIcursor(tablePtr, argv[4], &value) == TCL_ERROR) {
-       result = TCL_ERROR;
-       break;
-      }
-      if (value >= posn && (tablePtr->flags & HAS_ACTIVE) &&
-         !(tablePtr->flags & ACTIVE_DISABLED) &&
-         tablePtr->state == STATE_NORMAL)
-       TableDeleteChars(tablePtr, posn, value-posn);
-      break;   /* DELETE ACTIVE */
-    case MOD_COLS:
-    case MOD_ROWS:
-      result = TableModifyRC(tablePtr, interp, CMD_DELETE, sub_retval,
-                            argc, argv);
-      break;   /* DELETE ROWS */
-    }
-    break;     /* DELETE */
-
-  case CMD_GET: {
-    int r1, c1, r2, c2;
-
-    if (argc < 3 || argc > 4) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " get first ?last?\"", (char *)NULL);
-      result = TCL_ERROR;
-    } else if (TableGetIndex(tablePtr, argv[2], &row, &col) == TCL_ERROR) {
-      result = TCL_ERROR;
-    } else if (argc == 3) {
-      Tcl_SetResult(interp, TableGetCellValue(tablePtr, row, col), TCL_STATIC);
-    } else if (TableGetIndex(tablePtr, argv[3], &r2, &c2) == TCL_ERROR) {
-      result = TCL_ERROR;
-    } else {
-      r1 = MIN(row,r2); r2 = MAX(row,r2);
-      c1 = MIN(col,c2); c2 = MAX(col,c2);
-      for ( row = r1; row <= r2; row++ ) {
-       for ( col = c1; col <= c2; col++ ) {
-         Tcl_AppendElement(interp, TableGetCellValue(tablePtr, row, col));
+    } while (recalc);
+
+    if (tablePtr->rowStarts) ckfree((char *) tablePtr->rowStarts);
+    tablePtr->rowStarts = (int *) ckalloc((tablePtr->rows+1)*sizeof(int));
+    /*
+     * Now do the padding and calculate the row starts
+     */
+    total = 0;
+    for (i = 0; i < tablePtr->rows; i++) {
+       if (tablePtr->rowPixels[i] == -1) {
+           tablePtr->rowPixels[i] = defRowHeight
+               + ((i==lastUnpreset)?lastPad:pad);
+       } else if (tablePtr->rowStretch == STRETCH_MODE_ALL) {
+           tablePtr->rowPixels[i] += (i==lastUnpreset)?lastPad:pad;
        }
-      }
-    }
-  }
-  break;       /* GET */
-
-  case CMD_FLUSH: /* FIX - DEPRECATED */
-    if (argc > 4) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " flush ?first? ?last?\"", (char *) NULL);
-      result = TCL_ERROR;
-    } else {
-      result = TableClear(tablePtr, CLEAR_CACHE,
-                         (argc>2)?argv[2]:NULL, (argc>3)?argv[3]:NULL);
-    }
-    break;     /* FLUSH */
-
-  case CMD_HEIGHT:
-  case CMD_WIDTH:
-    /* changes the width/height of certain selected columns */
-    if (argc != 3 && (argc & 1)) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      (retval == CMD_WIDTH) ?
-                      " width ?col? ?width col width ...?\"" :
-                      " height ?row? ?height row height ...?\"",
-                      (char *) NULL);
-      result = TCL_ERROR;
-      break;
+       /* calculate the start of each row */
+       tablePtr->rowStarts[i] = total;
+       total += tablePtr->rowPixels[i];
     }
-    if (retval == CMD_WIDTH) {
-      hashTablePtr = tablePtr->colWidths;
-      offset = tablePtr->colOffset;
-    } else { 
-      hashTablePtr = tablePtr->rowHeights;
-      offset = tablePtr->rowOffset;
+    tablePtr->rowStarts[i] = tablePtr->maxHeight = total;
+
+    /*
+     * Make sure the top row and col have reasonable real indices
+     */
+    CONSTRAIN(tablePtr->topRow, tablePtr->titleRows, tablePtr->rows-1);
+    CONSTRAIN(tablePtr->leftCol, tablePtr->titleCols, tablePtr->cols-1);
+
+    /*
+     * If we don't have the info, don't bother to fix up the other parameters
+     */
+    if (Tk_WindowId(tablePtr->tkwin) == None) {
+       tablePtr->oldTopRow = tablePtr->oldLeftCol = -1;
+       return;
     }
 
-    if (argc == 2) {
-      /* print out all the preset column widths or row heights */
-      entryPtr = Tcl_FirstHashEntry(hashTablePtr, &search);
-      while (entryPtr != NULL) {
-       posn = ((int) Tcl_GetHashKey(hashTablePtr, entryPtr)) + offset;
-       value = (int) Tcl_GetHashValue(entryPtr);
-       sprintf(buf1, "%d %d", posn, value);
-       Tcl_AppendElement(interp, buf1);
-       entryPtr = Tcl_NextHashEntry(&search);
-      }
-    } else if (argc == 3) {
-      /* get the width/height of a particular row/col */
-      if (Tcl_GetInt(interp, argv[2], &posn) != TCL_OK) {
-       result = TCL_ERROR;
-       break;
-      }
-      /* no range check is done, why bother? */
-      posn -= offset;
-      entryPtr = Tcl_FindHashEntry(hashTablePtr, (char *) posn);
-      if (entryPtr != NULL) {
-       sprintf(buf1, "%d", (int) Tcl_GetHashValue(entryPtr));
-       Tcl_SetResult(interp, buf1, TCL_VOLATILE);
-      } else {
-       sprintf(buf1, "%d", (retval == CMD_WIDTH) ?
-               tablePtr->defColWidth : tablePtr->defRowHeight);
-       Tcl_SetResult(interp, buf1, TCL_VOLATILE);
-      }
-    } else {
-      for (i=2; i<argc; i++) {
-       /* set new width|height here */
-       value = -999999;
-       if (Tcl_GetInt(interp, argv[i++], &posn) != TCL_OK ||
-           (strncmp(argv[i], "default", strlen(argv[i])) &&
-            Tcl_GetInt(interp, argv[i], &value) != TCL_OK)) {
-         result = TCL_ERROR;
-         break;
-       }
-       posn -= offset;
-       if (value == -999999) {
-         /* reset that field */
-         if ((entryPtr = Tcl_FindHashEntry(hashTablePtr, (char *) posn)))
-           Tcl_DeleteHashEntry(entryPtr);
-       } else {
-         entryPtr = Tcl_CreateHashEntry(hashTablePtr, (char *) posn, &dummy);
-         Tcl_SetHashValue(entryPtr, (ClientData) value);
+    topRow  = tablePtr->topRow;
+    leftCol = tablePtr->leftCol;
+    w += hl;
+    h += hl;
+    /* 
+     * If we use this value of topRow, will we fill the window?
+     * if not, decrease it until we will, or until it gets to titleRows 
+     * make sure we don't cut off the bottom row
+     */
+    for (; topRow > tablePtr->titleRows; topRow--) {
+       if ((tablePtr->maxHeight-(tablePtr->rowStarts[topRow-1] -
+               tablePtr->rowStarts[tablePtr->titleRows])) > h) {
+           break;
        }
-      }
-      TableAdjustParams(tablePtr);
-      /* rerequest geometry */
-      TableGeometryRequest(tablePtr);
-      /*
-       * Invalidate the whole window as TableAdjustParams
-       * will only check to see if the top left cell has moved
-       * FIX: should just move from lowest order visible cell to edge of window
-       */
-      TableInvalidateAll(tablePtr, 0);
-    }
-    break;     /* HEIGHT && WIDTH */
-
-  case CMD_ICURSOR:
-    /* set the insertion cursor */
-    if (!(tablePtr->flags & HAS_ACTIVE) ||
-       (tablePtr->flags & ACTIVE_DISABLED) ||
-        tablePtr->state == STATE_DISABLED)
-      break;
-    switch (argc) {
-    case 2:
-      sprintf(buf1, "%d", tablePtr->icursor);
-      Tcl_SetResult(interp, buf1, TCL_VOLATILE);
-      break;
-    case 3:
-      if (TableGetIcursor(tablePtr, argv[2], (int *)0) != TCL_OK) {
-       result = TCL_ERROR;
-       break;
-      }
-      TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol, CELL);
-      break;
-    default:
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " icursor arg\"", (char *) NULL);
-      result = TCL_ERROR;
-      break;
-    }
-    break;     /* ICURSOR */
-
-  case CMD_INDEX:
-    if (argc < 3 || argc > 4 ||
-       TableGetIndex(tablePtr, argv[2], &row, &col) == TCL_ERROR ||
-       (argc == 4 && (strcmp(argv[3], "row") && strcmp(argv[3], "col")))) {
-      if (!strlen(Tcl_GetStringResult(interp))) {
-       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                        " index index ?row|col?\"", (char *)NULL);
-      }
-      result = TCL_ERROR;
-      break;
-    }
-    if (argc == 3) {
-      TableMakeArrayIndex(row, col, buf1);
-    } else if (argv[3][0] == 'r') { /* INDEX row */
-      sprintf(buf1, "%d", row);
-    } else {   /* INDEX col */
-      sprintf(buf1, "%d", col);
-    }
-    Tcl_SetResult(interp, buf1, TCL_VOLATILE);
-    break;     /* INDEX */
-
-  case CMD_INSERT:
-    /* are edits enabled */
-    if (argc < 4) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " insert option ?switches? arg ?arg?\"", (char *)NULL);
-      result = TCL_ERROR;
-      break;
-    }
-    sub_retval = Cmd_Parse(interp, mod_cmds, argv[2]);
-    switch (sub_retval) {
-    case 0:
-      result = TCL_ERROR;
-      break;
-    case MOD_ACTIVE:
-      if (argc != 5) {
-       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                        " insert active index string\"", (char *)NULL);
-       result = TCL_ERROR;
-      } else if (TableGetIcursor(tablePtr, argv[3], &posn) != TCL_OK) {
-       result = TCL_ERROR;
-      } else if ((tablePtr->flags & HAS_ACTIVE) &&
-                !(tablePtr->flags & ACTIVE_DISABLED) &&
-                tablePtr->state == STATE_NORMAL) {
-       TableInsertChars(tablePtr, posn, argv[4]);
-      }
-      break;   /* INSERT ACTIVE */
-    case MOD_COLS:
-    case MOD_ROWS:
-      result = TableModifyRC(tablePtr, interp, CMD_INSERT, sub_retval,
-                            argc, argv);
-      break;
-    }
-    break;     /* INSERT */
-
-  case CMD_REREAD:
-    /* this rereads the selection from the array */
-    if (!(tablePtr->flags & HAS_ACTIVE) ||
-       (tablePtr->flags & ACTIVE_DISABLED) ||
-       tablePtr->state == STATE_DISABLED)
-      break;
-    TableGetActiveBuf(tablePtr);
-    TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
-                CELL|INV_FORCE);
-    break;     /* REREAD */
-
-  case CMD_SCAN:
-    if (argc != 5) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"",
-                      argv[0], " scan mark|dragto x y\"", (char *) NULL);
-      result = TCL_ERROR;
-      break;
-    } else if (Tcl_GetInt(interp, argv[3], &x) == TCL_ERROR ||
-              Tcl_GetInt(interp, argv[4], &y) == TCL_ERROR) {
-      result = TCL_ERROR;
-      break;
-    }
-    if ((argv[2][0] == 'm')
-       && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
-      TableWhatCell(tablePtr, x, y, &row, &col);
-      tablePtr->scanMarkRow = row-tablePtr->topRow;
-      tablePtr->scanMarkCol = col-tablePtr->leftCol;
-      tablePtr->scanMarkX = x;
-      tablePtr->scanMarkY = y;
-    } else if ((argv[2][0] == 'd')
-              && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
-      int oldTop = tablePtr->topRow, oldLeft = tablePtr->leftCol;
-      y += (5*(y-tablePtr->scanMarkY));
-      x += (5*(x-tablePtr->scanMarkX));
-
-      TableWhatCell(tablePtr, x, y, &row, &col);
-
-      /* maintain appropriate real index */
-      tablePtr->topRow  = MAX(MIN(row-tablePtr->scanMarkRow,
-                                 tablePtr->rows-1), tablePtr->titleRows);
-      tablePtr->leftCol = MAX(MIN(col-tablePtr->scanMarkCol,
-                                 tablePtr->cols-1), tablePtr->titleCols);
-
-      /* Adjust the table if new top left */
-      if (oldTop != tablePtr->topRow || oldLeft != tablePtr->leftCol)
-       TableAdjustParams(tablePtr);
-    } else {
-      Tcl_AppendResult(interp, "bad scan option \"", argv[2],
-                      "\": must be mark or dragto", (char *) NULL);
-      result = TCL_ERROR;
-      break;
-    }
-    break;     /* SCAN */
-
-  case CMD_SEE:
-    if (argc!=3 || TableGetIndex(tablePtr,argv[2],&row,&col)==TCL_ERROR) {
-      if (!strlen(Tcl_GetStringResult(interp))) {
-       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                        " see index\"", (char *)NULL);
-      }
-      result = TCL_ERROR;
-      break;
     }
-    /* Adjust from user to master coords */
-    row -= tablePtr->rowOffset;
-    col -= tablePtr->colOffset;
-    if (!TableCellVCoords(tablePtr, row, col, &x, &x, &x, &x, 1)) {
-      tablePtr->topRow  = row-1;
-      tablePtr->leftCol = col-1;
-      TableAdjustParams(tablePtr);
-    }
-    break;     /* SEE */
-
-  case CMD_SELECTION:
-    if (argc<3) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " selection option args\"", (char *)NULL);
-      result=TCL_ERROR;
-      break;
-    }
-    retval = Cmd_Parse(interp, sel_cmds, argv[2]);
-    switch(retval) {
-    case 0:            /* failed to parse the argument, error */
-      return TCL_ERROR;
-    case SEL_ANCHOR:
-      if (argc != 4 || TableGetIndex(tablePtr,argv[3],&row,&col) != TCL_OK) {
-       if (!strlen(Tcl_GetStringResult(interp)))
-         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                          " selection anchor index\"", (char *)NULL);
-       result=TCL_ERROR;
-       break;
-      }
-      tablePtr->flags |= HAS_ANCHOR;
-      /* maintain appropriate real index */
-      if (tablePtr->selectTitles) {
-       tablePtr->anchorRow = MIN(MAX(0,row-tablePtr->rowOffset),
-                                 tablePtr->rows-1);
-       tablePtr->anchorCol = MIN(MAX(0,col-tablePtr->colOffset),
-                                 tablePtr->cols-1);
-      } else {
-       tablePtr->anchorRow = MIN(MAX(tablePtr->titleRows,
-                                     row-tablePtr->rowOffset),
-                                 tablePtr->rows-1);
-       tablePtr->anchorCol = MIN(MAX(tablePtr->titleCols,
-                                     col-tablePtr->colOffset),
-                                 tablePtr->cols-1);
-      }
-      break;
-    case SEL_CLEAR:
-      if ( argc != 4 && argc != 5 ) {
-       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                        " selection clear all|<first> ?<last>?\"",
-                        (char *)NULL);
-       result=TCL_ERROR;
-       break;
-      }
-      if (strcmp(argv[3],"all") == 0) {
-       for(entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
-           entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
-         TableParseArrayIndex(&row, &col,
-                              Tcl_GetHashKey(tablePtr->selCells,entryPtr));
-         Tcl_DeleteHashEntry(entryPtr);
-         TableCellCoords(tablePtr, row-tablePtr->rowOffset,
-                         col-tablePtr->colOffset, &x, &y, &width, &height);
-         TableInvalidate(tablePtr, x, y, width, height, 0);
-       }
-      } else {
-       int clo=0,chi=0,r1,c1,r2,c2;
-       if (TableGetIndex(tablePtr,argv[3],&row,&col) == TCL_ERROR ||
-           (argc==5 && TableGetIndex(tablePtr,argv[4],&r2,&c2)==TCL_ERROR)) {
-         result = TCL_ERROR;
-         break;
-       }
-       key = 0;
-       if (argc == 4) {
-         r1 = r2 = row;
-         c1 = c2 = col;
-       } else {
-         r1 = MIN(row,r2); r2 = MAX(row,r2);
-         c1 = MIN(col,c2); c2 = MAX(col,c2);
-       }
-       switch (tablePtr->selectType) {
-       case SEL_BOTH:
-         clo = c1; chi = c2;
-         c1 = tablePtr->colOffset;
-         c2 = tablePtr->cols-1+c1;
-         key = 1;
-         goto CLEAR_CELLS;
-       CLEAR_BOTH:
-         key = 0;
-         c1 = clo; c2 = chi;
-       case SEL_COL:
-         r1 = tablePtr->rowOffset;
-         r2 = tablePtr->rows-1+r1;
-         break;
-       case SEL_ROW:
-         c1 = tablePtr->colOffset;
-         c2 = tablePtr->cols-1+c1;
-         break;
+    /* 
+     * If we use this value of topCol, will we fill the window?
+     * if not, decrease it until we will, or until it gets to titleCols 
+     * make sure we don't cut off the left column
+     */
+    for (; leftCol > tablePtr->titleCols; leftCol--) {
+       if ((tablePtr->maxWidth-(tablePtr->colStarts[leftCol-1] -
+               tablePtr->colStarts[tablePtr->titleCols])) > w) {
+           break;
        }
-       /* row/col are in user index coords */
-      CLEAR_CELLS:
-       for ( row = r1; row <= r2; row++ ) {
-         for ( col = c1; col <= c2; col++ ) {
-           TableMakeArrayIndex(row, col, buf1);
-           if ((entryPtr=Tcl_FindHashEntry(tablePtr->selCells, buf1))!=NULL) {
-             Tcl_DeleteHashEntry(entryPtr);
-             TableCellCoords(tablePtr, row-tablePtr->rowOffset,
-                             col-tablePtr->colOffset,&x,&y,&width,&height);
-             TableInvalidate(tablePtr, x, y, width, height, 0);
+    }
+
+    tablePtr->topRow  = topRow;
+    tablePtr->leftCol = leftCol;
+
+    /*
+     * Now work out where the bottom right is for scrollbar update and to test
+     * for one last stretch.  Avoid the confusion that spans could cause for
+     * determining the last cell dimensions.
+     */
+    tablePtr->flags |= AVOID_SPANS;
+    TableGetLastCell(tablePtr, &row, &col);
+    TableCellVCoords(tablePtr, row, col, &x, &y, &width, &height, 0);
+    tablePtr->flags &= ~AVOID_SPANS;
+
+    /*
+     * Do we have scrollbars, if so, calculate and call the TCL functions In
+     * order to get the scrollbar to be completely full when the whole screen
+     * is shown and there are titles, we have to arrange for the scrollbar
+     * range to be 0 -> rows-titleRows etc.  This leads to the position
+     * setting methods, toprow and leftcol, being relative to the titles, not
+     * absolute row and column numbers.
+     */
+    if (tablePtr->yScrollCmd != NULL || tablePtr->xScrollCmd != NULL) {
+       Tcl_Interp *interp = tablePtr->interp;
+       char buf[INDEX_BUFSIZE];
+       double first, last;
+
+       /*
+        * We must hold onto the interpreter because the data referred to at
+        * tablePtr might be freed as a result of the call to Tcl_VarEval.
+        */
+       Tcl_Preserve((ClientData) interp);
+
+       /* Do we have a Y-scrollbar and rows to scroll? */
+       if (tablePtr->yScrollCmd != NULL) {
+           if (row < tablePtr->titleRows) {
+               first = 0;
+               last  = 1;
+           } else {
+               diff = tablePtr->rowStarts[tablePtr->titleRows];
+               last = (double) (tablePtr->rowStarts[tablePtr->rows]-diff);
+               if (last <= 0.0) {
+                   first = 0;
+                   last  = 1;
+               } else {
+                   first = (tablePtr->rowStarts[topRow]-diff) / last;
+                   last  = (height+tablePtr->rowStarts[row]-diff) / last;
+               }
+           }
+           sprintf(buf, " %g %g", first, last);
+           if (Tcl_VarEval(interp, tablePtr->yScrollCmd,
+                   buf, (char *)NULL) != TCL_OK) {
+               Tcl_AddErrorInfo(interp,
+                       "\n\t(vertical scrolling command executed by table)");
+               Tcl_BackgroundError(interp);
            }
-         }
-       }
-       if (key) goto CLEAR_BOTH;
-      }
-      break;   /* SELECTION CLEAR */
-    case SEL_INCLUDES:
-      if (argc != 4) {
-       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                        " selection includes index\"", (char *)NULL);
-       result = TCL_ERROR;
-      } else if (TableGetIndex(tablePtr, argv[3], &row, &col) == TCL_ERROR) {
-       result = TCL_ERROR;
-      } else {
-       TableMakeArrayIndex(row, col, buf1);
-       if (Tcl_FindHashEntry(tablePtr->selCells, buf1)) {
-         Tcl_SetResult(interp, "1", TCL_STATIC);
-       } else {
-         Tcl_SetResult(interp, "0", TCL_STATIC);
        }
-      }
-      break;   /* SELECTION INCLUDES */
-    case SEL_SET: {
-      int clo=0, chi=0, r1, c1, r2, c2, titleRows, titleCols;
-      if (argc < 4 || argc > 5) {
-       Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                        " selection set first ?last?\"", (char *)NULL);
-       result = TCL_ERROR;
-       break;
-      }
-      if (TableGetIndex(tablePtr,argv[3],&row,&col) == TCL_ERROR ||
-         (argc==5 && TableGetIndex(tablePtr,argv[4],&r2,&c2)==TCL_ERROR)) {
-       result = TCL_ERROR;
-       break;
-      }
-      key = 0;
-      if (tablePtr->selectTitles) {
-       titleRows = 0;
-       titleCols = 0;
-      } else {
-       titleRows = tablePtr->titleRows;
-       titleCols = tablePtr->titleCols;
-      }
-      /* maintain appropriate user index */
-      row = MIN(MAX(titleRows+tablePtr->rowOffset, row),
-               tablePtr->rows-1+tablePtr->rowOffset);
-      col = MIN(MAX(titleCols+tablePtr->colOffset, col),
-               tablePtr->cols-1+tablePtr->colOffset);
-      if (argc == 4) {
-       r1 = r2 = row;
-       c1 = c2 = col;
-      } else {
-       r2 = MIN(MAX(titleRows+tablePtr->rowOffset, r2),
-                tablePtr->rows-1+tablePtr->rowOffset);
-       c2 = MIN(MAX(titleCols+tablePtr->colOffset, c2),
-                tablePtr->cols-1+tablePtr->colOffset);
-       r1 = MIN(row,r2); r2 = MAX(row,r2);
-       c1 = MIN(col,c2); c2 = MAX(col,c2);
-      }
-      switch (tablePtr->selectType) {
-      case SEL_BOTH:
-       clo = c1; chi = c2;
-       c1 = titleCols+tablePtr->colOffset;
-       c2 = tablePtr->cols-1+tablePtr->colOffset;
-       key = 1;
-       goto SET_CELLS;
-      SET_BOTH:
-       key = 0;
-       c1 = clo; c2 = chi;
-      case SEL_COL:
-       r1 = titleRows+tablePtr->rowOffset;
-       r2 = tablePtr->rows-1+tablePtr->rowOffset;
-       break;
-      case SEL_ROW:
-       c1 = titleCols+tablePtr->colOffset;
-       c2 = tablePtr->cols-1+tablePtr->colOffset;
-       break;
-      }
-    SET_CELLS:
-      entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
-      for ( row = r1; row <= r2; row++ ) {
-       for ( col = c1; col <= c2; col++ ) {
-         TableMakeArrayIndex(row, col, buf1);
-         if (Tcl_FindHashEntry(tablePtr->selCells, buf1) == NULL) {
-           Tcl_CreateHashEntry(tablePtr->selCells, buf1, &dummy);
-           TableCellCoords(tablePtr, row-tablePtr->rowOffset,
-                           col-tablePtr->colOffset, &x, &y, &width, &height);
-           TableInvalidate(tablePtr, x, y, width, height, 0);
-         }
+       /* Do we have a X-scrollbar and cols to scroll? */
+       if (tablePtr->xScrollCmd != NULL) {
+           if (col < tablePtr->titleCols) {
+               first = 0;
+               last  = 1;
+           } else {
+               diff = tablePtr->colStarts[tablePtr->titleCols];
+               last = (double) (tablePtr->colStarts[tablePtr->cols]-diff);
+               if (last <= 0.0) {
+                   first = 0;
+                   last  = 1;
+               } else {
+                   first = (tablePtr->colStarts[leftCol]-diff) / last;
+                   last  = (width+tablePtr->colStarts[col]-diff) / last;
+               }
+           }
+           sprintf(buf, " %g %g", first, last);
+           if (Tcl_VarEval(interp, tablePtr->xScrollCmd,
+                   buf, (char *)NULL) != TCL_OK) {
+               Tcl_AddErrorInfo(interp,
+                       "\n\t(horizontal scrolling command executed by table)");
+               Tcl_BackgroundError(interp);
+           }
        }
-      }
-      if (key) goto SET_BOTH;
-
-      /* Adjust the table for top left, selection on screen etc */
-      TableAdjustParams(tablePtr);
 
-      /* If the table was previously empty and we want to export the
-       * selection, we should grab it now */
-      if (entryPtr==NULL && tablePtr->exportSelection) {
-       Tk_OwnSelection(tablePtr->tkwin, XA_PRIMARY, TableLostSelection,
-                       (ClientData) tablePtr);
-      }
-    }
-    break;     /* SELECTION SET */
+       Tcl_Release((ClientData) interp);
     }
-    break;     /* SELECTION */
-
-  case CMD_SET:
-    /* sets any number of tags/indices to a given value */
-    if (argc < 3 || (argc > 3 && (argc & 1))) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " set index ?value? ?index value ...?\"",
-                      (char *) NULL);
-      result = TCL_ERROR;
-      break;
-    }
-    /* make sure there is a data source to accept set */
-    if (tablePtr->dataSource == DATA_NONE)
-      break;
-    if (argc == 3) {
-      if (TableGetIndex(tablePtr, argv[2], &row, &col) != TCL_OK) {
-       result = TCL_ERROR;
-       break;
-      }
-      Tcl_SetResult(interp, TableGetCellValue(tablePtr, row, col),
-                   TCL_STATIC);
-    } else if (tablePtr->state == STATE_NORMAL) {
-      for (i=2; i<argc; i++) {
-       if (TableGetIndex(tablePtr, argv[i], &row, &col) != TCL_OK) {
-         result = TCL_ERROR;
-         break;
-       }
-       if (TableSetCellValue(tablePtr, row, col, argv[++i]) == TCL_ERROR) {
-         result = TCL_ERROR;
-         break;
-       }
-       row -= tablePtr->rowOffset;
-       col -= tablePtr->colOffset;
-       if (row == tablePtr->activeRow && col == tablePtr->activeCol) {
-         TableGetActiveBuf(tablePtr);
+
+    /*
+     * Adjust the last row/col to fill empty space if it is visible.
+     * Do this after setting the scrollbars to not upset its calculations.
+     */
+    if (row == tablePtr->rows-1 && tablePtr->rowStretch != STRETCH_MODE_NONE) {
+       diff = h-(y+height);
+       if (diff > 0) {
+           tablePtr->rowPixels[tablePtr->rows-1] += diff;
+           tablePtr->rowStarts[tablePtr->rows] += diff;
        }
-       TableCellCoords(tablePtr, row, col, &x, &y, &width, &height);
-       TableInvalidate(tablePtr, x, y, width, height, 0);
-      }
     }
-    break;
-
-  case CMD_TAG:
-    /* a veritable plethora of tag commands */
-    /* do we have another argument */
-    if (argc < 3) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " tag option ?arg arg ...?\"", (char *) NULL);
-      result = TCL_ERROR;
-      break;
-    }
-    /* all the rest is now done in a separate function */
-    result = TableTagCmd(tablePtr, interp, argc, argv);
-    break;     /* TAG */
-
-  case CMD_VALIDATE:
-    if (argc != 3) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " validate index\"", (char *) NULL);
-      result = TCL_ERROR;
-    } else if (TableGetIndex(tablePtr, argv[2], &row, &col) == TCL_ERROR) {
-      result = TCL_ERROR;
-    } else {
-      value = tablePtr->validate;
-      tablePtr->validate = 1;
-      key = TableValidateChange(tablePtr, row, col, (char *) NULL,
-                               (char *) NULL, -1);
-      tablePtr->validate = value;
-      sprintf(buf1, "%d", (key == TCL_OK) ? 1 : 0);
-      Tcl_SetResult(interp, buf1, TCL_VOLATILE);
+    if (col == tablePtr->cols-1 && tablePtr->colStretch != STRETCH_MODE_NONE) {
+       diff = w-(x+width);
+       if (diff > 0) {
+           tablePtr->colPixels[tablePtr->cols-1] += diff;
+           tablePtr->colStarts[tablePtr->cols] += diff;
+       }
     }
-    break;
 
-  case CMD_VERSION:
-    if (argc != 2) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " version\"", (char *) NULL);
-      result = TCL_ERROR;
-    } else {
-      Tcl_SetResult(interp, TBL_VERSION, TCL_VOLATILE);
-    }
-    break;
-
-  case CMD_WINDOW:
-    /* a veritable plethora of window commands */
-    /* do we have another argument */
-    if (argc < 3) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " window option ?arg arg ...?\"", (char *) NULL);
-      result = TCL_ERROR;
-      break;
-    }
-    /* all the rest is now done in a separate function */
-    result = TableWindowCmd(tablePtr, interp, argc, argv);
-    break;
-
-  case CMD_XVIEW:
-  case CMD_YVIEW:
-    if (argc == 2) {
-      int diff;
-      double first, last;
-      TableGetLastCell(tablePtr, &row, &col);
-      TableCellVCoords(tablePtr, row, col, &x, &y, &width, &height, 0);
-      if (retval == CMD_YVIEW) {
-       if (row < tablePtr->titleRows) {
-         first = 0;
-         last  = 1;
-       } else {
-         diff = tablePtr->rowStarts[tablePtr->titleRows];
-         last = (double) (tablePtr->rowStarts[tablePtr->rows]-diff);
-         first = (tablePtr->rowStarts[tablePtr->topRow]-diff) / last;
-         last  = (height+tablePtr->rowStarts[row]-diff) / last;
-       }
-      } else {
-       if (col < tablePtr->titleCols) {
-         first = 0;
-         last  = 1;
-       } else {
-         diff = tablePtr->colStarts[tablePtr->titleCols];
-         last = (double) (tablePtr->colStarts[tablePtr->cols]-diff);
-         first = (tablePtr->colStarts[tablePtr->leftCol]-diff) / last;
-         last  = (width+tablePtr->colStarts[col]-diff) / last;
-       }
-      }
-      sprintf(buf1, "%g %g", first, last);
-      Tcl_SetResult(interp, buf1, TCL_VOLATILE);
-    } else {
-      /* cache old topleft to see if it changes */
-      int oldTop = tablePtr->topRow, oldLeft = tablePtr->leftCol;
-      if (argc == 3) {
-       if (Tcl_GetInt(interp, argv[2], &value) != TCL_OK) {
-         result = TCL_ERROR;
-         break;
-       }
-       if (retval == CMD_YVIEW) {
-         tablePtr->topRow  = value + tablePtr->titleRows;
-       } else {
-         tablePtr->leftCol = value + tablePtr->titleCols;
-       }
-      } else {
-       double frac;
-       sub_retval = Tk_GetScrollInfo(interp, argc, argv, &frac, &value);
-       switch (sub_retval) {
-       case TK_SCROLL_ERROR:
-         result = TCL_ERROR;
-         break;
-       case TK_SCROLL_MOVETO:
-         if (frac < 0) frac = 0;
-         if (retval == CMD_YVIEW) {
-           tablePtr->topRow = (int)(frac*tablePtr->rows)+tablePtr->titleRows;
-         } else {
-           tablePtr->leftCol = (int)(frac*tablePtr->cols)+tablePtr->titleCols;
-         }
-         break;
-       case TK_SCROLL_PAGES:
-         TableGetLastCell(tablePtr, &row, &col);
-         if (retval == CMD_YVIEW) {
-           tablePtr->topRow  += value * (row-tablePtr->topRow+1);
-         } else {
-           tablePtr->leftCol += value * (col-tablePtr->leftCol+1);
-         }
-         break;
-       case TK_SCROLL_UNITS:
-         if (retval == CMD_YVIEW) {
-           tablePtr->topRow  += value;
-         } else {
-           tablePtr->leftCol += value;
-         }
-         break;
-       }
-      }
-      /* maintain appropriate real index */
-      tablePtr->topRow  = MAX(tablePtr->titleRows,
-                             MIN(tablePtr->topRow, tablePtr->rows-1));
-      tablePtr->leftCol = MAX(tablePtr->titleCols,
-                             MIN(tablePtr->leftCol, tablePtr->cols-1));
-      /* Do the table adjustment if topRow || leftCol changed */       
-      if (oldTop != tablePtr->topRow || oldLeft != tablePtr->leftCol)
-       TableAdjustParams(tablePtr);
+    TableAdjustActive(tablePtr);
+
+    /*
+     * now check the new value of topleft cell against the originals,
+     * If they changed, invalidate the area, else leave it alone
+     */
+    if (tablePtr->topRow != tablePtr->oldTopRow ||
+       tablePtr->leftCol != tablePtr->oldLeftCol) {
+       /* set the old top row/col for the next time this function is called */
+       tablePtr->oldTopRow = tablePtr->topRow;
+       tablePtr->oldLeftCol = tablePtr->leftCol;
+       /* only the upper corner title cells wouldn't change */
+       TableInvalidateAll(tablePtr, 0);
     }
-    break; /* XVIEW/YVIEW */
-  }
-  Tcl_Release(clientData);
-  return result;
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TableDestroy --
- *     This procedure is invoked by Tcl_EventuallyFree
- *     to clean up the internal structure of a table at a safe time
- *     (when no-one is using it anymore).
+ * TableCursorEvent --
+ *     Toggle the cursor status.  Equivalent to EntryBlinkProc.
  *
  * Results:
  *     None.
  *
  * Side effects:
- *     Everything associated with the table is freed up (hopefully).
+ *     The cursor will be switched off/on.
  *
  *----------------------------------------------------------------------
  */
 static void
-TableDestroy(ClientData clientdata)
+TableCursorEvent(ClientData clientData)
 {
-  register Table *tablePtr = (Table *) clientdata;
-  Tcl_HashEntry *entryPtr;
-  Tcl_HashSearch search;
-
-  /* These may be repetitive from DestroyNotify, but it doesn't hurt */
-  /* cancel any pending update or timer */
-  if (tablePtr->flags & REDRAW_PENDING) {
-    Tcl_CancelIdleCall(TableDisplay, (ClientData) tablePtr);
-    tablePtr->flags &= ~REDRAW_PENDING;
-  }
-  Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
-  Tcl_DeleteTimerHandler(tablePtr->flashTimer);
-
-  /* delete the variable trace */
-  if (tablePtr->arrayVar != NULL) {
-    Tcl_UntraceVar(tablePtr->interp, tablePtr->arrayVar,
-                  TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY,
-                  (Tcl_VarTraceProc *)TableVarProc, (ClientData) tablePtr);
-  }
-
-  /* delete cached activeLayout */
-  if (tablePtr->activeLayout != NULL) {
-    Tk_FreeTextLayout(tablePtr->activeLayout);
-    tablePtr->activeLayout = NULL;
-  }
-  /* free the arrays with row/column pixel sizes */
-  if (tablePtr->colPixels) ckfree((char *) tablePtr->colPixels);
-  if (tablePtr->rowPixels) ckfree((char *) tablePtr->rowPixels);
-  if (tablePtr->colStarts) ckfree((char *) tablePtr->colStarts);
-  if (tablePtr->rowStarts) ckfree((char *) tablePtr->rowStarts);
-
-  /* free the selection buffer */
-  if (tablePtr->activeBuf != NULL) ckfree(tablePtr->activeBuf);
-
-  /* delete the cache, row, column and cell style hash tables */
-  Tcl_DeleteHashTable(tablePtr->cache);
-  ckfree((char *) (tablePtr->cache));
-  Tcl_DeleteHashTable(tablePtr->rowStyles);
-  ckfree((char *) (tablePtr->rowStyles));
-  Tcl_DeleteHashTable(tablePtr->colStyles);
-  ckfree((char *) (tablePtr->colStyles));
-  Tcl_DeleteHashTable(tablePtr->cellStyles);
-  ckfree((char *) (tablePtr->cellStyles));
-  Tcl_DeleteHashTable(tablePtr->flashCells);
-  ckfree((char *) (tablePtr->flashCells));
-  Tcl_DeleteHashTable(tablePtr->selCells);
-  ckfree((char *) (tablePtr->selCells));
-  Tcl_DeleteHashTable(tablePtr->colWidths);
-  ckfree((char *) (tablePtr->colWidths));
-  Tcl_DeleteHashTable(tablePtr->rowHeights);
-  ckfree((char *) (tablePtr->rowHeights));
-
-  /* Now free up all the tag information */
-  for (entryPtr = Tcl_FirstHashEntry(tablePtr->tagTable, &search);
-       entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
-    TableCleanupTag(tablePtr, (TableTag *) Tcl_GetHashValue(entryPtr));
-    ckfree((char *) Tcl_GetHashValue(entryPtr));
-  }
-  /* free up the stuff in the default tag */
-  TableCleanupTag(tablePtr, &(tablePtr->defaultTag));
-  /* And delete the actual hash table */
-  Tcl_DeleteHashTable(tablePtr->tagTable);
-  ckfree((char *) (tablePtr->tagTable));
-
-  /* Now free up all the embedded window info */
-  for (entryPtr = Tcl_FirstHashEntry(tablePtr->winTable, &search);
-       entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
-    EmbWinDelete(tablePtr, (TableEmbWindow *) Tcl_GetHashValue(entryPtr));
-  }
-  /* And delete the actual hash table */
-  Tcl_DeleteHashTable(tablePtr->winTable);
-  ckfree((char *) (tablePtr->winTable));
-
-  /* free the configuration options in the widget */
-  Tk_FreeOptions(TableConfig, (char *) tablePtr, tablePtr->display, 0);
-
-  /* and free the widget memory at last! */
-  ckfree((char *) (tablePtr));
+    register Table *tablePtr = (Table *) clientData;
+
+    if (!(tablePtr->flags & HAS_FOCUS) || (tablePtr->insertOffTime == 0)
+           || (tablePtr->flags & ACTIVE_DISABLED)
+           || (tablePtr->state != STATE_NORMAL)) {
+       return;
+    }
+
+    if (tablePtr->cursorTimer != NULL) {
+       Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
+    }
+
+    tablePtr->cursorTimer =
+       Tcl_CreateTimerHandler((tablePtr->flags & CURSOR_ON) ?
+               tablePtr->insertOffTime : tablePtr->insertOnTime,
+               TableCursorEvent, (ClientData) tablePtr);
+
+    /* Toggle the cursor */
+    tablePtr->flags ^= CURSOR_ON;
+
+    /* invalidate the cell */
+    TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol, CELL);
 }
 
 /*
- *--------------------------------------------------------------
+ *----------------------------------------------------------------------
  *
- * TableEventProc --
- *     This procedure is invoked by the Tk dispatcher for various
- *     events on tables.
+ * TableConfigCursor --
+ *     Configures the timer depending on the state of the table.
+ *     Equivalent to EntryFocusProc.
  *
  * Results:
  *     None.
  *
  * Side effects:
- *     When the window gets deleted, internal structures get
- *     cleaned up.  When it gets exposed, it is redisplayed.
+ *     The cursor will be switched off/on.
  *
- *--------------------------------------------------------------
+ *----------------------------------------------------------------------
  */
-static void
-TableEventProc(clientData, eventPtr)
-     ClientData clientData;    /* Information about window. */
-     XEvent *eventPtr;         /* Information about event. */
+void
+TableConfigCursor(register Table *tablePtr)
 {
-  Table *tablePtr = (Table *) clientData;
-  int row, col;
-
-  switch (eventPtr->type) {
-
-  case MotionNotify:
-    if (!(tablePtr->resize & SEL_NONE) && (tablePtr->bdcursor != None) &&
-       TableAtBorder(tablePtr, eventPtr->xmotion.x, eventPtr->xmotion.y,
-                     &row, &col) &&
-       ((row>=0 && (tablePtr->resize & SEL_ROW)) ||
-        (col>=0 && (tablePtr->resize & SEL_COL)))) {
-      /* The bordercursor is defined and we meet the criteria for being
-       * over a border.  Set the cursor to border if not already so */
-      if (!(tablePtr->flags & OVER_BORDER)) {
-       tablePtr->flags |= OVER_BORDER;
-       Tk_DefineCursor(tablePtr->tkwin, tablePtr->bdcursor);
-      }
-    } else if (tablePtr->flags & OVER_BORDER) {
-      tablePtr->flags &= ~OVER_BORDER;
-      if (tablePtr->cursor != None) {
-       Tk_DefineCursor(tablePtr->tkwin, tablePtr->cursor);
-      } else {
-       Tk_UndefineCursor(tablePtr->tkwin);
-      }
-    }
-    break;
-
-  case Expose:
-    TableInvalidate(tablePtr, eventPtr->xexpose.x, eventPtr->xexpose.y,
-                   eventPtr->xexpose.width, eventPtr->xexpose.height,
-                   INV_HIGHLIGHT);
-    break;
-
-  case DestroyNotify:
-    /* remove the command from the interpreter */
-    if (tablePtr->tkwin != NULL) {
-      tablePtr->tkwin = NULL;
-      Tcl_DeleteCommandFromToken(tablePtr->interp, tablePtr->widgetCmd);
-    }
-
-    /* cancel any pending update or timer */
-    if (tablePtr->flags & REDRAW_PENDING) {
-      Tcl_CancelIdleCall(TableDisplay, (ClientData) tablePtr);
-      tablePtr->flags &= ~REDRAW_PENDING;
-    }
-    Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
-    Tcl_DeleteTimerHandler(tablePtr->flashTimer);
+    /*
+     * To have a cursor, we have to have focus and allow edits
+     */
+    if ((tablePtr->flags & HAS_FOCUS) && (tablePtr->state == STATE_NORMAL) &&
+       !(tablePtr->flags & ACTIVE_DISABLED)) {
+       /*
+        * Turn the cursor ON
+        */
+       if (!(tablePtr->flags & CURSOR_ON)) {
+           tablePtr->flags |= CURSOR_ON;
+           /*
+            * Only refresh when we toggled cursor
+            */
+           TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
+                   CELL);
+       }
 
-    Tcl_EventuallyFree((ClientData) tablePtr, (Tcl_FreeProc *) TableDestroy);
-    break;
+       /* set up the first timer */
+       if (tablePtr->insertOffTime != 0) {
+           /* make sure nothing existed */
+           Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
+           tablePtr->cursorTimer =
+               Tcl_CreateTimerHandler(tablePtr->insertOnTime,
+                       TableCursorEvent, (ClientData) tablePtr);
+       }
+    } else {
+       /*
+        * Turn the cursor OFF
+        */
+       if ((tablePtr->flags & CURSOR_ON)) {
+           tablePtr->flags &= ~CURSOR_ON;
+           TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
+                   CELL);
+       }
 
-  case MapNotify: /* redraw table when remapped if it changed */
-    if (tablePtr->flags & REDRAW_ON_MAP) {
-      tablePtr->flags &= ~REDRAW_ON_MAP;
-      Tcl_Preserve((ClientData) tablePtr);
-      TableAdjustParams(tablePtr);
-      TableInvalidateAll(tablePtr, INV_FORCE|INV_HIGHLIGHT);
-      Tcl_Release((ClientData) tablePtr);
+       /* and disable the timer */
+       if (tablePtr->cursorTimer != NULL) {
+           Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
+       }
+       tablePtr->cursorTimer = NULL;
     }
-    break;
 
-  case ConfigureNotify:
-    Tcl_Preserve((ClientData) tablePtr);
-    TableAdjustParams(tablePtr);
-    TableInvalidateAll(tablePtr, INV_FORCE|INV_HIGHLIGHT);
-    Tcl_Release((ClientData) tablePtr);
-    break;
-
-  case FocusIn:
-  case FocusOut:
-    if (eventPtr->xfocus.detail != NotifyInferior) {
-      tablePtr->flags |= REDRAW_BORDER;
-      if (eventPtr->type == FocusOut) {
-       tablePtr->flags &= ~HAS_FOCUS;
-      } else {
-       tablePtr->flags |= HAS_FOCUS;
-      }
-      TableRedrawHighlight(tablePtr);
-      /* cancel the timer */
-      TableConfigCursor(tablePtr);
-    }
-    break;
-  }
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TableConfigure --
- *     This procedure is called to process an argv/argc list, plus
- *     the Tk option database, in order to configure (or reconfigure)
- *     a table widget.
+ * TableFetchSelection --
+ *     This procedure is called back by Tk when the selection is
+ *     requested by someone.  It returns part or all of the selection
+ *     in a buffer provided by the caller.
  *
  * Results:
- *     The return value is a standard Tcl result.  If TCL_ERROR is
- *     returned, then interp result contains an error message.
+ *     The return value is the number of non-NULL bytes stored
+ *     at buffer.  Buffer is filled (or partially filled) with a
+ *     NULL-terminated string containing part or all of the selection,
+ *     as given by offset and maxBytes.
  *
  * Side effects:
- *     Configuration information, such as colors, border width, etc.
- *     get set for tablePtr; old resources get freed, if there were any.
- *     Certain values might be constrained.
+ *     None.
  *
  *----------------------------------------------------------------------
  */
 static int
-TableConfigure(interp, tablePtr, argc, argv, flags, forceUpdate)
-    Tcl_Interp *interp;                /* Used for error reporting. */
-    register Table *tablePtr;  /* Information about widget;  may or may
-                                * not already have values for some fields. */
-    int argc;                  /* Number of valid entries in argv. */
-    char **argv;               /* Arguments. */
-    int flags;                 /* Flags to pass to Tk_ConfigureWidget. */
-    int forceUpdate;           /* Whether to force an update - required
-                                * for initial configuration */
+TableFetchSelection(clientData, offset, buffer, maxBytes)
+     ClientData clientData;    /* Information about table widget. */
+     int offset;               /* Offset within selection of first
+                                * character to be returned. */
+     char *buffer;             /* Location in which to place selection. */
+     int maxBytes;             /* Maximum number of bytes to place at buffer,
+                                * not including terminating NULL. */
 {
-  Tcl_HashSearch search;
-  int oldUse, oldCaching, oldExport, result = TCL_OK;
-  char *oldVar;
-  Tcl_DString error;
-  Tk_FontMetrics fm;
-
-  oldExport    = tablePtr->exportSelection;
-  oldCaching   = tablePtr->caching;
-  oldUse       = tablePtr->useCmd;
-  oldVar       = tablePtr->arrayVar;
-
-  /* Do the configuration */
-  if (Tk_ConfigureWidget(interp, tablePtr->tkwin, TableConfig, argc, argv,
-                        (char *) tablePtr, flags) != TCL_OK)
-    return TCL_ERROR;
-
-  Tcl_DStringInit(&error);
-
-  /* Any time we configure, reevaluate what our data source is */
-  tablePtr->dataSource = DATA_NONE;
-  if (tablePtr->caching) {
-    tablePtr->dataSource |= DATA_CACHE;
-  }
-  if (tablePtr->command && tablePtr->useCmd) {
-    tablePtr->dataSource |= DATA_COMMAND;
-  } else if (tablePtr->arrayVar) {
-    tablePtr->dataSource |= DATA_ARRAY;
-  }
-
-  /* Check to see if the array variable was changed */
-  if (strcmp((tablePtr->arrayVar?tablePtr->arrayVar:""),(oldVar?oldVar:""))) {
-    /* only do the following if arrayVar is our data source */
-    if (tablePtr->dataSource & DATA_ARRAY) {
-      /* ensure that the cache will flush later so it gets the new values */
-      oldCaching = !(tablePtr->caching);
+    register Table *tablePtr = (Table *) clientData;
+    Tcl_Interp *interp = tablePtr->interp;
+    char *value, *data, *rowsep = tablePtr->rowSep, *colsep = tablePtr->colSep;
+    Tcl_DString selection;
+    Tcl_HashEntry *entryPtr;
+    Tcl_HashSearch search;
+    int length, count, lastrow=0, needcs=0, r, c, listArgc, rslen=0, cslen=0;
+    int numcols, numrows;
+    char **listArgv;
+
+    /* if we are not exporting the selection ||
+     * we have no data source, return */
+    if (!tablePtr->exportSelection ||
+       (tablePtr->dataSource == DATA_NONE)) {
+       return -1;
     }
-    /* remove the trace on the old array variable if there was one */
-    if (oldVar != NULL)
-      Tcl_UntraceVar(interp, oldVar,
-                    TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY,
-                    (Tcl_VarTraceProc *)TableVarProc, (ClientData)tablePtr);
-    /* Check whether variable is an array and trace it if it is */
-    if (tablePtr->arrayVar != NULL) {
-      /* does the variable exist as an array? */
-      if (Tcl_SetVar2(interp, tablePtr->arrayVar, TEST_KEY, "",
-                     TCL_GLOBAL_ONLY) == NULL) {
-       Tcl_DStringAppend(&error, "invalid variable value \"", -1);
-       Tcl_DStringAppend(&error, tablePtr->arrayVar, -1);
-       Tcl_DStringAppend(&error, "\": could not be made an array", -1);
-       ckfree(tablePtr->arrayVar);
-       tablePtr->arrayVar = NULL;
-       tablePtr->dataSource &= ~DATA_ARRAY;
-       result = TCL_ERROR;
-      } else {
-       Tcl_UnsetVar2(interp, tablePtr->arrayVar, TEST_KEY, TCL_GLOBAL_ONLY);
-       /* remove the effect of the evaluation */
-       /* set a trace on the variable */
-       Tcl_TraceVar(interp, tablePtr->arrayVar,
-                    TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY,
-                    (Tcl_VarTraceProc *)TableVarProc, (ClientData) tablePtr);
 
-       /* only do the following if arrayVar is our data source */
-       if (tablePtr->dataSource & DATA_ARRAY) {
-         /* get the current value of the selection */
-         TableGetActiveBuf(tablePtr);
+    /* First get a sorted list of the selected elements */
+    Tcl_DStringInit(&selection);
+    for (entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
+        entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
+       Tcl_DStringAppendElement(&selection,
+                                Tcl_GetHashKey(tablePtr->selCells, entryPtr));
+    }
+    value = TableCellSort(tablePtr, Tcl_DStringValue(&selection));
+    Tcl_DStringFree(&selection);
+
+    if (value == NULL ||
+       Tcl_SplitList(interp, value, &listArgc, &listArgv) != TCL_OK) {
+       return -1;
+    }
+    Tcl_Free(value);
+
+    Tcl_DStringInit(&selection);
+    rslen = (rowsep?(strlen(rowsep)):0);
+    cslen = (colsep?(strlen(colsep)):0);
+    numrows = numcols = 0;
+    for (count = 0; count < listArgc; count++) {
+       TableParseArrayIndex(&r, &c, listArgv[count]);
+       if (count) {
+           if (lastrow != r) {
+               lastrow = r;
+               needcs = 0;
+               if (rslen) {
+                   Tcl_DStringAppend(&selection, rowsep, rslen);
+               } else {
+                   Tcl_DStringEndSublist(&selection);
+                   Tcl_DStringStartSublist(&selection);
+               }
+               ++numrows;
+           } else {
+               if (++needcs > numcols)
+                   numcols = needcs;
+           }
+       } else {
+           lastrow = r;
+           needcs = 0;
+           if (!rslen) {
+               Tcl_DStringStartSublist(&selection);
+           }
+       }
+       data = TableGetCellValue(tablePtr, r, c);
+       if (cslen) {
+           if (needcs) {
+               Tcl_DStringAppend(&selection, colsep, cslen);
+           }
+           Tcl_DStringAppend(&selection, data, -1);
+       } else {
+           Tcl_DStringAppendElement(&selection, data);
        }
-      }
     }
-  }
-  if ((tablePtr->command && tablePtr->useCmd && !oldUse) ||
-      (tablePtr->arrayVar && !(tablePtr->useCmd) && oldUse)) {
-    /* our effective data source changed, so flush and
-     * retrieve new active buffer */
-    TableFlushCache(tablePtr);
-    TableGetActiveBuf(tablePtr);
-    forceUpdate = 1;
-  } else if (oldCaching != tablePtr->caching) {
-    /* caching changed, so just clear the cache for safety */
-    TableFlushCache(tablePtr);
-    forceUpdate = 1;
-  }
-
-  /* set up the default column width and row height */
-  Tk_GetFontMetrics(tablePtr->defaultTag.tkfont, &fm);
-  tablePtr->charWidth = Tk_TextWidth(tablePtr->defaultTag.tkfont, "0", 1);
-  tablePtr->charHeight = fm.linespace + 2;
-
-  if (tablePtr->insertWidth <= 0) {
-    tablePtr->insertWidth = 2;
-  }
-  if (tablePtr->insertBorderWidth > tablePtr->insertWidth/2) {
-    tablePtr->insertBorderWidth = tablePtr->insertWidth/2;
-  }
-  tablePtr->highlightWidth = MAX(0,tablePtr->highlightWidth);
-  /* the border must be >= 0 */
-  tablePtr->borderWidth = MAX(0,tablePtr->borderWidth);
-  /* when drawing fast or single, the border must be <= 1 */
-  if (tablePtr->drawMode & (DRAW_MODE_SINGLE|DRAW_MODE_FAST)) {
-    tablePtr->borderWidth = MIN(1,tablePtr->borderWidth);
-  }
-
-  /* Ensure that certain values are within proper constraints */
-  tablePtr->rows = MAX(1,tablePtr->rows);
-  tablePtr->cols = MAX(1,tablePtr->cols);
-  tablePtr->titleRows = MIN(MAX(0,tablePtr->titleRows),tablePtr->rows);
-  tablePtr->titleCols = MIN(MAX(0,tablePtr->titleCols),tablePtr->cols);
-  tablePtr->padX = MAX(0,tablePtr->padX);
-  tablePtr->padY = MAX(0,tablePtr->padY);
-  tablePtr->maxReqCols = MAX(0,tablePtr->maxReqCols);
-  tablePtr->maxReqRows = MAX(0,tablePtr->maxReqRows);
-
-  /*
-   * Claim the selection if we've suddenly started exporting it and
-   * there is a selection to export.
-   */
-  if (tablePtr->exportSelection && !oldExport &&
-      (Tcl_FirstHashEntry(tablePtr->selCells, &search) != NULL)) {
-    Tk_OwnSelection(tablePtr->tkwin, XA_PRIMARY, TableLostSelection,
-                   (ClientData) tablePtr);
-  }
-
-  /* only do the full reconfigure if absolutely necessary */
-  if (!forceUpdate) {
-    int i;
-    for (i = 0; i < argc-1; i += 2) {
-      if (Cmd_GetValue(update_config, argv[i])) {
-       forceUpdate = 1;
-       break;
-      }
+    if (!rslen && count) {
+       Tcl_DStringEndSublist(&selection);
+    }
+    Tcl_Free((char *) listArgv);
+
+    if (tablePtr->selCmd != NULL) {
+       Tcl_DString script;
+       Tcl_DStringInit(&script);
+       ExpandPercents(tablePtr, tablePtr->selCmd, numrows+1, numcols+1,
+                      Tcl_DStringValue(&selection), (char *)NULL,
+                      listArgc, &script, CMD_ACTIVATE);
+       if (Tcl_GlobalEval(interp, Tcl_DStringValue(&script)) == TCL_ERROR) {
+           Tcl_AddErrorInfo(interp,
+                            "\n    (error in table selection command)");
+           Tcl_BackgroundError(interp);
+           Tcl_DStringFree(&script);
+           Tcl_DStringFree(&selection);
+           return -1;
+       } else {
+           Tcl_DStringGetResult(interp, &selection);
+       }
+       Tcl_DStringFree(&script);
     }
-  }
-  if (forceUpdate) {
-    /* 
-     * Calculate the row and column starts 
-     * Adjust the top left corner of the internal display 
-     */
-    TableAdjustParams(tablePtr);
-    /* reset the cursor */
-    TableConfigCursor(tablePtr);
-    /* set up the background colour in the window */
-    Tk_SetBackgroundFromBorder(tablePtr->tkwin, tablePtr->defaultTag.bg);
-    /* set the geometry and border */
-    TableGeometryRequest(tablePtr);
-    Tk_SetInternalBorder(tablePtr->tkwin, tablePtr->highlightWidth);
-    /* invalidate the whole table */
-    TableInvalidateAll(tablePtr, INV_HIGHLIGHT);
-  }
-  /* FIX this is goofy because the result could be munged by other
-   * functions.  Needs to be improved */
-  Tcl_ResetResult(interp);
-  if (result == TCL_ERROR) {
-    Tcl_AddErrorInfo(interp, "\t(configuring table widget)");
-    Tcl_DStringResult(interp, &error);
-  }
-  Tcl_DStringFree(&error);
-  return result;
+
+    length = Tcl_DStringLength(&selection);
+
+    if (length == 0)
+       return -1;
+
+    /* Copy the requested portion of the selection to the buffer. */
+    count = length - offset;
+    if (count <= 0) {
+       count = 0;
+    } else {
+       if (count > maxBytes) {
+           count = maxBytes;
+       }
+       memcpy((VOID *) buffer,
+              (VOID *) (Tcl_DStringValue(&selection) + offset),
+              (size_t) count);
+    }
+    buffer[count] = '\0';
+    Tcl_DStringFree(&selection);
+    return count;
 }
 
-#ifndef CLASSPATCH
 /*
- * As long as we wait for the Function in general
+ *----------------------------------------------------------------------
  *
- * This parses the "-class" option for the table.
+ * TableLostSelection --
+ *     This procedure is called back by Tk when the selection is
+ *     grabbed away from a table widget.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     The existing selection is unhighlighted, and the window is
+ *     marked as not containing a selection.
+ *
+ *----------------------------------------------------------------------
  */
-static void
-Tk_ClassOption(Tk_Window tkwin, char *defaultclass, int *argcp, char ***argvp)
+void
+TableLostSelection(clientData)
+     ClientData clientData;    /* Information about table widget. */
 {
-  char *classname = (((*argcp)<3) || (strcmp((*argvp)[2],"-class"))) ?
-    defaultclass : ((*argcp)-=2,(*argcp)+=2,(*argvp)[1]);
-  Tk_SetClass(tkwin,classname);
+    register Table *tablePtr = (Table *) clientData;
+
+    if (tablePtr->exportSelection) {
+       Tcl_HashEntry *entryPtr;
+       Tcl_HashSearch search;
+       int row, col;
+
+       /* Same as SEL CLEAR ALL */
+       for (entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
+            entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
+           TableParseArrayIndex(&row, &col,
+                                Tcl_GetHashKey(tablePtr->selCells,entryPtr));
+           Tcl_DeleteHashEntry(entryPtr);
+           TableRefresh(tablePtr, row-tablePtr->rowOffset,
+                        col-tablePtr->colOffset, CELL);
+       }
+    }
 }
-#endif
 
 /*
  *----------------------------------------------------------------------
  *
- * TableCmdDeletedProc --
- *
- *     This procedure is invoked when a widget command is deleted.  If
- *     the widget isn't already in the process of being destroyed,
- *     this command destroys it.
+ * TableRestrictProc --
+ *     A Tk_RestrictProc used by TableValidateChange to eliminate any
+ *     extra key input events in the event queue that
+ *     have a serial number no less than a given value.
  *
  * Results:
- *     None.
+ *     Returns either TK_DISCARD_EVENT or TK_DEFER_EVENT.
  *
  * Side effects:
- *     The widget is destroyed.
+ *     None.
  *
  *----------------------------------------------------------------------
  */
-static void
-TableCmdDeletedProc(ClientData clientData)
+static Tk_RestrictAction
+TableRestrictProc(serial, eventPtr)
+     ClientData serial;
+     XEvent *eventPtr;
 {
-  Table *tablePtr = (Table *) clientData;
-  Tk_Window tkwin;
-
-  /*
-   * This procedure could be invoked either because the window was
-   * destroyed and the command was then deleted (in which case tkwin
-   * is NULL) or because the command was deleted, and then this procedure
-   * destroys the widget.
-   */
-
-  /* This is needed to avoid bug where the DLL is unloaded before
-   * the table is properly destroyed */
-  Tcl_DeleteExitHandler((Tcl_ExitProc *) TableCmdDeletedProc,
-                       (ClientData) tablePtr);
-  if (tablePtr->tkwin != NULL) {
-    tkwin = tablePtr->tkwin;
-    tablePtr->tkwin = NULL;
-    Tk_DestroyWindow(tkwin);
-  }
+    if ((eventPtr->type == KeyRelease || eventPtr->type == KeyPress) &&
+       ((eventPtr->xany.serial-(unsigned int)serial) > 0)) {
+       return TK_DEFER_EVENT;
+    } else {
+       return TK_PROCESS_EVENT;
+    }
 }
 
 /*
  *--------------------------------------------------------------
  *
- * TableCmd --
- *     This procedure is invoked to process the "table" Tcl
- *     command.  See the user documentation for details on what
- *     it does.
+ * TableValidateChange --
+ *     This procedure is invoked when any character is added or
+ *     removed from the table widget, or a set has triggered validation.
  *
  * Results:
- *     A standard Tcl result.
+ *     TCL_OK    if the validatecommand accepts the new string,
+ *     TCL_BREAK if the validatecommand rejects the new string,
+ *      TCL_ERROR if any problems occured with validatecommand.
  *
  * Side effects:
- *     See the user documentation.
+ *      The insertion/deletion may be aborted, and the
+ *      validatecommand might turn itself off (if an error
+ *      or loop condition arises).
  *
  *--------------------------------------------------------------
  */
-static int
-TableCmd(clientData, interp, argc, argv)
-     ClientData clientData;    /* Main window associated with
-                                * interpreter. */
-     Tcl_Interp *interp;       /* Current interpreter. */
-     int argc;                 /* Number of arguments. */
-     char **argv;              /* Argument strings. */
+int
+TableValidateChange(tablePtr, r, c, old, new, index)
+     register Table *tablePtr; /* Table that needs validation. */
+     int r, c;                 /* row,col index of cell in user coords */
+     char *old;                        /* current value of cell */
+     char *new;                        /* potential new value of cell */
+     int index;                        /* index of insert/delete, -1 otherwise */
 {
-  register Table *tablePtr;
-  Tk_Window tkwin = (Tk_Window) clientData;
-  Tk_Window new;
-
-  if (argc < 2) {
-    Tcl_AppendResult(interp, "wrong # args: should be \"",
-                    argv[0], " pathname ?options?\"", (char *) NULL);
-    return TCL_ERROR;
-  }
-
-  new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
-  if (new == NULL) {
-    return TCL_ERROR;
-  }
-
-  tablePtr                     = (Table *) ckalloc(sizeof(Table));
-  tablePtr->tkwin              = new;
-  tablePtr->display            = Tk_Display(new);
-  tablePtr->interp             = interp;
-
-  tablePtr->topRow             = 0;
-  tablePtr->leftCol            = 0;
-  tablePtr->anchorRow          = -1;
-  tablePtr->anchorCol          = -1;
-  tablePtr->activeRow          = -1;
-  tablePtr->activeCol          = -1;
-  tablePtr->oldTopRow          = -1;
-  tablePtr->oldLeftCol         = -1;
-  tablePtr->oldActRow          = -1;
-  tablePtr->oldActCol          = -1;
-  tablePtr->seen[0]            = -1;
-  tablePtr->icursor            = 0;
-  tablePtr->flags              = 0;
-
-  tablePtr->colPixels          = (int *) 0;
-  tablePtr->rowPixels          = (int *) 0;
-  tablePtr->colStarts          = (int *) 0;
-  tablePtr->rowStarts          = (int *) 0;
-  tablePtr->cursorTimer                = (Tcl_TimerToken)0;
-  tablePtr->flashTimer         = (Tcl_TimerToken)0;
-  tablePtr->dataSource         = DATA_NONE;
-  tablePtr->activeBuf          = ckalloc(1);
-  *(tablePtr->activeBuf)       = '\0';
-  tablePtr->activeLayout       = NULL;
-
-  /* misc tables */
-  tablePtr->tagTable   = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
-  Tcl_InitHashTable(tablePtr->tagTable, TCL_STRING_KEYS);
-  tablePtr->winTable   = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
-  Tcl_InitHashTable(tablePtr->winTable, TCL_STRING_KEYS);
-
-  /* internal value cache */
-  tablePtr->cache      = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
-  Tcl_InitHashTable(tablePtr->cache, TCL_STRING_KEYS);
-
-  /* style hash tables */
-  tablePtr->colWidths  = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
-  Tcl_InitHashTable(tablePtr->colWidths, TCL_ONE_WORD_KEYS);
-  tablePtr->rowHeights = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
-  Tcl_InitHashTable(tablePtr->rowHeights, TCL_ONE_WORD_KEYS);
-
-  /* style hash tables */
-  tablePtr->rowStyles  = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
-  Tcl_InitHashTable(tablePtr->rowStyles, TCL_ONE_WORD_KEYS);
-  tablePtr->colStyles  = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
-  Tcl_InitHashTable(tablePtr->colStyles, TCL_ONE_WORD_KEYS);
-  tablePtr->cellStyles = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
-  Tcl_InitHashTable(tablePtr->cellStyles, TCL_STRING_KEYS);
-
-  /* special style hash tables */
-  tablePtr->flashCells = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
-  Tcl_InitHashTable(tablePtr->flashCells, TCL_STRING_KEYS);
-  tablePtr->selCells   = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
-  Tcl_InitHashTable(tablePtr->selCells, TCL_STRING_KEYS);
-
-  tablePtr->rows               = 0;
-  tablePtr->cols               = 0;
-  tablePtr->selectMode         = NULL;
-  tablePtr->selectTitles       = 0;
-  tablePtr->defRowHeight       = 0;
-  tablePtr->defColWidth                = 0;
-  tablePtr->arrayVar           = NULL;
-  tablePtr->borderWidth                = 0;
-  tablePtr->defaultTag.anchor  = TK_ANCHOR_CENTER;
-  tablePtr->defaultTag.bg      = NULL;
-  tablePtr->defaultTag.fg      = NULL;
-  tablePtr->defaultTag.tkfont  = NULL;
-  tablePtr->defaultTag.image   = NULL;
-  tablePtr->defaultTag.imageStr        = NULL;
-  tablePtr->defaultTag.justify = TK_JUSTIFY_LEFT;
-  tablePtr->defaultTag.multiline       = 1;
-  tablePtr->defaultTag.relief  = TK_RELIEF_FLAT;
-  tablePtr->defaultTag.showtext        = 0;
-  tablePtr->defaultTag.state   = STATE_UNKNOWN;
-  tablePtr->defaultTag.wrap    = 0;
-  tablePtr->yScrollCmd         = NULL;
-  tablePtr->xScrollCmd         = NULL;
-  tablePtr->insertBg           = NULL;
-  tablePtr->cursor             = None;
-  tablePtr->bdcursor           = None;
-  tablePtr->titleRows          = 0;
-  tablePtr->titleCols          = 0;
-  tablePtr->drawMode           = DRAW_MODE_TK_COMPAT;
-  tablePtr->colStretch         = STRETCH_MODE_NONE;
-  tablePtr->rowStretch         = STRETCH_MODE_NONE;
-  tablePtr->maxWidth           = 0;
-  tablePtr->maxHeight          = 0;
-  tablePtr->charWidth          = 0;
-  tablePtr->charHeight         = 0;
-  tablePtr->colOffset          = 0;
-  tablePtr->rowOffset          = 0;
-  tablePtr->flashTime          = 2;
-  tablePtr->rowTagCmd          = NULL;
-  tablePtr->colTagCmd          = NULL;
-  tablePtr->highlightWidth     = 0;
-  tablePtr->highlightBgColorPtr        = NULL;
-  tablePtr->highlightColorPtr  = NULL;
-  tablePtr->takeFocus          = NULL;
-  tablePtr->state              = STATE_NORMAL;
-  tablePtr->insertWidth                = 0;
-  tablePtr->insertBorderWidth  = 0;
-  tablePtr->insertOnTime       = 0;
-  tablePtr->insertOffTime      = 0;
-  tablePtr->invertSelected     = 0;
-  tablePtr->autoClear          = 0;
-  tablePtr->flashMode          = 0;
-  tablePtr->exportSelection    = 1;
-  tablePtr->rowSep             = NULL;
-  tablePtr->colSep             = NULL;
-  tablePtr->browseCmd          = NULL;
-  tablePtr->command            = NULL;
-  tablePtr->selCmd             = NULL;
-  tablePtr->valCmd             = NULL;
-  tablePtr->validate           = 0;
-  tablePtr->useCmd             = 1;
-  tablePtr->caching            = 0;
-  tablePtr->padX               = 0;
-  tablePtr->padY               = 0;
-  tablePtr->maxReqCols         = 0;
-  tablePtr->maxReqRows         = 0;
-  tablePtr->maxReqWidth                = 800;
-  tablePtr->maxReqHeight       = 600;
-
-  /* selection handlers needed here */
-
-  Tk_ClassOption(new, "Table", &argc, &argv);
-  Tk_CreateEventHandler(tablePtr->tkwin,
-                       PointerMotionMask|ExposureMask|StructureNotifyMask|FocusChangeMask|VisibilityChangeMask,
-                       TableEventProc, (ClientData) tablePtr);
-  Tk_CreateSelHandler(tablePtr->tkwin, XA_PRIMARY, XA_STRING,
-                     TableFetchSelection, (ClientData) tablePtr, XA_STRING);
-
-  tablePtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(tablePtr->tkwin),
-                       TableWidgetCmd, (ClientData) tablePtr,
-                       (Tcl_CmdDeleteProc *) TableCmdDeletedProc);
-  if (TableConfigure(interp, tablePtr, argc - 2, argv + 2, 0, 1) != TCL_OK) {
-    Tk_DestroyWindow(new);
-    return TCL_ERROR;
-  }
-  TableInitTags(tablePtr);
-  /* This is needed to avoid bug where the DLL is unloaded before
-   * the table is properly destroyed */
-  Tcl_CreateExitHandler((Tcl_ExitProc *) TableCmdDeletedProc,
-                       (ClientData) tablePtr);
-  Tcl_SetResult(interp, Tk_PathName(tablePtr->tkwin), TCL_STATIC);
-  return TCL_OK;
+    register Tcl_Interp *interp = tablePtr->interp;
+    int code, bool;
+    Tk_RestrictProc *rstrct;
+    ClientData cdata;
+    Tcl_DString script;
+    
+    if (tablePtr->valCmd == NULL || tablePtr->validate == 0) {
+       return TCL_OK;
+    }
+
+    /* Magic code to make this bit of code UI synchronous in the face of
+     * possible new key events */
+    XSync(tablePtr->display, False);
+    rstrct = Tk_RestrictEvents(TableRestrictProc, (ClientData)
+                                NextRequest(tablePtr->display), &cdata);
+
+    /*
+     * If we're already validating, then we're hitting a loop condition
+     * Return and set validate to 0 to disallow further validations
+     * and prevent current validation from finishing
+     */
+    if (tablePtr->flags & VALIDATING) {
+       tablePtr->validate = 0;
+       return TCL_OK;
+    }
+    tablePtr->flags |= VALIDATING;
+
+    /* Now form command string and run through the -validatecommand */
+    Tcl_DStringInit(&script);
+    ExpandPercents(tablePtr, tablePtr->valCmd, r, c, old, new, index, &script,
+                  CMD_VALIDATE);
+    code = Tcl_GlobalEval(tablePtr->interp, Tcl_DStringValue(&script));
+    Tcl_DStringFree(&script);
+
+    if (code != TCL_OK && code != TCL_RETURN) {
+       Tcl_AddErrorInfo(interp,
+                        "\n\t(in validation command executed by table)");
+       Tcl_BackgroundError(interp);
+       code = TCL_ERROR;
+    } else if (Tcl_GetBooleanFromObj(interp, Tcl_GetObjResult(interp),
+                                    &bool) != TCL_OK) {
+       Tcl_AddErrorInfo(interp,
+                        "\n\tboolean not returned by validation command");
+       Tcl_BackgroundError(interp);
+       code = TCL_ERROR;
+    } else {
+       code = (bool) ? TCL_OK : TCL_BREAK;
+    }
+    Tcl_SetStringObj(Tcl_GetObjResult(interp), (char *) NULL, 0);
+
+    /*
+     * If ->validate has become VALIDATE_NONE during the validation,
+     * it means that a loop condition almost occured.  Do not allow
+     * this validation result to finish.
+     */
+    if (tablePtr->validate == 0) {
+       code = TCL_ERROR;
+    }
+
+    /* If validate will return ERROR, then disallow further validations */
+    if (code == TCL_ERROR) {
+       tablePtr->validate = 0;
+    }
+
+    Tk_RestrictEvents(rstrct, cdata, &cdata);
+    tablePtr->flags &= ~VALIDATING;
+
+    return code;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ExpandPercents --
+ *     Given a command and an event, produce a new command
+ *     by replacing % constructs in the original command
+ *     with information from the X event.
+ *
+ * Results:
+ *     The new expanded command is appended to the dynamic string
+ *     given by dsPtr.
+ *
+ * Side effects:
+ *     None.
+ *
+ *--------------------------------------------------------------
+ */
+void
+ExpandPercents(tablePtr, before, r, c, old, new, index, dsPtr, cmdType)
+     Table *tablePtr;          /* Table that needs validation. */
+     char *before;             /* Command containing percent
+                                * expressions to be replaced. */
+     int r, c;                 /* row,col index of cell */
+     char *old;                 /* current value of cell */
+     char *new;                 /* potential new value of cell */
+     int index;                 /* index of insert/delete */
+     Tcl_DString *dsPtr;        /* Dynamic string in which to append
+                                * new command. */
+     int cmdType;              /* type of command to make %-subs for */
+{
+    int length, spaceNeeded, cvtFlags;
+#ifdef TCL_UTF_MAX
+    Tcl_UniChar ch;
+#else
+    char ch;
+#endif
+    char *string, buf[INDEX_BUFSIZE];
+
+    /* This returns the static value of the string as set in the array */
+    if (old == NULL && cmdType == CMD_VALIDATE) {
+       old = TableGetCellValue(tablePtr, r, c);
+    }
+
+    while (1) {
+       if (*before == '\0') {
+           break;
+       }
+       /*
+        * Find everything up to the next % character and append it
+        * to the result string.
+        */
+
+       string = before;
+#ifdef TCL_UTF_MAX
+       /* No need to convert '%', as it is in ascii range */
+       string = Tcl_UtfFindFirst(before, '%');
+#else
+       string = strchr(before, '%');
+#endif
+       if (string == (char *) NULL) {
+           Tcl_DStringAppend(dsPtr, before, -1);
+           break;
+       } else if (string != before) {
+           Tcl_DStringAppend(dsPtr, before, string-before);
+           before = string;
+       }
+
+       /*
+        * There's a percent sequence here.  Process it.
+        */
+
+       before++; /* skip over % */
+       if (*before != '\0') {
+#ifdef TCL_UTF_MAX
+           before += Tcl_UtfToUniChar(before, &ch);
+#else
+           ch = before[0];
+           before++;
+#endif
+       } else {
+           ch = '%';
+       }
+       switch (ch) {
+       case 'c':
+           sprintf(buf, "%d", c);
+           string = buf;
+           break;
+       case 'C': /* index of cell */
+           TableMakeArrayIndex(r, c, buf);
+           string = buf;
+           break;
+       case 'r':
+           sprintf(buf, "%d", r);
+           string = buf;
+           break;
+       case 'i': /* index of cursor OR |number| of cells selected */
+           sprintf(buf, "%d", index);
+           string = buf;
+           break;
+       case 's': /* Current cell value */
+           string = old;
+           break;
+       case 'S': /* Potential new value of cell */
+           string = (new?new:old);
+           break;
+       case 'W': /* widget name */
+           string = Tk_PathName(tablePtr->tkwin);
+           break;
+       default:
+#ifdef TCL_UTF_MAX
+           length = Tcl_UniCharToUtf(ch, buf);
+#else
+           buf[0] = ch;
+           length = 1;
+#endif
+           buf[length] = '\0';
+           string = buf;
+           break;
+       }
+
+       spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
+       length = Tcl_DStringLength(dsPtr);
+       Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
+       spaceNeeded = Tcl_ConvertElement(string,
+                                        Tcl_DStringValue(dsPtr) + length,
+                                        cvtFlags | TCL_DONT_USE_BRACES);
+       Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
+    }
+    Tcl_DStringAppend(dsPtr, "", 1);
 }
 
 /* Function to call on loading the Table module */
 
-EXPORT(int,Tktable_Init)(interp)
-    Tcl_Interp *interp;
+#ifdef BUILD_tkTable
+#   undef TCL_STORAGE_CLASS
+#   define TCL_STORAGE_CLASS DLLEXPORT
+#endif
+#ifdef MAC_TCL
+#pragma export on
+#endif
+EXTERN int
+Tktable_Init(interp)
+     Tcl_Interp *interp;
 {
-  static char init_script[] =
-    "if {[catch {source \"" TCL_RUNTIME "\"}]} {\n"
-#include "tkTabletcl.h"
-    "}\n";
-  if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 0) == NULL ||
-      Tcl_PkgRequire(interp, "Tk", TK_VERSION, 0) == NULL ||
-      Tcl_PkgProvide(interp, "Tktable", TBL_VERSION) != TCL_OK) {
-    return TCL_ERROR;
-  }
-  Tcl_CreateCommand(interp, TBL_COMMAND, TableCmd,
-                   (ClientData) Tk_MainWindow(interp),
-                   (Tcl_CmdDeleteProc *) NULL);
-
-  return Tcl_Eval(interp, init_script);
+    /* This defines the static chars tkTable(Safe)InitScript */
+#include "tkTableInitScript.h"
+
+    if (
+#ifdef USE_TCL_STUBS
+       Tcl_InitStubs(interp, "8.0", 0)
+#else
+       Tcl_PkgRequire(interp, "Tcl", "8.0", 0)
+#endif
+       == NULL) {
+       return TCL_ERROR;
+    }
+    if (
+#ifdef USE_TK_STUBS
+       Tk_InitStubs(interp, "8.0", 0)
+#else
+#    if (TK_MAJOR_VERSION == 8) && (TK_MINOR_VERSION == 0)
+       /* We require 8.0 exact because of the Unicode in 8.1+ */
+       Tcl_PkgRequire(interp, "Tk", "8.0", 1)
+#    else
+       Tcl_PkgRequire(interp, "Tk", "8.0", 0)
+#    endif
+#endif
+       == NULL) {
+       return TCL_ERROR;
+    }
+    if (Tcl_PkgProvide(interp, "Tktable", TBL_VERSION) != TCL_OK) {
+       return TCL_ERROR;
+    }
+    Tcl_CreateObjCommand(interp, TBL_COMMAND, Tk_TableObjCmd,
+                        (ClientData) Tk_MainWindow(interp),
+                        (Tcl_CmdDeleteProc *) NULL);
+
+    /*
+     * The init script can't make certain calls in a safe interpreter,
+     * so we always have to use the embedded runtime for it
+     */
+    return Tcl_Eval(interp, Tcl_IsSafe(interp) ?
+           tkTableSafeInitScript : tkTableInitScript);
 }
 
-EXPORT(int,Tktable_SafeInit)(interp)
-    Tcl_Interp *interp;
+EXTERN int
+Tktable_SafeInit(interp)
+     Tcl_Interp *interp;
 {
-  return Tktable_Init(interp);
+    return Tktable_Init(interp);
 }
+#ifdef MAC_TCL
+#pragma export reset
+#endif
 
-#ifdef _WIN32
+#ifdef WIN32
 /*
  *----------------------------------------------------------------------
  *
@@ -4889,10 +3868,10 @@ EXPORT(int,Tktable_SafeInit)(interp)
 
 BOOL APIENTRY
 DllEntryPoint(hInst, reason, reserved)
-    HINSTANCE hInst;           /* Library instance handle. */
-    DWORD reason;              /* Reason this function is being called. */
-    LPVOID reserved;           /* Not used. */
+     HINSTANCE hInst;          /* Library instance handle. */
+     DWORD reason;             /* Reason this function is being called. */
+     LPVOID reserved;          /* Not used. */
 {
-  return TRUE;
+    return TRUE;
 }
 #endif
index 360b1de..e4c32d3 100644 (file)
@@ -4,11 +4,12 @@
  *     This is the header file for the module that implements
  *     table widgets for the Tk toolkit.
  *
- * Copyright (c) 1997,1998 Jeffrey Hobbs
+ * Copyright (c) 1997-2000 Jeffrey Hobbs
  *
- * See the file "license.terms" for information on usage and redistribution
+ * See the file "license.txt" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  *
+ * RCS: @(#) $Id$
  */
 
 #ifndef _TKTABLE_H_
 #include <string.h>
 #include <stdlib.h>
 #include <tk.h>
-#include <X11/Xatom.h>
+#ifdef MAC_TCL
+# include <Xatom.h>
+#else
+# include <X11/Xatom.h>
+#endif /* MAC_TCL */
+
+#if (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION == 0) /* Tcl8.0 stuff */
+#define Tcl_GetString(objPtr)  Tcl_GetStringFromObj(objPtr, (int *)NULL)
+#endif
+
+/* This EXTERN declaration is needed for Tcl < 8.0.3 */
+#ifndef EXTERN
+# ifdef __cplusplus
+#  define EXTERN extern "C"
+# else
+#  define EXTERN extern
+# endif
+#endif
 
-#include "tkTableCmd.h"
+#ifdef TCL_STORAGE_CLASS
+# undef TCL_STORAGE_CLASS
+#endif
+#ifdef BUILD_tkTable
+# define TCL_STORAGE_CLASS DLLEXPORT
+#else
+# define TCL_STORAGE_CLASS DLLIMPORT
+#endif
 
-#ifdef _WIN32
+#ifdef WIN32
 #   define WIN32_LEAN_AND_MEAN
 #   include <windows.h>
 #   undef WIN32_LEAN_AND_MEAN
-
-/*
- * VC++ has an alternate entry point called DllMain, so we need to rename
- * our entry point.
- */
-
+/* VC++ has an entry point called DllMain instead of DllEntryPoint */
 #   if defined(_MSC_VER)
-#      define EXPORT(a,b) __declspec(dllexport) a b
 #      define DllEntryPoint DllMain
-#   else
-#      if defined(__BORLANDC__)
-#          define EXPORT(a,b) a _export b
-#      else
-#          define EXPORT(a,b) a b
-#      endif
 #   endif
-
-/* Necessary to get XSync call defined */
-#   include <tkInt.h>
-
-#else  /* ! WIN32 */
-#   define EXPORT(a,b) a b
-#endif /* WIN32 */
-
-#ifdef INLINE
-#undef INLINE
-#endif
-#ifdef __GNUC__
-#    define INLINE inline
-#else
-#    if defined(_MSC_VER)
-#      define INLINE __inline
-#    else
-#      define INLINE
-#    endif
 #endif
 
+#if defined(WIN32) || defined(MAC_TCL)
+/* XSync call defined in the internals for some reason */
+#   ifndef XSync
+#      define XSync(display, bool) {display->request++;}
+#   endif
+#endif /* defn of XSync */
+
 #ifndef NORMAL_BG
-#      ifdef _WIN32
-#              define NORMAL_BG        "SystemButtonFace"
-#              define ACTIVE_BG        NORMAL_BG
-#              define SELECT_BG        "SystemHighlight"
-#              define DISABLED         "SystemDisabledText"
-#              define HIGHLIGHT        "SystemWindowFrame"
-#              define DEF_TABLE_FONT   "{MS Sans Serif} 8"
-#      else
-#              define NORMAL_BG        "#d9d9d9"
-#              define ACTIVE_BG        "#fcfcfc"
-#              define SELECT_BG        "#c3c3c3"
-#              define DISABLED         "#a3a3a3"
-#              define HIGHLIGHT        "Black"
-#              define DEF_TABLE_FONT   "Helvetica -12"
-#      endif
-#endif
+#   ifdef WIN32
+#      define NORMAL_BG        "SystemButtonFace"
+#      define ACTIVE_BG        NORMAL_BG
+#      define SELECT_BG        "SystemHighlight"
+#      define SELECT_FG        "SystemHighlightText"
+#      define DISABLED         "SystemDisabledText"
+#      define HIGHLIGHT        "SystemWindowFrame"
+#      define DEF_TABLE_FONT   "{MS Sans Serif} 8"
+#   elif defined(MAC_TCL)
+#      define NORMAL_BG        "systemWindowBody"
+#      define ACTIVE_BG        "#ececec"
+#      define SELECT_BG        "systemHighlight"
+#      define SELECT_FG        "systemHighlightText"
+#      define DISABLED         "#a3a3a3"
+#      define HIGHLIGHT        "Black"
+#      define DEF_TABLE_FONT   "Helvetica 12"
+#   else
+#      define NORMAL_BG        "#d9d9d9"
+#      define ACTIVE_BG        "#fcfcfc"
+#      define SELECT_BG        "#c3c3c3"
+#      define SELECT_FG        "Black"
+#      define DISABLED         "#a3a3a3"
+#      define HIGHLIGHT        "Black"
+#      define DEF_TABLE_FONT   "Helvetica -12"
+#   endif
+#endif /* NORMAL_BG */
 
 #define MAX(A,B)       (((A)>(B))?(A):(B))
 #define MIN(A,B)       (((A)>(B))?(B):(A))
+#define BETWEEN(val,min,max)   ( ((val)<(min)) ? (min) : \
+                               ( ((val)>(max)) ? (max) : (val) ) )
+#define CONSTRAIN(val,min,max) if ((val) < (min)) { (val) = (min); } \
+                               else if ((val) > (max)) { (val) = (max); }
+#define STREQ(s1, s2)  (strcmp((s1), (s2)) == 0)
 #define ARSIZE(A)      (sizeof(A)/sizeof(*A))
-#define INDEX_BUFSIZE  64              /* max size of buffer for indices */
+#define INDEX_BUFSIZE  32              /* max size of buffer for indices */
 #define TEST_KEY       "#TEST KEY#"    /* index for testing array existence */
 
 /*
  * ACTIVE_DISABLED:    Non-zero means the active cell is -state disabled
  * OVER_BORDER:                Non-zero means we are over a table cell border
  * REDRAW_ON_MAP:      Forces a redraw on the unmap
+ * AVOID_SPANS:                prevent cell spans from being used
  *
  * FIX - consider adding UPDATE_SCROLLBAR a la entry
  */
 #define ACTIVE_DISABLED                (1L<<10)
 #define OVER_BORDER            (1L<<11)
 #define REDRAW_ON_MAP          (1L<<12)
+#define AVOID_SPANS            (1L<<13)
 
 /* Flags for TableInvalidate && TableRedraw */
 #define ROW            (1L<<0)
 #define COL            (1L<<1)
-#define CELL           (ROW|COL)
+#define CELL           (1L<<2)
+
+#define CELL_BAD       (1<<0)
+#define CELL_OK                (1<<1)
+#define CELL_SPAN      (1<<2)
+#define CELL_HIDDEN    (1<<3)
+#define CELL_VIEWABLE  (CELL_OK|CELL_SPAN)
+
 #define INV_FILL       (1L<<3) /* use for Redraw when the affected
                                 * row/col will affect neighbors */
 #define INV_FORCE      (1L<<4)
 #define INV_HIGHLIGHT  (1L<<5)
+#define INV_NO_ERR_MSG (1L<<5) /* Don't leave an error message */
+
+/* These alter how the selection set/clear commands behave */
+#define SEL_ROW                (1<<0)
+#define SEL_COL                (1<<1)
+#define SEL_BOTH       (1<<2)
+#define SEL_CELL       (1<<3)
+#define SEL_NONE       (1<<4)
 
 /*
  * Definitions for tablePtr->dataSource, by bit
 #define        DATA_ARRAY      (1<<2)
 #define DATA_COMMAND   (1<<3)
 
+/*
+ * Definitions for configuring -borderwidth
+ */
+#define BD_TABLE       0
+#define BD_TABLE_TAG   (1<<1)
+#define BD_TABLE_WIN   (1<<2)
+
+/*
+ * Possible state values for tags
+ */
 typedef enum {
-  STATE_UNUSED, STATE_UNKNOWN, STATE_HIDDEN,
-  STATE_NORMAL, STATE_DISABLED, STATE_ACTIVE,
-  STATE_LAST
+    STATE_UNUSED, STATE_UNKNOWN, STATE_HIDDEN,
+    STATE_NORMAL, STATE_DISABLED, STATE_ACTIVE, STATE_LAST
 } TableState;
 
-/* The tag structure */
+/*
+ * Structure for use in parsing table commands/values.
+ * Accessor functions defined in tkTableUtil.c
+ */
 typedef struct {
-  Tk_3DBorder  bg;             /* background color */
-  Tk_3DBorder  fg;             /* foreground color */
-  int          relief;         /* relief type */
-  Tk_Font      tkfont;         /* Information about text font, or NULL. */
-  Tk_Anchor    anchor;         /* default anchor point */
-  char *       imageStr;       /* name of image */
-  Tk_Image     image;          /* actual pointer to image, if any */
-  TableState   state;          /* state of the cell */
-  Tk_Justify   justify;        /* justification of text in the cell */
-  int          multiline;      /* wrapping style of multiline text */
-  int          wrap;           /* wrapping style of multiline text */
-  int          showtext;       /* whether to display text over image */
+  char *name;          /* name of the command/value */
+  int value;           /* >0 because 0 represents an error or proc */
+} Cmd_Struct;
+
+/*
+ * The tag structure
+ */
+typedef struct {
+    Tk_3DBorder        bg;             /* background color */
+    Tk_3DBorder        fg;             /* foreground color */
+
+    char *     borderStr;      /* border style */
+    int                borders;        /* number of borders specified (1, 2 or 4) */
+    int                bd[4];          /* cell border width */
+
+    int                relief;         /* relief type */
+    Tk_Font    tkfont;         /* Information about text font, or NULL. */
+    Tk_Anchor  anchor;         /* default anchor point */
+    char *     imageStr;       /* name of image */
+    Tk_Image   image;          /* actual pointer to image, if any */
+    TableState state;          /* state of the cell */
+    Tk_Justify justify;        /* justification of text in the cell */
+    int                multiline;      /* wrapping style of multiline text */
+    int                wrap;           /* wrapping style of multiline text */
+    int                showtext;       /* whether to display text over image */
 } TableTag;
 
 /*  The widget structure for the table Widget */
 
 typedef struct {
-  /* basic information about the window and the interpreter */
-  Tk_Window tkwin;
-  Display *display;
-  Tcl_Interp *interp;
-  Tcl_Command widgetCmd;       /* Token for entry's widget command. */
-  /* Configurable Options */
-  int autoClear;
-  char *selectMode;            /* single, browse, multiple, or extended */
-  int selectType;              /* row, col, both, or cell */
-  int selectTitles;            /* whether to do automatic title selection */
-  int rows, cols;              /* number of rows and columns */
-  int defRowHeight;            /* default row height in chars (positive)
+    /* basic information about the window and the interpreter */
+    Tk_Window tkwin;
+    Display *display;
+    Tcl_Interp *interp;
+    Tcl_Command widgetCmd;     /* Token for entry's widget command. */
+
+    /*
+     * Configurable Options
+     */
+    int autoClear;
+    char *selectMode;          /* single, browse, multiple, or extended */
+    int selectType;            /* row, col, both, or cell */
+    int selectTitles;          /* whether to do automatic title selection */
+    int rows, cols;            /* number of rows and columns */
+    int defRowHeight;          /* default row height in chars (positive)
                                 * or pixels (negative) */
-  int defColWidth;             /* default column width in chars (positive)
+    int defColWidth;           /* default column width in chars (positive)
                                 * or pixels (negative) */
-  int maxReqCols;              /* the requested # cols to display */
-  int maxReqRows;              /* the requested # rows to display */
-  int maxReqWidth;             /* the maximum requested width in pixels */
-  int maxReqHeight;            /* the maximum requested height in pixels */
-  char *arrayVar;              /* name of traced array variable */
-  char *rowSep;                        /* separator string to place between
+    int maxReqCols;            /* the requested # cols to display */
+    int maxReqRows;            /* the requested # rows to display */
+    int maxReqWidth;           /* the maximum requested width in pixels */
+    int maxReqHeight;          /* the maximum requested height in pixels */
+    char *arrayVar;            /* name of traced array variable */
+    char *rowSep;              /* separator string to place between
                                 * rows when getting selection */
-  char *colSep;                        /* separator string to place between
+    char *colSep;              /* separator string to place between
                                 * cols when getting selection */
-  int borderWidth;             /* internal borderwidth */
-  TableTag defaultTag;         /* the default tag colors/fonts etc */
-  char *yScrollCmd;            /* the y-scroll command */
-  char *xScrollCmd;            /* the x-scroll command */
-  char *browseCmd;             /* the command that is called when the
+    TableTag defaultTag;       /* the default tag colors/fonts etc */
+    char *yScrollCmd;          /* the y-scroll command */
+    char *xScrollCmd;          /* the x-scroll command */
+    char *browseCmd;           /* the command that is called when the
                                 * active cell changes */
-  int caching;                 /* whether to cache values of table */
-  char *command;               /* A command to eval when get/set occurs
+    int caching;               /* whether to cache values of table */
+    char *command;             /* A command to eval when get/set occurs
                                 * for table values */
-  int useCmd;                  /* Signals whether to use command or the
+    int useCmd;                        /* Signals whether to use command or the
                                 * array variable, will be 0 if command errs */
-  char *selCmd;                        /* the command that is called to when a
+    char *selCmd;              /* the command that is called to when a
                                 * [selection get] call occurs for a table */
-  char *valCmd;                        /* Command prefix to use when invoking
+    char *valCmd;              /* Command prefix to use when invoking
                                 * validate command.  NULL means don't
                                 * invoke commands.  Malloc'ed. */
-  int validate;                        /* Non-zero means try to validate */
-  Tk_3DBorder insertBg;                /* the cursor color */
-  Tk_Cursor cursor;            /* the regular mouse pointer */
-  Tk_Cursor bdcursor;          /* the mouse pointer when over borders */
-  int exportSelection;         /* Non-zero means tie internal table
+    int validate;              /* Non-zero means try to validate */
+    Tk_3DBorder insertBg;      /* the cursor color */
+    Tk_Cursor cursor;          /* the regular mouse pointer */
+    Tk_Cursor bdcursor;                /* the mouse pointer when over borders */
+    int exportSelection;       /* Non-zero means tie internal table
                                 * to X selection. */
-  TableState state;            /* Normal or disabled.  Table is read-only
+    TableState state;          /* Normal or disabled.  Table is read-only
                                 * when disabled. */
-  int insertWidth;             /* Total width of insert cursor. */
-  int insertBorderWidth;       /* Width of 3-D border around insert cursor. */
-  int insertOnTime;            /* Number of milliseconds cursor should spend
+    int insertWidth;           /* Total width of insert cursor. */
+    int insertBorderWidth;     /* Width of 3-D border around insert cursor. */
+    int insertOnTime;          /* Number of milliseconds cursor should spend
                                 * in "on" state for each blink. */
-  int insertOffTime;           /* Number of milliseconds cursor should spend
+    int insertOffTime;         /* Number of milliseconds cursor should spend
                                 * in "off" state for each blink. */
-  int invertSelected;           /* Whether to draw selected cells swapping
-                                   foreground and background */
-  int colStretch;              /* The way to stretch columns if the window
-                                  is too large */
-  int rowStretch;              /* The way to stretch rows if the window is
-                                  too large */
-  int colOffset;               /* X index of leftmost col in the display */
-  int rowOffset;               /* Y index of topmost row in the display */
-  int drawMode;                        /* The mode to use when redrawing */
-  int flashMode;               /* Specifies whether flashing is enabled */
-  int flashTime;               /* The number of ms to flash a cell for */
-  int resize;                  /* -resizeborders option for interactive
+    int invertSelected;                /* Whether to draw selected cells swapping
+                                * foreground and background */
+    int colStretch;            /* The way to stretch columns if the window
+                                * is too large */
+    int rowStretch;            /* The way to stretch rows if the window is
+                                * too large */
+    int colOffset;             /* X index of leftmost col in the display */
+    int rowOffset;             /* Y index of topmost row in the display */
+    int drawMode;              /* The mode to use when redrawing */
+    int flashMode;             /* Specifies whether flashing is enabled */
+    int flashTime;             /* The number of ms to flash a cell for */
+    int resize;                        /* -resizeborders option for interactive
                                 * resizing of borders */
-  char *rowTagCmd, *colTagCmd; /* script to eval for getting row/tag cmd */
-  int highlightWidth;          /* Width in pixels of highlight to draw
+    int sparse;                        /* Whether to use "sparse" arrays by
+                                * deleting empty array elements (default) */
+    char *rowTagCmd, *colTagCmd;/* script to eval for getting row/tag cmd */
+    int highlightWidth;                /* Width in pixels of highlight to draw
                                 * around widget when it has the focus.
                                 * <= 0 means don't draw a highlight. */
-  XColor *highlightBgColorPtr; /* Color for drawing traversal highlight
+    XColor *highlightBgColorPtr;/* Color for drawing traversal highlight
                                 * area when highlight is off. */
-  XColor *highlightColorPtr;   /* Color for drawing traversal highlight. */
-  char *takeFocus;             /* Used only in Tcl to check if this
+    XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
+    char *takeFocus;           /* Used only in Tcl to check if this
                                 * widget will accept focus */
-  int padX, padY;              /* Extra space around text (pixels to leave
+    int padX, padY;            /* Extra space around text (pixels to leave
                                 * on each side).  Ignored for bitmaps and
                                 * images. */
-
-  /* Cached Information */
-  int titleRows, titleCols;    /* the number of rows|cols to use as a title */
-  /* these are kept in real coords */
-  int topRow, leftCol;         /* The topleft cell to display excluding the
+    int ipadX, ipadY;          /* Space to leave empty around cell borders.
+                                * This differs from pad* in that it is always
+                                * present for the cell (except windows). */
+
+    /*
+     * Cached Information
+     */
+    int titleRows, titleCols;  /* the number of rows|cols to use as a title */
+    /* these are kept in real coords */
+    int topRow, leftCol;       /* The topleft cell to display excluding the
                                 * fixed title rows.  This is just the
                                 * config request.  The actual cell used may
                                 * be different to keep the screen full */
-  int anchorRow, anchorCol;    /* the row,col of the anchor cell */
-  int activeRow, activeCol;    /* the row,col of the active cell */
-  int oldTopRow, oldLeftCol;   /* cached by TableAdjustParams */
-  int oldActRow, oldActCol;    /* cached by TableAdjustParams */
-  int icursor;                 /* The index of the insertion cursor in the
-                                  active cell */
-  int flags;                   /* An or'ed combination of flags concerning
-                                  redraw/cursor etc. */
-  int dataSource;              /* where our data comes from:
+    int anchorRow, anchorCol;  /* the row,col of the anchor cell */
+    int activeRow, activeCol;  /* the row,col of the active cell */
+    int oldTopRow, oldLeftCol; /* cached by TableAdjustParams */
+    int oldActRow, oldActCol;  /* cached by TableAdjustParams */
+    int icursor;               /* The index of the insertion cursor in the
+                                * active cell */
+    int flags;                 /* An or'ed combination of flags concerning
+                                * redraw/cursor etc. */
+    int dataSource;            /* where our data comes from:
                                 * DATA_{NONE,CACHE,ARRAY,COMMAND} */
-  int maxWidth, maxHeight;     /* max width|height required in pixels */
-  int charWidth, charHeight;   /* size of a character in the default font */
-  int *colPixels;              /* Array of the pixel width of each column */
-  int *rowPixels;              /* Array of the pixel height of each row */
-  int *colStarts, *rowStarts;  /* Array of start pixels for rows|columns */
-  int scanMarkX, scanMarkY;    /* Used by "scan" and "border" to mark */
-  int scanMarkRow, scanMarkCol;        /* necessary information for dragto */
-  /* values in these are kept in user coords */
-  Tcl_HashTable *cache;                /* value cache */
-  /* colWidths and rowHeights are indexed from 0, so always adjust numbers
-     by the appropriate *Offset factor */
-  Tcl_HashTable *colWidths;    /* hash table of non default column widths */
-  Tcl_HashTable *rowHeights;   /* hash table of non default row heights */
-  Tcl_HashTable *tagTable;     /* table for style tags */
-  Tcl_HashTable *winTable;     /* table for embedded windows */
-  Tcl_HashTable *rowStyles;    /* table for row styles */
-  Tcl_HashTable *colStyles;    /* table for col styles */
-  Tcl_HashTable *cellStyles;   /* table for cell styles */
-  Tcl_HashTable *flashCells;   /* table of flashing cells */
-  Tcl_HashTable *selCells;     /* table of selected cells */
-  Tcl_TimerToken cursorTimer;  /* timer token for the cursor blinking */
-  Tcl_TimerToken flashTimer;   /* timer token for the cell flashing */
-  char *activeBuf;             /* buffer where the selection is kept
-                                  for editing the active cell */
-  Tk_TextLayout activeLayout;  /* cache of active layout */
-  int activeX, activeY;                /* cache offset of active layout in cell */
-  /* The invalid rectangle if there is an update pending */
-  int invalidX, invalidY, invalidWidth, invalidHeight;
-  int seen[4];                 /* see TableUndisplay */
+    int maxWidth, maxHeight;   /* max width|height required in pixels */
+    int charWidth, charHeight; /* size of a character in the default font */
+    int *colPixels, *rowPixels;        /* Array of the pixel widths/heights */
+    int *colStarts, *rowStarts;        /* Array of start pixels for rows|columns */
+    int scanMarkX, scanMarkY;  /* Used by "scan" and "border" to mark */
+    int scanMarkRow, scanMarkCol;/* necessary information for dragto */
+    /* values in these are kept in user coords */
+    Tcl_HashTable *cache;      /* value cache */
+
+    /*
+     * colWidths and rowHeights are indexed from 0, so always adjust numbers
+     * by the appropriate *Offset factor
+     */
+    Tcl_HashTable *colWidths;  /* hash table of non default column widths */
+    Tcl_HashTable *rowHeights; /* hash table of non default row heights */
+    Tcl_HashTable *spanTbl;    /* table for spans */
+    Tcl_HashTable *spanAffTbl; /* table for cells affected by spans */
+    Tcl_HashTable *tagTable;   /* table for style tags */
+    Tcl_HashTable *winTable;   /* table for embedded windows */
+    Tcl_HashTable *rowStyles;  /* table for row styles */
+    Tcl_HashTable *colStyles;  /* table for col styles */
+    Tcl_HashTable *cellStyles; /* table for cell styles */
+    Tcl_HashTable *flashCells; /* table of flashing cells */
+    Tcl_HashTable *selCells;   /* table of selected cells */
+    Tcl_TimerToken cursorTimer;        /* timer token for the cursor blinking */
+    Tcl_TimerToken flashTimer; /* timer token for the cell flashing */
+    char *activeBuf;           /* buffer where the selection is kept
+                                * for editing the active cell */
+    char **tagPrioNames;       /* list of tag names in priority order */
+    TableTag **tagPrios;       /* list of tag pointers in priority order */
+    TableTag *activeTagPtr;    /* cache of active composite tag */
+    int activeX, activeY;      /* cache offset of active layout in cell */
+    int tagPrioSize;           /* size of tagPrios list */
+    int tagPrioMax;            /* max allocated size of tagPrios list */
+
+    /* The invalid rectangle if there is an update pending */
+    int invalidX, invalidY, invalidWidth, invalidHeight;
+    int seen[4];                       /* see TableUndisplay */
+
+#ifdef POSTSCRIPT
+    /* Pointer to information used for generating Postscript for the canvas.
+     * NULL means no Postscript is currently being generated. */
+    struct TkPostscriptInfo *psInfoPtr;
+#endif
+
+#ifdef PROCS
+    Tcl_HashTable *inProc;     /* cells where proc is being evaled */
+    int showProcs;             /* whether to show embedded proc (1) or
+                                * its calculated value (0) */
+    int hasProcs;              /* whether table has embedded procs or not */
+#endif
 } Table;
 
 /*
@@ -304,95 +393,222 @@ typedef struct {
  */
 
 typedef struct TableEmbWindow {
-  Table *tablePtr;             /* Information about the overall table
+    Table *tablePtr;           /* Information about the overall table
                                 * widget. */
-  Tk_Window tkwin;             /* Window for this segment.  NULL
-                                * means that the window hasn't
-                                * been created yet. */
-  Tcl_HashEntry *hPtr;         /* entry into winTable */
-  Tk_3DBorder bg;              /* background color */
-  char *create;                        /* Script to create window on-demand.
+    Tk_Window tkwin;           /* Window for this segment.  NULL means that
+                                * the window hasn't been created yet. */
+    Tcl_HashEntry *hPtr;       /* entry into winTable */
+    char *create;              /* Script to create window on-demand.
                                 * NULL means no such script.
                                 * Malloc-ed. */
-  int relief;                  /* relief type */
-  int sticky;                  /* How to align window in space */
-  int padX, padY;              /* Padding to leave around each side
+    Tk_3DBorder bg;            /* background color */
+
+    char *borderStr;           /* border style */
+    int borders;               /* number of borders specified (1, 2 or 4) */
+    int bd[4];                 /* border width for cell around window */
+
+    int relief;                        /* relief type */
+    int sticky;                        /* How to align window in space */
+    int padX, padY;            /* Padding to leave around each side
                                 * of window, in pixels. */
-  int displayed;               /* Non-zero means that the window
-                                * has been displayed on the screen
-                                * recently. */
+    int displayed;             /* Non-zero means that the window has been
+                                * displayed on the screen recently. */
 } TableEmbWindow;
 
+extern Tk_ConfigSpec tableSpecs[];
+
 extern void    EmbWinDisplay _ANSI_ARGS_((Table *tablePtr, Drawable window,
-                                          TableEmbWindow *ewPtr,
-                                          TableTag *tagPtr, int x, int y,
-                                          int width, int height));
+                       TableEmbWindow *ewPtr, TableTag *tagPtr,
+                       int x, int y, int width, int height));
 extern void    EmbWinUnmap _ANSI_ARGS_((register Table *tablePtr,
-                                        int rlo, int rhi,
-                                        int clo, int chi));
+                       int rlo, int rhi, int clo, int chi));
 extern void    EmbWinDelete _ANSI_ARGS_((register Table *tablePtr,
-                                          TableEmbWindow *ewPtr));
-extern int     TableWindowCmd _ANSI_ARGS_((Table *tablePtr,
-                                           Tcl_Interp *interp,
-                                           int argc, char *argv[]));
+                       TableEmbWindow *ewPtr));
+extern int     Table_WinMove _ANSI_ARGS_((register Table *tablePtr,
+                       char *CONST srcPtr, char *CONST destPtr, int flags));
+extern int     Table_WinDelete _ANSI_ARGS_((register Table *tablePtr,
+                       char *CONST idxPtr));
+extern int     Table_WindowCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     TableValidateChange _ANSI_ARGS_((Table *tablePtr, int r,
+                       int c, char *oldVal, char *newVal, int idx));
+extern void    TableLostSelection _ANSI_ARGS_((ClientData clientData));
+extern void    TableSetActiveIndex _ANSI_ARGS_((register Table *tablePtr));
+
+/*
+ * HEADERS IN tkTableCmds.c
+ */
+
+extern int     Table_ActivateCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_AdjustCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_BboxCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_BorderCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_ClearCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_CurselectionCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_CurvalueCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_GetCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_ScanCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_SeeCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_SelAnchorCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_SelClearCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_SelIncludesCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_SelSetCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_ViewCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
 
 /*
- * HEADERS IN TKTABLETAG
+ * HEADERS IN tkTableEdit.c
  */
 
-extern TableTag *TableNewTag _ANSI_ARGS_((void));
-extern void    TableMergeTag _ANSI_ARGS_((TableTag *baseTag,
-                                          TableTag *addTag));
+extern int     Table_EditCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern void    TableDeleteChars _ANSI_ARGS_((register Table *tablePtr,
+                       int idx, int count));
+extern void    TableInsertChars _ANSI_ARGS_((register Table *tablePtr,
+                       int idx, char *string));
+
+/*
+ * HEADERS IN tkTableTag.c
+ */
+
+extern TableTag *TableNewTag _ANSI_ARGS_((Table *tablePtr));
+extern void    TableResetTag _ANSI_ARGS_((Table *tablePtr, TableTag *tagPtr));
+extern void    TableMergeTag _ANSI_ARGS_((Table *tablePtr, TableTag *baseTag,
+                       TableTag *addTag));
 extern void    TableInvertTag _ANSI_ARGS_((TableTag *baseTag));
+extern int     TableGetTagBorders _ANSI_ARGS_((TableTag *tagPtr,
+                       int *left, int *right, int *top, int *bottom));
 extern void    TableInitTags _ANSI_ARGS_((Table *tablePtr));
 extern TableTag *FindRowColTag _ANSI_ARGS_((Table *tablePtr,
-                                            int cell, int type));
+                       int cell, int type));
 extern void    TableCleanupTag _ANSI_ARGS_((Table *tablePtr,
-                                            TableTag *tagPtr));
-extern int     TableTagCmd _ANSI_ARGS_((Table *tablePtr, Tcl_Interp *interp,
-                                        int argc, char *argv[]));
+                       TableTag *tagPtr));
+extern int     Table_TagCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
 
 /*
- * HEADERS IN TKTABLECELL
+ * HEADERS IN tkTableUtil.c
  */
 
-extern void    TableCellCoords _ANSI_ARGS_((Table *tablePtr, int row,
-                                            int col, int *rx, int *ry,
-                                            int *rw, int *rh));
+extern int     TableOptionBdSet _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, Tk_Window tkwin,
+                       char *value, char *widgRec, int offset));
+extern char *  TableOptionBdGet _ANSI_ARGS_((ClientData clientData,
+                       Tk_Window tkwin, char *widgRec, int offset,
+                       Tcl_FreeProc **freeProcPtr));
+extern int     TableTagConfigureBd _ANSI_ARGS_((Table *tablePtr,
+                       TableTag *tagPtr, char *oldValue, int nullOK));
+extern int     Cmd_OptionSet _ANSI_ARGS_((ClientData clientData,
+                                          Tcl_Interp *interp,
+                                          Tk_Window unused, char *value,
+                                          char *widgRec, int offset));
+extern char *  Cmd_OptionGet _ANSI_ARGS_((ClientData clientData,
+                                          Tk_Window unused, char *widgRec,
+                                          int offset,
+                                          Tcl_FreeProc **freeProcPtr));
+
+/*
+ * HEADERS IN tkTableCell.c
+ */
+
+extern int     TableTrueCell _ANSI_ARGS_((Table *tablePtr, int row, int col,
+                                          int *trow, int *tcol));
+extern int     TableCellCoords _ANSI_ARGS_((Table *tablePtr, int row,
+                       int col, int *rx, int *ry, int *rw, int *rh));
 extern int     TableCellVCoords _ANSI_ARGS_((Table *tablePtr, int row,
-                                             int col, int *rx, int *ry,
-                                             int *rw, int *rh, int full));
+                       int col, int *rx, int *ry,
+                       int *rw, int *rh, int full));
 extern void    TableWhatCell _ANSI_ARGS_((register Table *tablePtr,
-                                          int x, int y, int *row, int *col));
+                       int x, int y, int *row, int *col));
 extern int     TableAtBorder _ANSI_ARGS_((Table *tablePtr, int x, int y,
-                                          int *row, int *col));
+                       int *row, int *col));
 extern char *  TableGetCellValue _ANSI_ARGS_((Table *tablePtr, int r, int c));
 extern int     TableSetCellValue _ANSI_ARGS_((Table *tablePtr, int r, int c,
-                                              char *value));
-extern char *  TableCellSort _ANSI_ARGS_((Table *tablePtr, char *str));
+                       char *value));
+extern int    TableMoveCellValue _ANSI_ARGS_((Table *tablePtr,
+                       int fromr, int fromc, char *frombuf,
+                       int tor, int toc, char *tobuf, int outOfBounds));
+
 extern int     TableGetIcursor _ANSI_ARGS_((Table *tablePtr, char *arg,
-                                            int *posn));
-extern int     TableGetIndex _ANSI_ARGS_((register Table *tablePtr, char *str,
-                                          int *row_p, int *col_p));
+                       int *posn));
+#define TableGetIcursorObj(tablePtr, objPtr, posnPtr) \
+       TableGetIcursor(tablePtr, Tcl_GetString(objPtr), posnPtr)
+extern int     TableGetIndex _ANSI_ARGS_((register Table *tablePtr,
+                       char *str, int *row_p, int *col_p));
+#define TableGetIndexObj(tablePtr, objPtr, rowPtr, colPtr) \
+       TableGetIndex(tablePtr, Tcl_GetString(objPtr), rowPtr, colPtr)
+extern int     Table_SetCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_HiddenCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern int     Table_SpanCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern void    TableSpanSanCheck _ANSI_ARGS_((register Table *tablePtr));
+
+/*
+ * HEADERS IN TKTABLECELLSORT
+ */
+/*
+ * We keep the old CellSort true because it is used for grabbing
+ * the selection, so we really want them ordered
+ */
+extern char *  TableCellSort _ANSI_ARGS_((Table *tablePtr, char *str));
+#ifdef NO_SORT_CELLS
+#  define TableCellSortObj(interp, objPtr) (objPtr)
+#else
+extern Tcl_Obj*        TableCellSortObj _ANSI_ARGS_((Tcl_Interp *interp,
+                       Tcl_Obj *listObjPtr));
+#endif
+
+/*
+ * HEADERS IN TKTABLEPS
+ */
+
+#ifdef POSTSCRIPT
+extern int     Table_PostscriptCmd _ANSI_ARGS_((ClientData clientData,
+                       Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]));
+extern void    Tcl_DStringAppendAll _ANSI_ARGS_(TCL_VARARGS(Tcl_DString *, arg1));
+#endif
 
 /*
  * HEADERS IN TKTABLE
  */
 
-EXTERN EXPORT(int,Example_Init) _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN int Tktable_Init                _ANSI_ARGS_((Tcl_Interp *interp));
+EXTERN int Tktable_SafeInit    _ANSI_ARGS_((Tcl_Interp *interp));
 
+extern void    TableGetActiveBuf _ANSI_ARGS_((register Table *tablePtr));
 extern void    ExpandPercents _ANSI_ARGS_((Table *tablePtr, char *before,
-                       int r, int c, char *old, char *new, int index,
+                       int r, int c, char *oldVal, char *newVal, int idx,
                        Tcl_DString *dsPtr, int cmdType));
 extern void    TableInvalidate _ANSI_ARGS_((Table *tablePtr, int x, int y,
-                                            int width, int height,
-                                            int force));
+                       int width, int height, int force));
 extern void    TableRefresh _ANSI_ARGS_((register Table *tablePtr,
-                                         int arg1, int arg2, int mode));
+                       int arg1, int arg2, int mode));
+extern void    TableGeometryRequest _ANSI_ARGS_((Table *tablePtr));
+extern void    TableAdjustActive _ANSI_ARGS_((register Table *tablePtr));
+extern void    TableAdjustParams _ANSI_ARGS_((register Table *tablePtr));
+extern void    TableConfigCursor _ANSI_ARGS_((register Table *tablePtr));
+extern void    TableAddFlash _ANSI_ARGS_((Table *tablePtr, int row, int col));
 
-#define TableInvalidateAll(tablePtr, flags)    \
+
+#define TableInvalidateAll(tablePtr, flags) \
        TableInvalidate((tablePtr), 0, 0, Tk_Width((tablePtr)->tkwin),\
-                       Tk_Height((tablePtr)->tkwin), (flags))
+               Tk_Height((tablePtr)->tkwin), (flags))
 
      /*
       * Turn row/col into an index into the table
@@ -408,11 +624,18 @@ extern void       TableRefresh _ANSI_ARGS_((register Table *tablePtr,
      /*
       * Macro for finding the last cell of the table
       */
-#define TableGetLastCell(tablePtr, rowPtr, colPtr)     \
+#define TableGetLastCell(tablePtr, rowPtr, colPtr) \
        TableWhatCell((tablePtr),\
-                     Tk_Width((tablePtr)->tkwin)-(tablePtr)->highlightWidth,\
-                     Tk_Height((tablePtr)->tkwin)-(tablePtr)->highlightWidth,\
-                     (rowPtr), (colPtr))
+               Tk_Width((tablePtr)->tkwin)-(tablePtr)->highlightWidth-1,\
+               Tk_Height((tablePtr)->tkwin)-(tablePtr)->highlightWidth-1,\
+               (rowPtr), (colPtr))
+
+/*
+ * end of header
+ * reset TCL_STORAGE_CLASS to DLLIMPORT.
+ */
+#undef TCL_STORAGE_CLASS
+#define TCL_STORAGE_CLASS DLLIMPORT
 
 #endif /* _TKTABLE_H_ */
 
index f10f035..4083311 100644 (file)
@@ -1,19 +1,28 @@
 # table.tcl --
 #
-# version 1.8, jeff.hobbs@acm.org
+# Version align with tkTable 2.7, jeff.hobbs@acm.org
 # This file defines the default bindings for Tk table widgets
 # and provides procedures that help in implementing those bindings.
 #
 
 #--------------------------------------------------------------------------
-# tkPriv elements used in this file:
+# ::tk::table::Priv elements used in this file:
 #
+# x && y -             Coords in widget
 # afterId -            Token returned by "after" for autoscanning.
 # tablePrev -          The last element to be selected or deselected
 #                      during a selection operation.
+# mouseMoved -         Boolean to indicate whether mouse moved while
+#                      the button was pressed.
 #--------------------------------------------------------------------------
 
-# tkTableClipboardKeysyms --
+namespace eval ::tk::table {
+    # Ensure that a namespace is created for us
+    variable Priv
+    array set Priv { x 0 y 0 afterId {} mouseMoved 0 }
+}
+
+# ::tk::table::ClipboardKeysyms --
 # This procedure is invoked to identify the keys that correspond to
 # the "copy", "cut", and "paste" functions for the clipboard.
 #
 # cut -                Name of the key used for the cut operation.
 # paste -      Name of the key used for the paste operation.
 
-proc tkTableClipboardKeysyms {copy cut paste} {
+proc ::tk::table::ClipboardKeysyms {copy cut paste} {
     bind Table <$copy> {tk_tableCopy %W}
     bind Table <$cut>  {tk_tableCut %W}
     bind Table <$paste>        {tk_tablePaste %W}
 }
+::tk::table::ClipboardKeysyms <Copy> <Cut> <Paste>
 
-## Interactive row resizing, affected by -resizeborders option
+## Interactive cell resizing, affected by -resizeborders option
 ##
 bind Table <3>         {
-    ## You might want to check for row returned if you want to
-    ## restrict the resizing of certain rows
+    ## You might want to check for cell returned if you want to
+    ## restrict the resizing of certain cells
     %W border mark %x %y
 }
 bind Table <B3-Motion> { %W border dragto %x %y }
@@ -42,65 +52,72 @@ bind Table <B3-Motion>      { %W border dragto %x %y }
 
 bind Table <1> {
     if {[winfo exists %W]} {
-       tkTableBeginSelect %W [%W index @%x,%y]
+       ::tk::table::BeginSelect %W [%W index @%x,%y]
        focus %W
     }
+    array set ::tk::table::Priv {x %x y %y}
+    set ::tk::table::Priv(mouseMoved) 0
 }
 bind Table <B1-Motion> {
-    array set tkPriv {x %x y %y}
-    tkTableMotion %W [%W index @%x,%y]
+    # If we already had motion, or we moved more than 1 pixel,
+    # then we start the Motion routine
+    if {
+       $::tk::table::Priv(mouseMoved)
+       || abs(%x-$::tk::table::Priv(x)) > 1
+       || abs(%y-$::tk::table::Priv(y)) > 1
+    } {
+       set ::tk::table::Priv(mouseMoved) 1
+    }
+    if {$::tk::table::Priv(mouseMoved)} {
+       ::tk::table::Motion %W [%W index @%x,%y]
+    }
 }
 bind Table <Double-1> {
     # empty
 }
 bind Table <ButtonRelease-1> {
     if {[winfo exists %W]} {
-       tkCancelRepeat
+       ::tk::table::CancelRepeat
        %W activate @%x,%y
     }
 }
 
-bind Table <Shift-1>   {tkTableBeginExtend %W [%W index @%x,%y]}
-bind Table <Control-1> {tkTableBeginToggle %W [%W index @%x,%y]}
-bind Table <B1-Enter>  {tkCancelRepeat}
+bind Table <Shift-1>   {::tk::table::BeginExtend %W [%W index @%x,%y]}
+bind Table <Control-1> {::tk::table::BeginToggle %W [%W index @%x,%y]}
+bind Table <B1-Enter>  {::tk::table::CancelRepeat}
 bind Table <B1-Leave>  {
-    array set tkPriv {x %x y %y}
-    tkTableAutoScan %W
+    array set ::tk::table::Priv {x %x y %y}
+    ::tk::table::AutoScan %W
 }
 bind Table <2> {
     %W scan mark %x %y
-    array set tkPriv {x %x y %y}
-    set tkPriv(mouseMoved) 0
+    array set ::tk::table::Priv {x %x y %y}
+    set ::tk::table::Priv(mouseMoved) 0
 }
 bind Table <B2-Motion> {
-    if {(%x != $tkPriv(x)) || (%y != $tkPriv(y))} { set tkPriv(mouseMoved) 1 }
-    if $tkPriv(mouseMoved) { %W scan dragto %x %y }
+    if {(%x != $::tk::table::Priv(x)) || (%y != $::tk::table::Priv(y))} {
+       set ::tk::table::Priv(mouseMoved) 1
+    }
+    if {$::tk::table::Priv(mouseMoved)} { %W scan dragto %x %y }
 }
 bind Table <ButtonRelease-2> {
-    if {!$tkPriv(mouseMoved)} { tk_tablePaste %W [%W index @%x,%y] }
+    if {!$::tk::table::Priv(mouseMoved)} { tk_tablePaste %W [%W index @%x,%y] }
 }
 
 ## Key events
 
-if {[string comp {} [info command event]]} {
-    tkTableClipboardKeysyms <Copy> <Cut> <Paste>
-} else {
-    tkTableClipboardKeysyms Control-c Control-x Control-v
-}
-
-bind Table <Any-Tab> {
-    # empty to allow Tk focus movement
-}
 # This forces a cell commit if an active cell exists
-# Remove this if you don't want cell commit to occur
-# on every FocusOut
-bind Table <FocusOut> {
+bind Table <<Table_Commit>> {
     catch {%W activate active}
 }
-bind Table <Shift-Up>          {tkTableExtendSelect %W -1  0}
-bind Table <Shift-Down>                {tkTableExtendSelect %W  1  0}
-bind Table <Shift-Left>                {tkTableExtendSelect %W  0 -1}
-bind Table <Shift-Right>       {tkTableExtendSelect %W  0  1}
+# Remove this if you don't want cell commit to occur on every
+# Leave of the table.  Another possible choice is <FocusOut>.
+event add <<Table_Commit>> <Leave>
+
+bind Table <Shift-Up>          {::tk::table::ExtendSelect %W -1  0}
+bind Table <Shift-Down>                {::tk::table::ExtendSelect %W  1  0}
+bind Table <Shift-Left>                {::tk::table::ExtendSelect %W  0 -1}
+bind Table <Shift-Right>       {::tk::table::ExtendSelect %W  0  1}
 bind Table <Prior>             {%W yview scroll -1 pages; %W activate @0,0}
 bind Table <Next>              {%W yview scroll  1 pages; %W activate @0,0}
 bind Table <Control-Prior>     {%W xview scroll -1 pages}
@@ -119,44 +136,128 @@ bind Table <Control-End> {
     %W selection set active
     %W see active
 }
-bind Table <Shift-Control-Home>        {tkTableDataExtend %W origin}
-bind Table <Shift-Control-End> {tkTableDataExtend %W end}
-bind Table <Select>            {tkTableBeginSelect %W [%W index active]}
-bind Table <Shift-Select>      {tkTableBeginExtend %W [%W index active]}
-bind Table <Control-slash>     {tkTableSelectAll %W}
+bind Table <Shift-Control-Home>        {::tk::table::DataExtend %W origin}
+bind Table <Shift-Control-End> {::tk::table::DataExtend %W end}
+bind Table <Select>            {::tk::table::BeginSelect %W [%W index active]}
+bind Table <Shift-Select>      {::tk::table::BeginExtend %W [%W index active]}
+bind Table <Control-slash>     {::tk::table::SelectAll %W}
 bind Table <Control-backslash> {
     if {[string match browse [%W cget -selectmode]]} {%W selection clear all}
 }
-bind Table <Up>                        {tkTableMoveCell %W -1  0}
-bind Table <Down>              {tkTableMoveCell %W  1  0}
-bind Table <Left>              {tkTableMoveCell %W  0 -1}
-bind Table <Right>             {tkTableMoveCell %W  0  1}
-bind Table <Any-KeyPress> {
-    if {[string compare {} %A]} { %W insert active insert %A }
-}
-bind Table <BackSpace> {
-    set tkPriv(junk) [%W icursor]
-    if {[string compare {} $tkPriv(junk)] && $tkPriv(junk)} {
-       %W delete active [expr {$tkPriv(junk)-1}]
-    }
-}
+bind Table <Up>                        {::tk::table::MoveCell %W -1  0}
+bind Table <Down>              {::tk::table::MoveCell %W  1  0}
+bind Table <Left>              {::tk::table::MoveCell %W  0 -1}
+bind Table <Right>             {::tk::table::MoveCell %W  0  1}
+bind Table <KeyPress>          {::tk::table::Insert %W %A}
+bind Table <BackSpace>         {::tk::table::BackSpace %W}
 bind Table <Delete>            {%W delete active insert}
 bind Table <Escape>            {%W reread}
 
-#bind Table <Return>           {tkTableMoveCell %W 1 0}
-bind Table <Return> {
-    %W insert active insert "\n"
-}
+#bind Table <Return>           {::tk::table::MoveCell %W 1 0}
+bind Table <Return>            {::tk::table::Insert %W "\n"}
 
 bind Table <Control-Left>      {%W icursor [expr {[%W icursor]-1}]}
 bind Table <Control-Right>     {%W icursor [expr {[%W icursor]+1}]}
 bind Table <Control-e>         {%W icursor end}
 bind Table <Control-a>         {%W icursor 0}
 bind Table <Control-k>         {%W delete active insert end}
-bind Table <Control-equal>     {tkTableChangeWidth %W active  1}
-bind Table <Control-minus>     {tkTableChangeWidth %W active -1}
+bind Table <Control-equal>     {::tk::table::ChangeWidth %W active  1}
+bind Table <Control-minus>     {::tk::table::ChangeWidth %W active -1}
+
+# Ignore all Alt, Meta, and Control keypresses unless explicitly bound.
+# Otherwise, if a widget binding for one of these is defined, the
+# <KeyPress> class binding will also fire and insert the character,
+# which is wrong.  Ditto for Tab.
+
+bind Table <Alt-KeyPress>      {# nothing}
+bind Table <Meta-KeyPress>     {# nothing}
+bind Table <Control-KeyPress>  {# nothing}
+bind Table <Any-Tab>           {# nothing}
+if {[string match "macintosh" $tcl_platform(platform)]} {
+    bind Table <Command-KeyPress> {# nothing}
+}
+
+# ::tk::table::GetSelection --
+#   This tries to obtain the default selection.  On Unix, we first try
+#   and get a UTF8_STRING, a type supported by modern Unix apps for
+#   passing Unicode data safely.  We fall back on the default STRING
+#   type otherwise.  On Windows, only the STRING type is necessary.
+# Arguments:
+#   w  The widget for which the selection will be retrieved.
+#      Important for the -displayof property.
+#   sel        The source of the selection (PRIMARY or CLIPBOARD)
+# Results:
+#   Returns the selection, or an error if none could be found
+#
+if {[string equal $tcl_platform(platform) "unix"]} {
+    proc ::tk::table::GetSelection {w {sel PRIMARY}} {
+       if {[catch {selection get -displayof $w -selection $sel \
+               -type UTF8_STRING} txt] \
+               && [catch {selection get -displayof $w -selection $sel} txt]} {
+           return -code error "could not find default selection"
+       } else {
+           return $txt
+       }
+    }
+} else {
+    proc ::tk::table::GetSelection {w {sel PRIMARY}} {
+       if {[catch {selection get -displayof $w -selection $sel} txt]} {
+           return -code error "could not find default selection"
+       } else {
+           return $txt
+       }
+    }
+}
+
+# ::tk::table::CancelRepeat --
+# A copy of tkCancelRepeat, just in case it's not available or changes.
+# This procedure is invoked to cancel an auto-repeat action described
+# by ::tk::table::Priv(afterId).  It's used by several widgets to auto-scroll
+# the widget when the mouse is dragged out of the widget with a
+# button pressed.
+#
+# Arguments:
+# None.
+
+proc ::tk::table::CancelRepeat {} {
+    variable Priv
+    after cancel $Priv(afterId)
+    set Priv(afterId) {}
+}
 
-# tkTableBeginSelect --
+# ::tk::table::Insert --
+#
+#   Insert into the active cell
+#
+# Arguments:
+#   w  - the table widget
+#   s  - the string to insert
+# Results:
+#   Returns nothing
+#
+proc ::tk::table::Insert {w s} {
+    if {[string compare $s {}]} {
+       $w insert active insert $s
+    }
+}
+
+# ::tk::table::BackSpace --
+#
+#   BackSpace in the current cell
+#
+# Arguments:
+#   w  - the table widget
+# Results:
+#   Returns nothing
+#
+proc ::tk::table::BackSpace {w} {
+    set cur [$w icursor]
+    if {[string compare {} $cur] && $cur} {
+       $w delete active [expr {$cur-1}]
+    }
+}
+
+# ::tk::table::BeginSelect --
 #
 # This procedure is typically invoked on button-1 presses. It begins
 # the process of making a selection in the table. Its exact behavior
@@ -168,8 +269,8 @@ bind Table <Control-minus>  {tkTableChangeWidth %W active -1}
 # el   - The element for the selection operation (typically the
 #      one under the pointer).  Must be in row,col form.
 
-proc tkTableBeginSelect {w el} {
-    global tkPriv
+proc ::tk::table::BeginSelect {w el} {
+    variable Priv
     if {[scan $el %d,%d r c] != 2} return
     switch [$w cget -selectmode] {
        multiple {
@@ -194,7 +295,7 @@ proc tkTableBeginSelect {w el} {
                set inc $el
                set el2 $el
            }
-           if [$w selection includes $inc] {
+           if {[$w selection includes $inc]} {
                $w selection clear $el $el2
            } else {
                $w selection set $el $el2
@@ -206,7 +307,8 @@ proc tkTableBeginSelect {w el} {
                if {$r < [$w cget -titlerows]+[$w cget -roworigin]} {
                    ## We're in a column header
                    if {$c < [$w cget -titlecols]+[$w cget -colorigin]} {
-                       $w selection set origin end
+                       ## We're in the topleft title area
+                       $w selection set $el end
                    } else {
                        $w selection set $el [$w index end row],$c
                    }
@@ -218,20 +320,20 @@ proc tkTableBeginSelect {w el} {
                $w selection set $el
            }
            $w selection anchor $el
-           set tkPriv(tablePrev) $el
+           set Priv(tablePrev) $el
        }
        default {
            if {![$w tag includes title $el]} {
                $w selection clear all
                $w selection set $el
-               set tkPriv(tablePrev) $el
+               set Priv(tablePrev) $el
            }
            $w selection anchor $el
        }
     }
 }
 
-# tkTableMotion --
+# ::tk::table::Motion --
 #
 # This procedure is called to process mouse motion events while
 # button 1 is down. It may move or extend the selection, depending
@@ -241,21 +343,21 @@ proc tkTableBeginSelect {w el} {
 # w    - The table widget.
 # el   - The element under the pointer (must be in row,col form).
 
-proc tkTableMotion {w el} {
-    global tkPriv
-    if {![info exists tkPriv(tablePrev)]} {
-       set tkPriv(tablePrev) $el
+proc ::tk::table::Motion {w el} {
+    variable Priv
+    if {![info exists Priv(tablePrev)]} {
+       set Priv(tablePrev) $el
        return
     }
-    if {[string match $tkPriv(tablePrev) $el]} return
+    if {[string match $Priv(tablePrev) $el]} return
     switch [$w cget -selectmode] {
        browse {
            $w selection clear all
            $w selection set $el
-           set tkPriv(tablePrev) $el
+           set Priv(tablePrev) $el
        }
        extended {
-           scan $tkPriv(tablePrev) %d,%d r c
+           scan $Priv(tablePrev) %d,%d r c
            scan $el %d,%d elr elc
            if {[$w tag includes title $el]} {
                if {$r < [$w cget -titlerows]+[$w cget -roworigin]} {
@@ -273,15 +375,15 @@ proc tkTableMotion {w el} {
                    $w selection set anchor $elr,[$w index end col]
                }
            } else {
-               $w selection clear anchor $tkPriv(tablePrev)
+               $w selection clear anchor $Priv(tablePrev)
                $w selection set anchor $el
            }
-           set tkPriv(tablePrev) $el
+           set Priv(tablePrev) $el
        }
     }
 }
 
-# tkTableBeginExtend --
+# ::tk::table::BeginExtend --
 #
 # This procedure is typically invoked on shift-button-1 presses. It
 # begins the process of extending a selection in the table. Its
@@ -293,14 +395,14 @@ proc tkTableMotion {w el} {
 # el - The element for the selection operation (typically the
 # one under the pointer). Must be in numerical form.
 
-proc tkTableBeginExtend {w el} {
+proc ::tk::table::BeginExtend {w el} {
     if {[string match extended [$w cget -selectmode]] &&
        [$w selection includes anchor]} {
-       tkTableMotion $w $el
+       ::tk::table::Motion $w $el
     }
 }
 
-# tkTableBeginToggle --
+# ::tk::table::BeginToggle --
 #
 # This procedure is typically invoked on control-button-1 presses. It
 # begins the process of toggling a selection in the table. Its
@@ -312,34 +414,52 @@ proc tkTableBeginExtend {w el} {
 # el - The element for the selection operation (typically the
 # one under the pointer). Must be in numerical form.
 
-proc tkTableBeginToggle {w el} {
-    global tkPriv
+proc ::tk::table::BeginToggle {w el} {
     if {[string match extended [$w cget -selectmode]]} {
-       set tkPriv(tablePrev) $el
+       variable Priv
+       set Priv(tablePrev) $el
        $w selection anchor $el
-       if [$w selection includes $el] {
-           $w selection clear $el
+       if {[$w tag includes title $el]} {
+           scan $el %d,%d r c
+           if {$r < [$w cget -titlerows]+[$w cget -roworigin]} {
+               ## We're in a column header
+               if {$c < [$w cget -titlecols]+[$w cget -colorigin]} {
+                   ## We're in the topleft title area
+                   set end end
+               } else {
+                   set end [$w index end row],$c
+               }
+           } else {
+               ## We're in a row header
+               set end $r,[$w index end col]
+           }
        } else {
-           $w selection set $el
+           ## We're in a non-title cell
+           set end $el
        }
+       if {[$w selection includes  $end]} {
+           $w selection clear $el $end
+       } else {
+           $w selection set   $el $end
+        }
     }
 }
 
-# tkTableAutoScan --
-# This procedure is invoked when the mouse leaves an entry window
+# ::tk::table::AutoScan --
+# This procedure is invoked when the mouse leaves an table window
 # with button 1 down. It scrolls the window up, down, left, or
 # right, depending on where the mouse left the window, and reschedules
 # itself as an "after" command so that the window continues to scroll until
 # the mouse moves back into the window or the mouse button is released.
 #
 # Arguments:
-# w - The entry window.
+# w - The table window.
 
-proc tkTableAutoScan {w} {
-    global tkPriv
+proc ::tk::table::AutoScan {w} {
     if {![winfo exists $w]} return
-    set x $tkPriv(x)
-    set y $tkPriv(y)
+    variable Priv
+    set x $Priv(x)
+    set y $Priv(y)
     if {$y >= [winfo height $w]} {
        $w yview scroll 1 units
     } elseif {$y < 0} {
@@ -351,26 +471,43 @@ proc tkTableAutoScan {w} {
     } else {
        return
     }
-    tkTableMotion $w [$w index @$x,$y]
-    set tkPriv(afterId) [after 50 tkTableAutoScan $w]
+    ::tk::table::Motion $w [$w index @$x,$y]
+    set Priv(afterId) [after 50 ::tk::table::AutoScan $w]
 }
 
-# tkTableMoveCell --
+# ::tk::table::MoveCell --
 #
 # Moves the location cursor (active element) by the specified number
 # of cells and changes the selection if we're in browse or extended
-# selection mode.
+# selection mode.  If the new cell is "hidden", we skip to the next
+# visible cell if possible, otherwise just abort.
 #
 # Arguments:
 # w - The table widget.
 # x - +1 to move down one cell, -1 to move up one cell.
 # y - +1 to move right one cell, -1 to move left one cell.
 
-proc tkTableMoveCell {w x y} {
-    global tkPriv
+proc ::tk::table::MoveCell {w x y} {
     if {[catch {$w index active row} r]} return
     set c [$w index active col]
-    $w activate [incr r $x],[incr c $y]
+    set cell [$w index [incr r $x],[incr c $y]]
+    while {[string compare [set true [$w hidden $cell]] {}]} {
+       # The cell is in some way hidden
+       if {[string compare $true [$w index active]]} {
+           # The span cell wasn't the previous cell, so go to that
+           set cell $true
+           break
+       }
+       if {$x > 0} {incr r} elseif {$x < 0} {incr r -1}
+       if {$y > 0} {incr c} elseif {$y < 0} {incr c -1}
+       if {[string compare $cell [$w index $r,$c]]} {
+           set cell [$w index $r,$c]
+       } else {
+           # We couldn't find a non-hidden cell, just don't move
+           return
+       }
+    }
+    $w activate $cell
     $w see active
     switch [$w cget -selectmode] {
        browse {
@@ -378,15 +515,16 @@ proc tkTableMoveCell {w x y} {
            $w selection set active
        }
        extended {
+           variable Priv
            $w selection clear all
            $w selection set active
            $w selection anchor active
-           set tkPriv(tablePrev) [$w index active]
+           set Priv(tablePrev) [$w index active]
        }
     }
 }
 
-# tkTableExtendSelect --
+# ::tk::table::ExtendSelect --
 #
 # Does nothing unless we're in extended selection mode; in this
 # case it moves the location cursor (active element) by the specified
@@ -397,16 +535,16 @@ proc tkTableMoveCell {w x y} {
 # x - +1 to move down one cell, -1 to move up one cell.
 # y - +1 to move right one cell, -1 to move left one cell.
 
-proc tkTableExtendSelect {w x y} {
+proc ::tk::table::ExtendSelect {w x y} {
     if {[string compare extended [$w cget -selectmode]] ||
        [catch {$w index active row} r]} return
     set c [$w index active col]
     $w activate [incr r $x],[incr c $y]
     $w see active
-    tkTableMotion $w [$w index active]
+    ::tk::table::Motion $w [$w index active]
 }
 
-# tkTableDataExtend
+# ::tk::table::DataExtend
 #
 # This procedure is called for key-presses such as Shift-KEndData.
 # If the selection mode isnt multiple or extend then it does nothing.
@@ -417,19 +555,19 @@ proc tkTableExtendSelect {w x y} {
 # w - The table widget.
 # el - An integer cell number.
 
-proc tkTableDataExtend {w el} {
+proc ::tk::table::DataExtend {w el} {
     set mode [$w cget -selectmode]
     if {[string match extended $mode]} {
        $w activate $el
        $w see $el
-       if [$w selection includes anchor] {tkTableMotion $w $el}
+       if {[$w selection includes anchor]} {::tk::table::Motion $w $el}
     } elseif {[string match multiple $mode]} {
        $w activate $el
        $w see $el
     }
 }
 
-# tkTableSelectAll
+# ::tk::table::SelectAll
 #
 # This procedure is invoked to handle the "select all" operation.
 # For single and browse mode, it just selects the active element.
@@ -438,17 +576,19 @@ proc tkTableDataExtend {w el} {
 # Arguments:
 # w - The table widget.
 
-proc tkTableSelectAll {w} {
+proc ::tk::table::SelectAll {w} {
     if {[regexp {^(single|browse)$} [$w cget -selectmode]]} {
        $w selection clear all
        $w selection set active
-       tkTableHandleType $w [$w index active]
+       ::tk::table::HandleType $w [$w index active]
+    } elseif {[$w cget -selecttitles]} {
+       $w selection set [$w cget -roworigin],[$w cget -colorigin] end
     } else {
        $w selection set origin end
     }
 }
 
-# tkTableChangeWidth --
+# ::tk::table::ChangeWidth --
 # Adjust the widget of the specified cell by $a.
 #
 # Arguments:
@@ -456,12 +596,12 @@ proc tkTableSelectAll {w} {
 # i - cell index
 # a - amount to adjust by
 
-proc tkTableChangeWidth {w i a} {
+proc ::tk::table::ChangeWidth {w i a} {
     set tmp [$w index $i col]
     if {[set width [$w width $tmp]] >= 0} {
        $w width $tmp [incr width $a]
     } else {
-       $w width $tmp [incr width -$a]
+       $w width $tmp [incr width [expr {-$a}]]
     }
 }
 
@@ -475,7 +615,7 @@ proc tkTableChangeWidth {w i a} {
 proc tk_tableCopy w {
     if {[selection own -displayof $w] == "$w"} {
        clipboard clear -displayof $w
-       catch {clipboard append -displayof $w [selection get -displayof $w]}
+       catch {clipboard append -displayof $w [::tk::table::GetSelection $w]}
     }
 }
 
@@ -491,8 +631,8 @@ proc tk_tableCut w {
     if {[selection own -displayof $w] == "$w"} {
        clipboard clear -displayof $w
        catch {
-           clipboard append -displayof $w [selection get -displayof $w]
-           $w cursel set {}
+           clipboard append -displayof $w [::tk::table::GetSelection $w]
+           $w cursel {}
            $w selection clear all
        }
     }
@@ -508,9 +648,9 @@ proc tk_tableCut w {
 
 proc tk_tablePaste {w {cell {}}} {
     if {[string compare {} $cell]} {
-       if {[catch {selection get -displayof $w} data]} return
+       if {[catch {::tk::table::GetSelection $w} data]} return
     } else {
-       if {[catch {selection get -displayof $w -selection CLIPBOARD} data]} {
+       if {[catch {::tk::table::GetSelection $w CLIPBOARD} data]} {
            return
        }
        set cell active
diff --git a/libgui/src/tkTable.tcl.h b/libgui/src/tkTable.tcl.h
new file mode 100644 (file)
index 0000000..614106e
--- /dev/null
@@ -0,0 +1,366 @@
+"proc tkTableClipboardKeysyms {copy cut paste} {\n"
+"    bind Table <$copy>        {tk_tableCopy %W}\n"
+"    bind Table <$cut> {tk_tableCut %W}\n"
+"    bind Table <$paste>       {tk_tablePaste %W}\n"
+"}\n"
+"bind Table <3>                {\n"
+"    ## You might want to check for row returned if you want to\n"
+"    ## restrict the resizing of certain rows\n"
+"    %W border mark %x %y\n"
+"}\n"
+"bind Table <B3-Motion>        { %W border dragto %x %y }\n"
+"bind Table <1> {\n"
+"    if {[winfo exists %W]} {\n"
+"      tkTableBeginSelect %W [%W index @%x,%y]\n"
+"      focus %W\n"
+"    }\n"
+"}\n"
+"bind Table <B1-Motion> {\n"
+"    array set tkPriv {x %x y %y}\n"
+"    tkTableMotion %W [%W index @%x,%y]\n"
+"}\n"
+"bind Table <Double-1> {\n"
+"    # empty\n"
+"}\n"
+"bind Table <ButtonRelease-1> {\n"
+"    if {[winfo exists %W]} {\n"
+"      tkCancelRepeat\n"
+"      %W activate @%x,%y\n"
+"    }\n"
+"}\n"
+"bind Table <Shift-1>  {tkTableBeginExtend %W [%W index @%x,%y]}\n"
+"bind Table <Control-1>        {tkTableBeginToggle %W [%W index @%x,%y]}\n"
+"bind Table <B1-Enter> {tkCancelRepeat}\n"
+"bind Table <B1-Leave> {\n"
+"    array set tkPriv {x %x y %y}\n"
+"    tkTableAutoScan %W\n"
+"}\n"
+"bind Table <2> {\n"
+"    %W scan mark %x %y\n"
+"    array set tkPriv {x %x y %y}\n"
+"    set tkPriv(mouseMoved) 0\n"
+"}\n"
+"bind Table <B2-Motion> {\n"
+"    if {(%x != $tkPriv(x)) || (%y != $tkPriv(y))} { set tkPriv(mouseMoved) 1 }\n"
+"    if $tkPriv(mouseMoved) { %W scan dragto %x %y }\n"
+"}\n"
+"bind Table <ButtonRelease-2> {\n"
+"    if {!$tkPriv(mouseMoved)} { tk_tablePaste %W [%W index @%x,%y] }\n"
+"}\n"
+"if {[string comp {} [info command event]]} {\n"
+"    tkTableClipboardKeysyms <Copy> <Cut> <Paste>\n"
+"} else {\n"
+"    tkTableClipboardKeysyms Control-c Control-x Control-v\n"
+"}\n"
+"bind Table <Any-Tab> {\n"
+"    # empty to allow Tk focus movement\n"
+"}\n"
+"bind Table <FocusOut> {\n"
+"    catch {%W activate active}\n"
+"}\n"
+"bind Table <Shift-Up>         {tkTableExtendSelect %W -1  0}\n"
+"bind Table <Shift-Down>               {tkTableExtendSelect %W  1  0}\n"
+"bind Table <Shift-Left>               {tkTableExtendSelect %W  0 -1}\n"
+"bind Table <Shift-Right>      {tkTableExtendSelect %W  0  1}\n"
+"bind Table <Prior>            {%W yview scroll -1 pages; %W activate @0,0}\n"
+"bind Table <Next>             {%W yview scroll  1 pages; %W activate @0,0}\n"
+"bind Table <Control-Prior>    {%W xview scroll -1 pages}\n"
+"bind Table <Control-Next>     {%W xview scroll  1 pages}\n"
+"bind Table <Home>             {%W see origin}\n"
+"bind Table <End>              {%W see end}\n"
+"bind Table <Control-Home> {\n"
+"    %W selection clear all\n"
+"    %W activate origin\n"
+"    %W selection set active\n"
+"    %W see active\n"
+"}\n"
+"bind Table <Control-End> {\n"
+"    %W selection clear all\n"
+"    %W activate end\n"
+"    %W selection set active\n"
+"    %W see active\n"
+"}\n"
+"bind Table <Shift-Control-Home>       {tkTableDataExtend %W origin}\n"
+"bind Table <Shift-Control-End>        {tkTableDataExtend %W end}\n"
+"bind Table <Select>           {tkTableBeginSelect %W [%W index active]}\n"
+"bind Table <Shift-Select>     {tkTableBeginExtend %W [%W index active]}\n"
+"bind Table <Control-slash>    {tkTableSelectAll %W}\n"
+"bind Table <Control-backslash> {\n"
+"    if {[string match browse [%W cget -selectmode]]} {%W selection clear all}\n"
+"}\n"
+"bind Table <Up>                       {tkTableMoveCell %W -1  0}\n"
+"bind Table <Down>             {tkTableMoveCell %W  1  0}\n"
+"bind Table <Left>             {tkTableMoveCell %W  0 -1}\n"
+"bind Table <Right>            {tkTableMoveCell %W  0  1}\n"
+"bind Table <Any-KeyPress> {\n"
+"    if {[string compare {} %A]} { %W insert active insert %A }\n"
+"}\n"
+"bind Table <BackSpace> {\n"
+"    set tkPriv(junk) [%W icursor]\n"
+"    if {[string compare {} $tkPriv(junk)] && $tkPriv(junk)} {\n"
+"      %W delete active [expr {$tkPriv(junk)-1}]\n"
+"    }\n"
+"}\n"
+"bind Table <Delete>           {%W delete active insert}\n"
+"bind Table <Escape>           {%W reread}\n"
+"bind Table <Return> {\n"
+"    %W insert active insert \"\n\"\n"
+"}\n"
+"bind Table <Control-Left>     {%W icursor [expr {[%W icursor]-1}]}\n"
+"bind Table <Control-Right>    {%W icursor [expr {[%W icursor]+1}]}\n"
+"bind Table <Control-e>                {%W icursor end}\n"
+"bind Table <Control-a>                {%W icursor 0}\n"
+"bind Table <Control-k>                {%W delete active insert end}\n"
+"bind Table <Control-equal>    {tkTableChangeWidth %W active  1}\n"
+"bind Table <Control-minus>    {tkTableChangeWidth %W active -1}\n"
+"proc tkTableBeginSelect {w el} {\n"
+"    global tkPriv\n"
+"    if {[scan $el %d,%d r c] != 2} return\n"
+"    switch [$w cget -selectmode] {\n"
+"      multiple {\n"
+"          if {[$w tag includes title $el]} {\n"
+"              ## in the title area\n"
+"              if {$r < [$w cget -titlerows]+[$w cget -roworigin]} {\n"
+"                  ## We're in a column header\n"
+"                  if {$c < [$w cget -titlecols]+[$w cget -colorigin]} {\n"
+"                      ## We're in the topleft title area\n"
+"                      set inc topleft\n"
+"                      set el2 end\n"
+"                  } else {\n"
+"                      set inc [$w index topleft row],$c\n"
+"                      set el2 [$w index end row],$c\n"
+"                  }\n"
+"              } else {\n"
+"                  ## We're in a row header\n"
+"                  set inc $r,[$w index topleft col]\n"
+"                  set el2 $r,[$w index end col]\n"
+"              }\n"
+"          } else {\n"
+"              set inc $el\n"
+"              set el2 $el\n"
+"          }\n"
+"          if [$w selection includes $inc] {\n"
+"              $w selection clear $el $el2\n"
+"          } else {\n"
+"              $w selection set $el $el2\n"
+"          }\n"
+"      }\n"
+"      extended {\n"
+"          $w selection clear all\n"
+"          if {[$w tag includes title $el]} {\n"
+"              if {$r < [$w cget -titlerows]+[$w cget -roworigin]} {\n"
+"                  ## We're in a column header\n"
+"                  if {$c < [$w cget -titlecols]+[$w cget -colorigin]} {\n"
+"                      $w selection set origin end\n"
+"                  } else {\n"
+"                      $w selection set $el [$w index end row],$c\n"
+"                  }\n"
+"              } else {\n"
+"                  ## We're in a row header\n"
+"                  $w selection set $el $r,[$w index end col]\n"
+"              }\n"
+"          } else {\n"
+"              $w selection set $el\n"
+"          }\n"
+"          $w selection anchor $el\n"
+"          set tkPriv(tablePrev) $el\n"
+"      }\n"
+"      default {\n"
+"          if {![$w tag includes title $el]} {\n"
+"              $w selection clear all\n"
+"              $w selection set $el\n"
+"              set tkPriv(tablePrev) $el\n"
+"          }\n"
+"          $w selection anchor $el\n"
+"      }\n"
+"    }\n"
+"}\n"
+"proc tkTableMotion {w el} {\n"
+"    global tkPriv\n"
+"    if {![info exists tkPriv(tablePrev)]} {\n"
+"      set tkPriv(tablePrev) $el\n"
+"      return\n"
+"    }\n"
+"    if {[string match $tkPriv(tablePrev) $el]} return\n"
+"    switch [$w cget -selectmode] {\n"
+"      browse {\n"
+"          $w selection clear all\n"
+"          $w selection set $el\n"
+"          set tkPriv(tablePrev) $el\n"
+"      }\n"
+"      extended {\n"
+"          scan $tkPriv(tablePrev) %d,%d r c\n"
+"          scan $el %d,%d elr elc\n"
+"          if {[$w tag includes title $el]} {\n"
+"              if {$r < [$w cget -titlerows]+[$w cget -roworigin]} {\n"
+"                  ## We're in a column header\n"
+"                  if {$c < [$w cget -titlecols]+[$w cget -colorigin]} {\n"
+"                      ## We're in the topleft title area\n"
+"                      $w selection clear anchor end\n"
+"                  } else {\n"
+"                      $w selection clear anchor [$w index end row],$c\n"
+"                  }\n"
+"                  $w selection set anchor [$w index end row],$elc\n"
+"              } else {\n"
+"                  ## We're in a row header\n"
+"                  $w selection clear anchor $r,[$w index end col]\n"
+"                  $w selection set anchor $elr,[$w index end col]\n"
+"              }\n"
+"          } else {\n"
+"              $w selection clear anchor $tkPriv(tablePrev)\n"
+"              $w selection set anchor $el\n"
+"          }\n"
+"          set tkPriv(tablePrev) $el\n"
+"      }\n"
+"    }\n"
+"}\n"
+"proc tkTableBeginExtend {w el} {\n"
+"    if {[string match extended [$w cget -selectmode]] &&\n"
+"      [$w selection includes anchor]} {\n"
+"      tkTableMotion $w $el\n"
+"    }\n"
+"}\n"
+"proc tkTableBeginToggle {w el} {\n"
+"    global tkPriv\n"
+"    if {[string match extended [$w cget -selectmode]]} {\n"
+"      set tkPriv(tablePrev) $el\n"
+"      $w selection anchor $el\n"
+"      if [$w selection includes $el] {\n"
+"          $w selection clear $el\n"
+"      } else {\n"
+"          $w selection set $el\n"
+"      }\n"
+"    }\n"
+"}\n"
+"proc tkTableAutoScan {w} {\n"
+"    global tkPriv\n"
+"    if {![winfo exists $w]} return\n"
+"    set x $tkPriv(x)\n"
+"    set y $tkPriv(y)\n"
+"    if {$y >= [winfo height $w]} {\n"
+"      $w yview scroll 1 units\n"
+"    } elseif {$y < 0} {\n"
+"      $w yview scroll -1 units\n"
+"    } elseif {$x >= [winfo width $w]} {\n"
+"      $w xview scroll 1 units\n"
+"    } elseif {$x < 0} {\n"
+"      $w xview scroll -1 units\n"
+"    } else {\n"
+"      return\n"
+"    }\n"
+"    tkTableMotion $w [$w index @$x,$y]\n"
+"    set tkPriv(afterId) [after 50 tkTableAutoScan $w]\n"
+"}\n"
+"proc tkTableMoveCell {w x y} {\n"
+"    global tkPriv\n"
+"    if {[catch {$w index active row} r]} return\n"
+"    set c [$w index active col]\n"
+"    $w activate [incr r $x],[incr c $y]\n"
+"    $w see active\n"
+"    switch [$w cget -selectmode] {\n"
+"      browse {\n"
+"          $w selection clear all\n"
+"          $w selection set active\n"
+"      }\n"
+"      extended {\n"
+"          $w selection clear all\n"
+"          $w selection set active\n"
+"          $w selection anchor active\n"
+"          set tkPriv(tablePrev) [$w index active]\n"
+"      }\n"
+"    }\n"
+"}\n"
+"proc tkTableExtendSelect {w x y} {\n"
+"    if {[string compare extended [$w cget -selectmode]] ||\n"
+"      [catch {$w index active row} r]} return\n"
+"    set c [$w index active col]\n"
+"    $w activate [incr r $x],[incr c $y]\n"
+"    $w see active\n"
+"    tkTableMotion $w [$w index active]\n"
+"}\n"
+"proc tkTableDataExtend {w el} {\n"
+"    set mode [$w cget -selectmode]\n"
+"    if {[string match extended $mode]} {\n"
+"      $w activate $el\n"
+"      $w see $el\n"
+"      if [$w selection includes anchor] {tkTableMotion $w $el}\n"
+"    } elseif {[string match multiple $mode]} {\n"
+"      $w activate $el\n"
+"      $w see $el\n"
+"    }\n"
+"}\n"
+"proc tkTableSelectAll {w} {\n"
+"    if {[regexp {^(single|browse)$} [$w cget -selectmode]]} {\n"
+"      $w selection clear all\n"
+"      $w selection set active\n"
+"      tkTableHandleType $w [$w index active]\n"
+"    } else {\n"
+"      $w selection set origin end\n"
+"    }\n"
+"}\n"
+"proc tkTableChangeWidth {w i a} {\n"
+"    set tmp [$w index $i col]\n"
+"    if {[set width [$w width $tmp]] >= 0} {\n"
+"      $w width $tmp [incr width $a]\n"
+"    } else {\n"
+"      $w width $tmp [incr width -$a]\n"
+"    }\n"
+"}\n"
+"proc tk_tableCopy w {\n"
+"    if {[selection own -displayof $w] == \"$w\"} {\n"
+"      clipboard clear -displayof $w\n"
+"      catch {clipboard append -displayof $w [selection get -displayof $w]}\n"
+"    }\n"
+"}\n"
+"proc tk_tableCut w {\n"
+"    if {[selection own -displayof $w] == \"$w\"} {\n"
+"      clipboard clear -displayof $w\n"
+"      catch {\n"
+"          clipboard append -displayof $w [selection get -displayof $w]\n"
+"          $w cursel set {}\n"
+"          $w selection clear all\n"
+"      }\n"
+"    }\n"
+"}\n"
+"proc tk_tablePaste {w {cell {}}} {\n"
+"    if {[string compare {} $cell]} {\n"
+"      if {[catch {selection get -displayof $w} data]} return\n"
+"    } else {\n"
+"      if {[catch {selection get -displayof $w -selection CLIPBOARD} data]} {\n"
+"          return\n"
+"      }\n"
+"      set cell active\n"
+"    }\n"
+"    tk_tablePasteHandler $w [$w index $cell] $data\n"
+"    if {[$w cget -state] == \"normal\"} {focus $w}\n"
+"}\n"
+"proc tk_tablePasteHandler {w cell data} {\n"
+"    set rows  [expr {[$w cget -rows]-[$w cget -roworigin]}]\n"
+"    set cols  [expr {[$w cget -cols]-[$w cget -colorigin]}]\n"
+"    set r     [$w index $cell row]\n"
+"    set c     [$w index $cell col]\n"
+"    set rsep  [$w cget -rowseparator]\n"
+"    set csep  [$w cget -colseparator]\n"
+"    ## Assume separate rows are split by row separator if specified\n"
+"    ## If you were to want multi-character row separators, you would need:\n"
+"    # regsub -all $rsep $data <newline> data\n"
+"    # set data [join $data <newline>]\n"
+"    if {[string comp {} $rsep]} { set data [split $data $rsep] }\n"
+"    set row   $r\n"
+"    foreach line $data {\n"
+"      if {$row > $rows} break\n"
+"      set col $c\n"
+"      ## Assume separate cols are split by col separator if specified\n"
+"      ## Unless a -separator was specified\n"
+"      if {[string comp {} $csep]} { set line [split $line $csep] }\n"
+"      ## If you were to want multi-character col separators, you would need:\n"
+"      # regsub -all $csep $line <newline> line\n"
+"      # set line [join $line <newline>]\n"
+"      foreach item $line {\n"
+"          if {$col > $cols} break\n"
+"          $w set $row,$col $item\n"
+"          incr col\n"
+"      }\n"
+"      incr row\n"
+"    }\n"
+"}\n"
index 9d307b7..9a1d428 100644 (file)
@@ -4,17 +4,64 @@
  *     This module implements cell oriented functions for table
  *     widgets.
  *
- * Copyright (c) 1998 Jeffrey Hobbs
+ * Copyright (c) 1998-2000 Jeffrey Hobbs
  *
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  *
+ * RCS: @(#) $Id$
  */
 
 #include "tkTable.h"
 
-static int     TableSortCompareProc _ANSI_ARGS_((CONST VOID *first,
-                                                 CONST VOID *second));
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableTrueCell --
+ *     Takes a row,col pair in user coords and returns the true
+ *     cell that it relates to, either dimension bounded, or a
+ *     span cell if it was hidden.
+ *
+ * Results:
+ *     The true row, col in user coords are placed in the pointers.
+ *     If the value changed for some reasons, 0 is returned (it was not
+ *     the /true/ cell).
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+TableTrueCell(Table *tablePtr, int r, int c, int *row, int *col)
+{
+    *row = r; *col = c;
+    /*
+     * We check spans before constraints, because we don't want to
+     * constrain and then think we ended up in a span
+     */
+    if (tablePtr->spanAffTbl && !(tablePtr->flags & AVOID_SPANS)) {
+       char buf[INDEX_BUFSIZE];
+       Tcl_HashEntry *entryPtr;
+
+       TableMakeArrayIndex(r, c, buf);
+       entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl, buf);
+       if ((entryPtr != NULL) &&
+               ((char *)Tcl_GetHashValue(entryPtr) != NULL)) {
+           /*
+            * This cell is covered by another spanning cell.
+            * We need to return the coords for that spanning cell.
+            */
+           TableParseArrayIndex(row, col, (char *)Tcl_GetHashValue(entryPtr));
+           return 0;
+       }
+    }
+    *row = BETWEEN(r, tablePtr->rowOffset,
+           tablePtr->rows-1+tablePtr->rowOffset);
+    *col = BETWEEN(c, tablePtr->colOffset,
+           tablePtr->cols-1+tablePtr->colOffset);
+    return ((*row == r) && (*col == c));
+}
 
 /*
  *----------------------------------------------------------------------
@@ -32,26 +79,96 @@ static int  TableSortCompareProc _ANSI_ARGS_((CONST VOID *first,
  *
  *----------------------------------------------------------------------
  */
-void
+int
 TableCellCoords(Table *tablePtr, int row, int col,
-               int *x, int *y, int *width, int *height)
+               int *x, int *y, int *w, int *h)
 {
-  if (tablePtr->rows <= 0 || tablePtr->cols <= 0) {
-    *width = *height = *x = *y = 0;
-    return;
-  }
-  /* real coords required, always should be passed acceptable values,
-   * but this is a possible seg fault otherwise */
-  row = MIN(tablePtr->rows-1, MAX(0, row));
-  col = MIN(tablePtr->cols-1, MAX(0, col));
-  *width = tablePtr->colPixels[col];
-  *height = tablePtr->rowPixels[row];
-  *x = tablePtr->highlightWidth + tablePtr->colStarts[col] - 
-    ((col < tablePtr->titleCols) ? 0 : tablePtr->colStarts[tablePtr->leftCol]
-     - tablePtr->colStarts[tablePtr->titleCols]);
-  *y = tablePtr->highlightWidth + tablePtr->rowStarts[row] -
-    ((row < tablePtr->titleRows) ? 0 : tablePtr->rowStarts[tablePtr->topRow]
-     - tablePtr->rowStarts[tablePtr->titleRows]);
+    register int hl = tablePtr->highlightWidth;
+    int result = CELL_OK;
+
+    if (tablePtr->rows <= 0 || tablePtr->cols <= 0) {
+       *w = *h = *x = *y = 0;
+       return CELL_BAD;
+    }
+    /*
+     * Real coords required, always should be passed acceptable values,
+     * but this is a possible seg fault otherwise
+     */
+    CONSTRAIN(row, 0, tablePtr->rows-1);
+    CONSTRAIN(col, 0, tablePtr->cols-1);
+    *w = tablePtr->colPixels[col];
+    *h = tablePtr->rowPixels[row];
+    /*
+     * Adjust for sizes of spanning cells
+     * and ensure that this cell isn't "hidden"
+     */
+    if (tablePtr->spanAffTbl && !(tablePtr->flags & AVOID_SPANS)) {
+       char buf[INDEX_BUFSIZE];
+       Tcl_HashEntry *entryPtr;
+
+       TableMakeArrayIndex(row+tablePtr->rowOffset,
+                           col+tablePtr->colOffset, buf);
+       entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl, buf);
+       if (entryPtr != NULL) {
+           int rs, cs;
+           char *cell;
+
+           cell = (char *) Tcl_GetHashValue(entryPtr);
+           if (cell != NULL) {
+               /* This cell is covered by another spanning cell */
+               /* We need to return the coords for that cell */
+               TableParseArrayIndex(&rs, &cs, cell);
+               *w = rs;
+               *h = cs;
+               result = CELL_HIDDEN;
+               goto setxy;
+           }
+           /* Get the actual span values out of spanTbl */
+           entryPtr = Tcl_FindHashEntry(tablePtr->spanTbl, buf);
+           cell = (char *) Tcl_GetHashValue(entryPtr);
+           TableParseArrayIndex(&rs, &cs, cell);
+           if (rs > 0) {
+               /*
+                * Make sure we don't overflow our space
+                */
+               if (row < tablePtr->titleRows) {
+                   rs = MIN(tablePtr->titleRows-1, row+rs);
+               } else {
+                   rs = MIN(tablePtr->rows-1, row+rs);
+               }
+               *h = tablePtr->rowStarts[rs+1]-tablePtr->rowStarts[row];
+               result = CELL_SPAN;
+           } else if (rs <= 0) {
+               /* currently negative spans are not supported */
+           }
+           if (cs > 0) {
+               /*
+                * Make sure we don't overflow our space
+                */
+               if (col < tablePtr->titleCols) {
+                   cs = MIN(tablePtr->titleCols-1, col+cs);
+               } else {
+                   cs = MIN(tablePtr->cols-1, col+cs);
+               }
+               *w = tablePtr->colStarts[cs+1]-tablePtr->colStarts[col];
+               result = CELL_SPAN;
+           } else if (cs <= 0) {
+               /* currently negative spans are not supported */
+           }
+       }
+    }
+setxy:
+    *x = hl + tablePtr->colStarts[col];
+    if (col >= tablePtr->titleCols) {
+       *x -= tablePtr->colStarts[tablePtr->leftCol]
+           - tablePtr->colStarts[tablePtr->titleCols];
+    }
+    *y = hl + tablePtr->rowStarts[row];
+    if (row >= tablePtr->titleRows) {
+       *y -= tablePtr->rowStarts[tablePtr->topRow]
+           - tablePtr->rowStarts[tablePtr->titleRows];
+    }
+    return result;
 }
 
 /*
@@ -76,41 +193,86 @@ int
 TableCellVCoords(Table *tablePtr, int row, int col,
                 int *rx, int *ry, int *rw, int *rh, int full)
 {
-  if (tablePtr->tkwin == NULL) return 0;
-
-  if ((row < tablePtr->topRow && row >= tablePtr->titleRows) ||
-      (col < tablePtr->leftCol && col >= tablePtr->titleCols)) {
-    /* hiding in "dead" space between title areas and visible cells */
-    *rx = 0; *ry = 0; *rw = 0; *rh = 0;
-    return 0;
-  } else {
-    int x, y, w, h, w0, h0, hl = tablePtr->highlightWidth;
-    /* Necessary to use separate vars in case dummies are passed in */
-    TableCellCoords(tablePtr, row, col, &x, &y, &w, &h);
-    *rx = x; *ry = y;
+    int x, y, w, h, w0, h0, cellType, hl = tablePtr->highlightWidth;
+
+    if (tablePtr->tkwin == NULL) return 0;
+
+    /*
+     * Necessary to use separate vars in case dummies are passed in
+     */
+    cellType = TableCellCoords(tablePtr, row, col, &x, &y, &w, &h);
+    *rx = x; *ry = y; *rw = w; *rh = h;
+    if (cellType == CELL_OK) {
+       if ((row < tablePtr->topRow && row >= tablePtr->titleRows) ||
+           (col < tablePtr->leftCol && col >= tablePtr->titleCols)) {
+           /*
+            * A non-spanning cell hiding in "dead" space
+            * between title areas and visible cells
+            */
+           return 0;
+       }
+    } else if (cellType == CELL_SPAN) {
+       /*
+        * we might need to treat full better is CELL_SPAN but primary
+        * cell is visible
+        */
+       int topX = tablePtr->colStarts[tablePtr->titleCols]+hl;
+       int topY = tablePtr->rowStarts[tablePtr->titleRows]+hl;
+       if ((col < tablePtr->leftCol) && (col >= tablePtr->titleCols)) {
+           if (full || (x+w < topX)) {
+               return 0;
+           } else {
+               w -= topX-x;
+               x = topX;
+           }
+       }
+       if ((row < tablePtr->topRow) && (row >= tablePtr->titleRows)) {
+           if (full || (y+h < topY)) {
+               return 0;
+           } else {
+               h -= topY-y;
+               y = topY;
+           }
+       }
+       /*
+        * re-set these according to changed coords
+        */
+       *rx = x; *ry = y; *rw = w; *rh = h;
+    } else {
+       /*
+        * If it is a hidden cell, then w,h is the row,col in user coords
+        * of the cell that spans over this one
+        */
+       return 0;
+    }
+    /*
+     * At this point, we know it is on the screen,
+     * but not if we can see 100% of it (if we care)
+     */
     if (full) {
-      w0 = w; h0 = h;
+       w0 = w; h0 = h;
     } else {
-      /* if we don't care about seeing the whole thing, then
-       * make sure we at least see a pixel worth */
-      w0 = h0 = 1;
-    }
-    /* Is the cell visible? */
-    if (x<hl || y<hl || (x+w0)>(Tk_Width(tablePtr->tkwin)-hl)
-       || (y+h0)>(Tk_Height(tablePtr->tkwin)-hl)) {
-      /* definitely off the screen */
-      *rw = *rh = 0;
-      return 0;
+       /*
+        * if we don't care about seeing the whole thing, then
+        * make sure we at least see a pixel worth
+        */
+       w0 = h0 = 1;
+    }
+    /*
+     * Is the cell visible?
+     */
+    if ((x < hl) || (y < hl) || ((x+w0) > (Tk_Width(tablePtr->tkwin)-hl))
+           || ((y+h0) > (Tk_Height(tablePtr->tkwin)-hl))) {
+       /* definitely off the screen */
+       return 0;
     } else {
-      if (full) {
-       *rw = w; *rh = h;
-      } else {
-       *rw = MIN(w, Tk_Width(tablePtr->tkwin)-hl-x);
-       *rh = MIN(h, Tk_Height(tablePtr->tkwin)-hl-y);
-      }
-      return 1;
-    }
-  }
+       /* if it was full, then w,h are already be properly constrained */
+       if (!full) {
+           *rw = MIN(w, Tk_Width(tablePtr->tkwin)-hl-x);
+           *rh = MIN(h, Tk_Height(tablePtr->tkwin)-hl-y);
+       }
+       return 1;
+    }
 }
 
 /*
@@ -132,25 +294,43 @@ TableCellVCoords(Table *tablePtr, int row, int col,
 void
 TableWhatCell(register Table *tablePtr, int x, int y, int *row, int *col)
 {
-  int i;
-  x = MAX(0, x); y = MAX(0, y);
-  /* Adjust for table's global highlightthickness border */
-  x -= tablePtr->highlightWidth;
-  y -= tablePtr->highlightWidth;
-  /* Adjust the x coord if not in the column titles to change display coords
-   * into internal coords */
-  x += (x < tablePtr->colStarts[tablePtr->titleCols]) ? 0 :
-    tablePtr->colStarts[tablePtr->leftCol] -
-    tablePtr->colStarts[tablePtr->titleCols];
-  y += (y < tablePtr->rowStarts[tablePtr->titleRows]) ? 0 :
-    tablePtr->rowStarts[tablePtr->topRow] -
-    tablePtr->rowStarts[tablePtr->titleRows];
-  x = MIN(x, tablePtr->maxWidth-1);
-  y = MIN(y, tablePtr->maxHeight-1);
-  for (i = 1; x >= tablePtr->colStarts[i]; i++);
-  *col = i - 1;
-  for (i = 1; y >= tablePtr->rowStarts[i]; i++);
-  *row = i - 1;
+    int i;
+    x = MAX(0, x); y = MAX(0, y);
+    /* Adjust for table's global highlightthickness border */
+    x -= tablePtr->highlightWidth;
+    y -= tablePtr->highlightWidth;
+    /* Adjust the x coord if not in the column titles to change display coords
+     * into internal coords */
+    x += (x < tablePtr->colStarts[tablePtr->titleCols]) ? 0 :
+       tablePtr->colStarts[tablePtr->leftCol] -
+       tablePtr->colStarts[tablePtr->titleCols];
+    y += (y < tablePtr->rowStarts[tablePtr->titleRows]) ? 0 :
+       tablePtr->rowStarts[tablePtr->topRow] -
+       tablePtr->rowStarts[tablePtr->titleRows];
+    x = MIN(x, tablePtr->maxWidth-1);
+    y = MIN(y, tablePtr->maxHeight-1);
+    for (i = 1; x >= tablePtr->colStarts[i]; i++);
+    *col = i - 1;
+    for (i = 1; y >= tablePtr->rowStarts[i]; i++);
+    *row = i - 1;
+    if (tablePtr->spanAffTbl && !(tablePtr->flags & AVOID_SPANS)) {
+       char buf[INDEX_BUFSIZE];
+       Tcl_HashEntry *entryPtr;
+
+       /* We now correct the returned cell if this was "hidden" */
+       TableMakeArrayIndex(*row+tablePtr->rowOffset,
+                           *col+tablePtr->colOffset, buf);
+       entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl, buf);
+       if ((entryPtr != NULL) &&
+           /* We have to make sure this was not already hidden
+            * that's an error */
+           ((char *)Tcl_GetHashValue(entryPtr) != NULL)) {
+           /* this is a "hidden" cell */
+           TableParseArrayIndex(row, col, (char *)Tcl_GetHashValue(entryPtr));
+           *row -= tablePtr->rowOffset;
+           *col -= tablePtr->colOffset;
+       }
+    }
 }
 
 /*
@@ -173,37 +353,80 @@ TableWhatCell(register Table *tablePtr, int x, int y, int *row, int *col)
 int
 TableAtBorder(Table * tablePtr, int x, int y, int *row, int *col)
 {
-  int i, borders = 2, bd = tablePtr->borderWidth;
-  int dbd = 2*bd;
-  x = MAX(0, x); y = MAX(0, y);
-  x -= tablePtr->highlightWidth; y -= tablePtr->highlightWidth;
-  /* Adjust the x coord if not in the column titles to change display coords
-   * into internal coords */
-  x += (x < tablePtr->colStarts[tablePtr->titleCols]) ? 0 :
-    tablePtr->colStarts[tablePtr->leftCol] -
-    tablePtr->colStarts[tablePtr->titleCols];
-  y += (y < tablePtr->rowStarts[tablePtr->titleRows]) ? 0 :
-    tablePtr->rowStarts[tablePtr->topRow] -
-    tablePtr->rowStarts[tablePtr->titleRows];
-  x = MIN(x, tablePtr->maxWidth - 1);
-  y = MIN(y, tablePtr->maxHeight - 1);
-  for (i = 1; i <= tablePtr->cols && x+dbd >= tablePtr->colStarts[i]; i++);
-  if (x > tablePtr->colStarts[--i]+bd) {
-    borders--;
-    *col = -1;
-  } else {
-    *col = (--i < tablePtr->leftCol && i >= tablePtr->titleCols) ?
-      tablePtr->titleCols-1 : i;
-  }
-  for (i = 1; i <= tablePtr->rows && y+dbd >= tablePtr->rowStarts[i]; i++);
-  if (y > tablePtr->rowStarts[--i]+bd) {
-    borders--;
-    *row = -1;
-  } else {
-    *row = (--i < tablePtr->topRow && i >= tablePtr->titleRows) ?
-      tablePtr->titleRows-1 : i;
-  }
-  return borders;
+    int i, brow, bcol, borders = 2, bd[6];
+
+    TableGetTagBorders(&(tablePtr->defaultTag),
+           &bd[0], &bd[1], &bd[2], &bd[3]);
+    bd[4] = (bd[0] + bd[1])/2;
+    bd[5] = (bd[2] + bd[3])/2;
+
+    /*
+     * Constrain x && y appropriately, and adjust x if it is not in the
+     * column titles to change display coords into internal coords.
+     */
+    x = MAX(0, x); y = MAX(0, y);
+    x -= tablePtr->highlightWidth; y -= tablePtr->highlightWidth;
+    x += (x < tablePtr->colStarts[tablePtr->titleCols]) ? 0 :
+       tablePtr->colStarts[tablePtr->leftCol] -
+       tablePtr->colStarts[tablePtr->titleCols];
+    x = MIN(x, tablePtr->maxWidth - 1);
+    for (i = 1; (i <= tablePtr->cols) &&
+            (x + (bd[0] + bd[1])) >= tablePtr->colStarts[i]; i++);
+    if (x > tablePtr->colStarts[--i] + bd[4]) {
+       borders--;
+       *col = -1;
+       bcol = (i < tablePtr->leftCol && i >= tablePtr->titleCols) ?
+           tablePtr->titleCols-1 : i-1;
+    } else {
+       bcol = *col = (i < tablePtr->leftCol && i >= tablePtr->titleCols) ?
+           tablePtr->titleCols-1 : i-1;
+    }
+    y += (y < tablePtr->rowStarts[tablePtr->titleRows]) ? 0 :
+       tablePtr->rowStarts[tablePtr->topRow] -
+       tablePtr->rowStarts[tablePtr->titleRows];
+    y = MIN(y, tablePtr->maxHeight - 1);
+    for (i = 1; i <= tablePtr->rows &&
+            (y + (bd[2] + bd[3])) >= tablePtr->rowStarts[i]; i++);
+    if (y > tablePtr->rowStarts[--i]+bd[5]) {
+       borders--;
+       *row = -1;
+       brow = (i < tablePtr->topRow && i >= tablePtr->titleRows) ?
+           tablePtr->titleRows-1 : i-1;
+    } else {
+       brow = *row = (i < tablePtr->topRow && i >= tablePtr->titleRows) ?
+           tablePtr->titleRows-1 : i-1;
+    }
+    /*
+     * We have to account for spanning cells, which may hide cells.
+     * In that case, we have to decrement our border count.
+     */
+    if (tablePtr->spanAffTbl && !(tablePtr->flags & AVOID_SPANS) && borders) {
+       char buf1[INDEX_BUFSIZE], buf2[INDEX_BUFSIZE];
+
+       if (*row != -1) {
+           TableMakeArrayIndex(brow+tablePtr->rowOffset,
+                               bcol+tablePtr->colOffset+1, buf1);
+           TableMakeArrayIndex(brow+tablePtr->rowOffset+1,
+                               bcol+tablePtr->colOffset+1, buf2);
+           if (Tcl_FindHashEntry(tablePtr->spanAffTbl, buf1) != NULL &&
+               Tcl_FindHashEntry(tablePtr->spanAffTbl, buf2) != NULL) {
+               borders--;
+               *row = -1;
+           }
+       }
+       if (*col != -1) {
+           TableMakeArrayIndex(brow+tablePtr->rowOffset+1,
+                               bcol+tablePtr->colOffset, buf1);
+           TableMakeArrayIndex(brow+tablePtr->rowOffset+1,
+                               bcol+tablePtr->colOffset+1, buf2);
+           if (Tcl_FindHashEntry(tablePtr->spanAffTbl, buf1) != NULL &&
+               Tcl_FindHashEntry(tablePtr->spanAffTbl, buf2) != NULL) {
+               borders--;
+               *col = -1;
+           }
+       }
+    }
+    return borders;
 }
 
 /*
@@ -226,56 +449,97 @@ TableAtBorder(Table * tablePtr, int x, int y, int *row, int *col)
 char *
 TableGetCellValue(Table *tablePtr, int r, int c)
 {
-  register Tcl_Interp *interp = tablePtr->interp;
-  char *result = NULL;
-  char buf[INDEX_BUFSIZE];
-  Tcl_HashEntry *entryPtr = NULL;
-  int new = 1;
-
-  TableMakeArrayIndex(r, c, buf);
-
-  if (tablePtr->caching) {
-    /* if we are caching, let's see if we have the value cached */
-    entryPtr = Tcl_CreateHashEntry(tablePtr->cache, buf, &new);
-    if (!new) {
-      result = (char *) Tcl_GetHashValue(entryPtr);
-      return result?result:"";
-    }
-  }
-  if (tablePtr->command && tablePtr->useCmd) {
-    Tcl_DString script;
-    Tcl_DStringInit(&script);
-    ExpandPercents(tablePtr, tablePtr->command, r, c, "", (char *)NULL,
-                  0, &script, 0);
-    if (Tcl_GlobalEval(interp, Tcl_DStringValue(&script)) == TCL_ERROR) {
-      tablePtr->useCmd = 0;
-      tablePtr->dataSource &= ~DATA_COMMAND;
-      if (tablePtr->arrayVar)
-       tablePtr->dataSource |= DATA_ARRAY;
-      Tcl_AddErrorInfo(interp, "\n\t(in command executed by table)");
-      Tcl_AddErrorInfo(interp, Tcl_DStringValue(&script));
-      Tk_BackgroundError(interp);
-      TableInvalidateAll(tablePtr, 0);
-    } else {
-      result = Tcl_GetStringResult(interp);
-    }
-    Tcl_FreeResult(interp);
-    Tcl_DStringFree(&script);
-  } else if (tablePtr->arrayVar) {
-    result = Tcl_GetVar2(interp, tablePtr->arrayVar, buf, TCL_GLOBAL_ONLY);
-  }
-  if (result == NULL)
-    result = "";
-  if (tablePtr->caching && entryPtr != NULL) {
-    /* if we are caching, make sure we cache the returned value */
-    /* entryPtr will have been set from above, but check to make sure
-     * someone didn't change caching during -command evaluation */
-    char *val;
-    val = (char *)ckalloc(strlen(result)+1);
-    strcpy(val, result);
-    Tcl_SetHashValue(entryPtr, val);
-  }
-  return result;
+    register Tcl_Interp *interp = tablePtr->interp;
+    char *result = NULL;
+    char buf[INDEX_BUFSIZE];
+    Tcl_HashEntry *entryPtr = NULL;
+    int new = 1;
+
+    TableMakeArrayIndex(r, c, buf);
+
+    if (tablePtr->caching) {
+       /*
+        * If we are caching, let's see if we have the value cached
+        */
+       entryPtr = Tcl_CreateHashEntry(tablePtr->cache, buf, &new);
+       if (!new) {
+           result = (char *) Tcl_GetHashValue(entryPtr);
+           if (result == NULL) {
+               result = "";
+           }
+           goto VALUE;
+       }
+    }
+    if (tablePtr->command && tablePtr->useCmd) {
+       Tcl_DString script;
+       Tcl_DStringInit(&script);
+       ExpandPercents(tablePtr, tablePtr->command, r, c, "", (char *)NULL,
+                      0, &script, 0);
+       if (Tcl_GlobalEval(interp, Tcl_DStringValue(&script)) == TCL_ERROR) {
+           tablePtr->useCmd = 0;
+           tablePtr->dataSource &= ~DATA_COMMAND;
+           if (tablePtr->arrayVar)
+               tablePtr->dataSource |= DATA_ARRAY;
+           Tcl_AddErrorInfo(interp, "\n\t(in -command evaled by table)");
+           Tcl_AddErrorInfo(interp, Tcl_DStringValue(&script));
+           Tcl_BackgroundError(interp);
+           TableInvalidateAll(tablePtr, 0);
+       } else {
+           result = Tcl_GetStringResult(interp);
+       }
+       Tcl_FreeResult(interp);
+       Tcl_DStringFree(&script);
+    } else if (tablePtr->arrayVar) {
+       result = Tcl_GetVar2(interp, tablePtr->arrayVar, buf, TCL_GLOBAL_ONLY);
+    }
+    if (result == NULL)
+       result = "";
+    if (tablePtr->caching && entryPtr != NULL) {
+       /*
+        * If we are caching, make sure we cache the returned value
+        *
+        * entryPtr will have been set from above, but check to make sure
+        * someone didn't change caching during -command evaluation.
+        */
+       char *val;
+       val = (char *)ckalloc(strlen(result)+1);
+       strcpy(val, result);
+       Tcl_SetHashValue(entryPtr, val);
+    }
+VALUE:
+#ifdef PROCS
+    if (result != NULL) {
+       /* Do we have procs, are we showing their value, is this a proc? */
+       if (tablePtr->hasProcs && !tablePtr->showProcs && *result == '=' &&
+           !(r-tablePtr->rowOffset == tablePtr->activeRow &&
+             c-tablePtr->colOffset == tablePtr->activeCol)) {
+           Tcl_DString script;
+           /* provides a rough mutex on preventing proc loops */
+           entryPtr = Tcl_CreateHashEntry(tablePtr->inProc, buf, &new);
+           if (!new) {
+               Tcl_SetHashValue(entryPtr, 1);
+               Tcl_AddErrorInfo(interp, "\n\t(loop hit in proc evaled by table)");
+               return result;
+           }
+           Tcl_SetHashValue(entryPtr, 0);
+           Tcl_DStringInit(&script);
+           ExpandPercents(tablePtr, result+1, r, c, result+1, (char *)NULL,
+                          0, &script, 0);
+           if (Tcl_GlobalEval(interp, Tcl_DStringValue(&script)) != TCL_OK ||
+               Tcl_GetHashValue(entryPtr) == 1) {
+               Tcl_AddErrorInfo(interp, "\n\tin proc evaled by table:\n");
+               Tcl_AddErrorInfo(interp, Tcl_DStringValue(&script));
+               Tcl_BackgroundError(interp);
+           } else {
+               result = Tcl_GetStringResult(interp);
+           }
+           Tcl_FreeResult(interp);
+           Tcl_DStringFree(&script);      
+           Tcl_DeleteHashEntry(entryPtr);
+       }
+    }
+#endif
+    return (result?result:"");
 }
 
 /*
@@ -299,123 +563,160 @@ TableGetCellValue(Table *tablePtr, int r, int c)
 int
 TableSetCellValue(Table *tablePtr, int r, int c, char *value)
 {
-  register Tcl_Interp *interp = tablePtr->interp;
-  char buf[INDEX_BUFSIZE];
-  int code = TCL_OK;
-
-  TableMakeArrayIndex(r, c, buf);
-
-  if (tablePtr->state == STATE_DISABLED)
-    return code;
-  if (tablePtr->command && tablePtr->useCmd) {
-    Tcl_DString script;
-
-    Tcl_DStringInit(&script);
-    ExpandPercents(tablePtr, tablePtr->command, r, c, value, (char *)NULL,
-                  1, &script, 0);
-    if (Tcl_GlobalEval(interp, Tcl_DStringValue(&script)) == TCL_ERROR) {
-      /* An error resulted.  Prevent further triggering of the command
-       * and set up the error message. */
-      tablePtr->useCmd = 0;
-      tablePtr->dataSource &= ~DATA_COMMAND;
-      if (tablePtr->arrayVar)
-       tablePtr->dataSource |= DATA_ARRAY;
-      Tcl_AddErrorInfo(interp, "\n\t(in command executed by table)");
-      Tk_BackgroundError(interp);
-      code = TCL_ERROR;
-    }
-    Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
-    Tcl_DStringFree(&script);
-  } else if (tablePtr->arrayVar) {
-    if (value == NULL || *value == '\0') {
-      Tcl_UnsetVar2(interp, tablePtr->arrayVar, buf, TCL_GLOBAL_ONLY);
-    } else if (Tcl_SetVar2(interp, tablePtr->arrayVar, buf, value,
-                          TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
-      code = TCL_ERROR;
-    }
-  }
-  if (tablePtr->caching && code == TCL_OK) {
-    Tcl_HashEntry *entryPtr;
-    int new;
-    char *val;
-
-    val = (char *)ckalloc(strlen(value)+1);
-    strcpy(val, value);
-    entryPtr = Tcl_CreateHashEntry(tablePtr->cache, buf, &new);
-    Tcl_SetHashValue(entryPtr, val);
-  }
-  return code;
-}
+    register Tcl_Interp *interp = tablePtr->interp;
+    char buf[INDEX_BUFSIZE];
+    int code = TCL_OK, flash = 0;
 
-/*
- *----------------------------------------------------------------------
- *
- * TableSortCompareProc --
- *     This procedure is invoked by qsort to determine the proper
- *     ordering between two elements.
- *
- * Results:
- *     < 0 means first is "smaller" than "second", > 0 means "first"
- *     is larger than "second", and 0 means they should be treated
- *     as equal.
- *
- * Side effects:
- *     None, unless a user-defined comparison command does something
- *     weird.
- *
- *----------------------------------------------------------------------
- */
-static int
-TableSortCompareProc(first, second)
-    CONST VOID *first, *second;                /* Elements to be compared. */
-{
-    int r1, c1, r2, c2;
-    char *firstString = *((char **) first);
-    char *secondString = *((char **) second);
-
-    /* This doesn't account for badly formed indices */
-    sscanf(firstString, "%d,%d", &r1, &c1);
-    sscanf(secondString, "%d,%d", &r2, &c2);
-    if (r1 > r2)
-      return 1;
-    else if (r1 < r2)
-      return -1;
-    else if (c1 > c2)
-      return 1;
-    else if (c1 < c2)
-      return -1;
-    return 0;
+    TableMakeArrayIndex(r, c, buf);
+
+    if (tablePtr->state == STATE_DISABLED) {
+       return TCL_OK;
+    }
+    if (tablePtr->command && tablePtr->useCmd) {
+       Tcl_DString script;
+
+       Tcl_DStringInit(&script);
+       ExpandPercents(tablePtr, tablePtr->command, r, c, value, (char *)NULL,
+                      1, &script, 0);
+       if (Tcl_GlobalEval(interp, Tcl_DStringValue(&script)) == TCL_ERROR) {
+           /* An error resulted.  Prevent further triggering of the command
+            * and set up the error message. */
+           tablePtr->useCmd = 0;
+           tablePtr->dataSource &= ~DATA_COMMAND;
+           if (tablePtr->arrayVar)
+               tablePtr->dataSource |= DATA_ARRAY;
+           Tcl_AddErrorInfo(interp, "\n\t(in command executed by table)");
+           Tcl_BackgroundError(interp);
+           code = TCL_ERROR;
+       } else {
+           flash = 1;
+       }
+       Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
+       Tcl_DStringFree(&script);
+    } else if (tablePtr->arrayVar) {
+       /* Warning: checking for \0 as the first char could invalidate
+        * allowing it as a valid first char */
+       if ((value == NULL || *value == '\0') && tablePtr->sparse) {
+           Tcl_UnsetVar2(interp, tablePtr->arrayVar, buf, TCL_GLOBAL_ONLY);
+       } else if (Tcl_SetVar2(interp, tablePtr->arrayVar, buf, value,
+                              TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
+           code = TCL_ERROR;
+       }
+    }
+    if (code == TCL_ERROR) {
+       return TCL_ERROR;
+    }
+
+    if (tablePtr->caching) {
+       Tcl_HashEntry *entryPtr;
+       int new;
+       char *val;
+
+       entryPtr = Tcl_CreateHashEntry(tablePtr->cache, buf, &new);
+       if (!new) {
+           val = (char *) Tcl_GetHashValue(entryPtr);
+           if (val) ckfree(val);
+       }
+       val = (char *)ckalloc(strlen(value)+1);
+       strcpy(val, value);
+       Tcl_SetHashValue(entryPtr, val);
+       flash = 1;
+    }
+    /* We do this conditionally because the var array already has
+     * it's own check to flash */
+    if (flash && tablePtr->flashMode) {
+       r -= tablePtr->rowOffset;
+       c -= tablePtr->colOffset;
+       TableAddFlash(tablePtr, r, c);
+       TableRefresh(tablePtr, r, c, CELL);
+    }
+    return TCL_OK;
 }
 
 /*
  *----------------------------------------------------------------------
  *
- * TableCellSort --
- *     Sort a list of table cell elements (of form row,col)
+ * TableMoveCellValue --
+ *     To move cells faster on delete/insert line or col when cache is on
+ *     and variable, command is off.
+ *     To avoid another call to TableMakeArrayIndex(r, c, buf),
+ *     we optionally provide the buffers.
+ *     outOfBounds means we will just set the cell value to ""
  *
  * Results:
- *     Returns the sorted list of elements.  Because Tcl_Merge allocs
- *     the space for result, it must later be ckfree'd by caller.
+ *     Returns TCL_ERROR or TCL_OK, depending on whether an error
+ *     occured during set (ie: during evaluation of -command).
  *
  * Side effects:
- *     Behaviour undefined for ill-formed input list of elements.
+ *     If the value is NULL (empty string), it will be unset from
+ *     an array rather than set to the empty string.
  *
  *----------------------------------------------------------------------
  */
-char *
-TableCellSort(Table *tablePtr, char *str)
+int
+TableMoveCellValue(Table *tablePtr, int fromr, int fromc, char *frombuf,
+       int tor, int toc, char *tobuf, int outOfBounds)
 {
-  int listArgc;
-  char **listArgv;
-  char *result;
-
-  if (Tcl_SplitList(tablePtr->interp, str, &listArgc, &listArgv) != TCL_OK)
-    return str;
-  qsort((VOID *) listArgv, (size_t) listArgc, sizeof (char *),
-       TableSortCompareProc);
-  result = Tcl_Merge(listArgc, listArgv);
-  ckfree((char *) listArgv);
-  return result;
+    int new;
+    char *result = NULL;
+    Tcl_Interp *interp = tablePtr->interp;
+
+    if (outOfBounds) {
+       return TableSetCellValue(tablePtr, tor, toc, "");
+    }
+
+    if (tablePtr->caching && (!(tablePtr->command && tablePtr->useCmd))) {
+       Tcl_HashEntry *entryPtr;
+       /*
+        * if we are caching, let's see if we have the value cached
+        */
+       entryPtr = Tcl_CreateHashEntry(tablePtr->cache, frombuf, &new);
+       if (!new) {
+           char *val;
+           result = (char *) Tcl_GetHashValue(entryPtr);
+           /*
+            * we set tho old value to NULL
+            */
+           Tcl_SetHashValue(entryPtr, NULL);
+
+           /*
+            * set the destination to the source pointer without new mallocing!
+            */
+           entryPtr = Tcl_CreateHashEntry(tablePtr->cache, tobuf, &new);
+           /*
+            * free old value
+            */
+           if (!new) {
+               val = (char *) Tcl_GetHashValue(entryPtr);
+               if (val) ckfree(val);
+           }
+           Tcl_SetHashValue(entryPtr, result);
+           if (tablePtr->arrayVar) {
+               /*
+                * first, delete from var.
+                */
+               Tcl_UnsetVar2(interp, tablePtr->arrayVar, frombuf,
+                       TCL_GLOBAL_ONLY);
+               /*
+                * Warning: checking for \0 as the first char could invalidate
+                * allowing it as a valid first char
+                */
+               if (Tcl_SetVar2(interp, tablePtr->arrayVar, tobuf, result,
+                       TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
+                   return TCL_ERROR;
+               }
+           }     
+      
+
+           return TCL_OK;
+       }
+    }
+    /*
+     * We have to do it the old way
+     */
+    return TableSetCellValue(tablePtr, tor, toc,
+           TableGetCellValue(tablePtr, fromr, fromc));
+
 }
 
 /*
@@ -437,30 +738,32 @@ TableCellSort(Table *tablePtr, char *str)
 int
 TableGetIcursor(Table *tablePtr, char *arg, int *posn)
 {
-  int tmp, len;
-#if (TK_MINOR_VERSION > 0)
-  len = Tcl_NumUtfChars(tablePtr->activeBuf, strlen(tablePtr->activeBuf));
-#else
-  len = strlen(tablePtr->activeBuf);
+    int tmp, len;
+
+    len = strlen(tablePtr->activeBuf);
+#ifdef TCL_UTF_MAX
+    /* Need to base it off strlen to account for \x00 (Unicode null) */
+    len = Tcl_NumUtfChars(tablePtr->activeBuf, len);
 #endif
-  /* ensure icursor didn't get out of sync */
-  if (tablePtr->icursor > len) tablePtr->icursor = len;
-  /* is this end */
-  if (strcmp(arg, "end") == 0) {
-    tmp = len;
-  } else if (strcmp(arg, "insert") == 0) {
-    tmp = tablePtr->icursor;
-  } else {
-    if (Tcl_GetInt(tablePtr->interp, arg, &tmp) != TCL_OK) {
-      return TCL_ERROR;
-    }
-    tmp = MIN(MAX(0, tmp), len);
-  }
-  if (posn)
-    *posn = tmp;
-  else
-    tablePtr->icursor = tmp;
-  return TCL_OK;
+    /* ensure icursor didn't get out of sync */
+    if (tablePtr->icursor > len) tablePtr->icursor = len;
+    /* is this end */
+    if (strcmp(arg, "end") == 0) {
+       tmp = len;
+    } else if (strcmp(arg, "insert") == 0) {
+       tmp = tablePtr->icursor;
+    } else {
+       if (Tcl_GetInt(tablePtr->interp, arg, &tmp) != TCL_OK) {
+           return TCL_ERROR;
+       }
+       CONSTRAIN(tmp, 0, len);
+    }
+    if (posn) {
+       *posn = tmp;
+    } else {
+       tablePtr->icursor = tmp;
+    }
+    return TCL_OK;
 }
 
 /*
@@ -483,81 +786,598 @@ TableGetIcursor(Table *tablePtr, char *arg, int *posn)
  */
 int
 TableGetIndex(tablePtr, str, row_p, col_p)
-     register Table *tablePtr; /* Table for which the index is being
+    register Table *tablePtr;  /* Table for which the index is being
                                 * specified. */
-     char *str;                        /* Symbolic specification of cell in table. */
-     int *row_p;               /* Where to store converted row. */
-     int *col_p;               /* Where to store converted col. */
+    char *str;                 /* Symbolic specification of cell in table. */
+    int *row_p;                /* Where to store converted row. */
+    int *col_p;                /* Where to store converted col. */
 {
-  int r, c, len = strlen(str);
-
-  /*
-   * Note that all of these values will be adjusted by row/ColOffset
-   */
-  if (str[0] == '@') {                         /* @x,y coordinate */ 
-    int x, y;
-    char *p, *end;
-
-    p = str+1;
-    x = strtol(p, &end, 0);
-    if ((end == p) || (*end != ','))
-      goto IndexError;
-    p = end+1;
-    y = strtol(p, &end, 0);
-    if ((end == p) || (*end != '\0'))
-      goto IndexError;
-    TableWhatCell(tablePtr, x, y, &r, &c);
-    r += tablePtr->rowOffset;
-    c += tablePtr->colOffset;
-  } else if (sscanf(str, "%d,%d", &r,&c) == 2) {
-    char buf[INDEX_BUFSIZE];
-    TableMakeArrayIndex(r, c, buf);
-    /* Make sure it won't work for "2,3extrastuff" */
-    if (strcmp(buf, str))
-      goto IndexError;
-    /* ensure appropriate user index */
-    r = MIN(MAX(tablePtr->rowOffset,r),tablePtr->rows-1+tablePtr->rowOffset);
-    c = MIN(MAX(tablePtr->colOffset,c),tablePtr->cols-1+tablePtr->colOffset);
-  } else if (len > 1 && strncmp(str, "active", len) == 0 ) {   /* active */
-    if (tablePtr->flags & HAS_ACTIVE) {
-      r = tablePtr->activeRow+tablePtr->rowOffset;
-      c = tablePtr->activeCol+tablePtr->colOffset;
+    int r, c, len = strlen(str);
+    char dummy;
+
+    /*
+     * Note that all of these values will be adjusted by row/ColOffset
+     */
+    if (str[0] == '@') {       /* @x,y coordinate */ 
+       int x, y;
+
+       if (sscanf(str+1, "%d,%d%c", &x, &y, &dummy) != 2) {
+           /* Make sure it won't work for "2,3extrastuff" */
+           goto IndexError;
+       }
+       TableWhatCell(tablePtr, x, y, &r, &c);
+       r += tablePtr->rowOffset;
+       c += tablePtr->colOffset;
+    } else if (*str == '-' || isdigit(str[0])) {
+       if (sscanf(str, "%d,%d%c", &r, &c, &dummy) != 2) {
+           /* Make sure it won't work for "2,3extrastuff" */
+           goto IndexError;
+       }
+       /* ensure appropriate user index */
+       CONSTRAIN(r, tablePtr->rowOffset,
+               tablePtr->rows-1+tablePtr->rowOffset);
+       CONSTRAIN(c, tablePtr->colOffset,
+               tablePtr->cols-1+tablePtr->colOffset);
+    } else if (len > 1 && strncmp(str, "active", len) == 0 ) { /* active */
+       if (tablePtr->flags & HAS_ACTIVE) {
+           r = tablePtr->activeRow+tablePtr->rowOffset;
+           c = tablePtr->activeCol+tablePtr->colOffset;
+       } else {
+           Tcl_SetStringObj(Tcl_GetObjResult(tablePtr->interp),
+                            "no \"active\" cell in table", -1);
+           return TCL_ERROR;
+       }
+    } else if (len > 1 && strncmp(str, "anchor", len) == 0) {  /* anchor */
+       if (tablePtr->flags & HAS_ANCHOR) {
+           r = tablePtr->anchorRow+tablePtr->rowOffset;
+           c = tablePtr->anchorCol+tablePtr->colOffset;
+       } else {
+           Tcl_SetStringObj(Tcl_GetObjResult(tablePtr->interp),
+                            "no \"anchor\" cell in table", -1);
+           return TCL_ERROR;
+       }
+    } else if (strncmp(str, "end", len) == 0) {                /* end */
+       r = tablePtr->rows-1+tablePtr->rowOffset;
+       c = tablePtr->cols-1+tablePtr->colOffset;
+    } else if (strncmp(str, "origin", len) == 0) {     /* origin */
+       r = tablePtr->titleRows+tablePtr->rowOffset;
+       c = tablePtr->titleCols+tablePtr->colOffset;
+    } else if (strncmp(str, "topleft", len) == 0) {    /* topleft */
+       r = tablePtr->topRow+tablePtr->rowOffset;
+       c = tablePtr->leftCol+tablePtr->colOffset;
+    } else if (strncmp(str, "bottomright", len) == 0) {        /* bottomright */
+       /*
+        * FIX: Should this avoid spans, or consider them in the bottomright?
+        tablePtr->flags |= AVOID_SPANS;
+        tablePtr->flags &= ~AVOID_SPANS;
+        */
+       TableGetLastCell(tablePtr, &r, &c);
+       r += tablePtr->rowOffset;
+       c += tablePtr->colOffset;
+    } else {
+    IndexError:
+       Tcl_AppendStringsToObj(Tcl_GetObjResult(tablePtr->interp),
+               "bad table index \"", str, "\": must be active, anchor, end, ",
+               "origin, topleft, bottomright, @x,y, or <row>,<col>",
+               (char *)NULL);
+       return TCL_ERROR;
+    }
+
+    /* Note: values are expected to be properly constrained 
+     * as a user index by this point */
+    if (row_p) *row_p = r;
+    if (col_p) *col_p = c;
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_SetCmd --
+ *     This procedure is invoked to process the set method
+ *     that corresponds to a widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_SetCmd(ClientData clientData, register Tcl_Interp *interp,
+            int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *)clientData;
+    int row, col, len, i, j, max;
+    char *str;
+
+    /* sets any number of tags/indices to a given value */
+    if (objc < 3) {
+    CMD_SET_USAGE:
+       Tcl_WrongNumArgs(interp, 2, objv,
+                        "?row|col? index ?value? ?index value ...?");
+       return TCL_ERROR;
+    }
+
+    /* make sure there is a data source to accept set */
+    if (tablePtr->dataSource == DATA_NONE) {
+       return TCL_OK;
+    }
+
+    str = Tcl_GetStringFromObj(objv[2], &len);
+    if (strncmp(str, "row", len) == 0 || strncmp(str, "col", len) == 0) {
+       Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
+       /* set row index list ?index list ...? */
+       if (objc < 4) {
+           goto CMD_SET_USAGE;
+       } else if (objc == 4) {
+           if (TableGetIndexObj(tablePtr, objv[3],
+                                &row, &col) != TCL_OK) {
+               return TCL_ERROR;
+           }
+           if (*str == 'r') {
+               max = tablePtr->cols+tablePtr->colOffset;
+               for (i=col; i<max; i++) {
+                   str = TableGetCellValue(tablePtr, row, i);
+                   Tcl_ListObjAppendElement(NULL, resultPtr,
+                                            Tcl_NewStringObj(str, -1));
+               }
+           } else {
+               max = tablePtr->rows+tablePtr->rowOffset;
+               for (i=row; i<max; i++) {
+                   str = TableGetCellValue(tablePtr, i, col);
+                   Tcl_ListObjAppendElement(NULL, resultPtr,
+                                            Tcl_NewStringObj(str, -1));
+               }
+           }
+       } else if (tablePtr->state == STATE_NORMAL) {
+           int listc;
+           Tcl_Obj **listv;
+           /* make sure there are an even number of index/list pairs */
+           if (objc & 0) {
+               goto CMD_SET_USAGE;
+           }
+           for (i = 3; i < objc-1; i += 2) {
+               if ((TableGetIndexObj(tablePtr, objv[i],
+                                     &row, &col) != TCL_OK) ||
+                   (Tcl_ListObjGetElements(interp, objv[i+1],
+                                           &listc, &listv) != TCL_OK)) {
+                   return TCL_ERROR;
+               }
+               if (*str == 'r') {
+                   max = col+MIN(tablePtr->cols+tablePtr->colOffset-col,
+                                 listc);
+                   for (j = col; j < max; j++) {
+                       if (TableSetCellValue(tablePtr, row, j,
+                                             Tcl_GetString(listv[j-col]))
+                           != TCL_OK) {
+                           return TCL_ERROR;
+                       }
+                       if (row-tablePtr->rowOffset == tablePtr->activeRow &&
+                           j-tablePtr->colOffset == tablePtr->activeCol) {
+                           TableGetActiveBuf(tablePtr);
+                       }
+                       TableRefresh(tablePtr, row-tablePtr->rowOffset,
+                                    j-tablePtr->colOffset, CELL);
+                   }
+               } else {
+                   max = row+MIN(tablePtr->rows+tablePtr->rowOffset-row,
+                                 listc);
+                   for (j = row; j < max; j++) {
+                       if (TableSetCellValue(tablePtr, j, col,
+                                             Tcl_GetString(listv[j-row]))
+                           != TCL_OK) {
+                           return TCL_ERROR;
+                       }
+                       if (j-tablePtr->rowOffset == tablePtr->activeRow &&
+                           col-tablePtr->colOffset == tablePtr->activeCol) {
+                           TableGetActiveBuf(tablePtr);
+                       }
+                       TableRefresh(tablePtr, j-tablePtr->rowOffset,
+                                    col-tablePtr->colOffset, CELL);
+                   }
+               }
+           }
+       }
+    } else if (objc == 3) {
+       /* set index */
+       if (TableGetIndexObj(tablePtr, objv[2], &row, &col) != TCL_OK) {
+           return TCL_ERROR;
+       } else {
+           /*
+            * Cannot use Tcl_GetObjResult here because TableGetCellValue
+            * can corrupt the resultPtr.
+            */
+           Tcl_SetObjResult(interp, Tcl_NewStringObj(
+               TableGetCellValue(tablePtr, row, col),-1));
+       }
+    } else {
+       /* set index val ?index val ...? */
+       /* make sure there are an even number of index/value pairs */
+       if (objc & 1) {
+           goto CMD_SET_USAGE;
+       }
+       for (i = 2; i < objc-1; i += 2) {
+           if ((TableGetIndexObj(tablePtr, objv[i], &row, &col) != TCL_OK) ||
+               (TableSetCellValue(tablePtr, row, col,
+                                  Tcl_GetString(objv[i+1])) != TCL_OK)) {
+               return TCL_ERROR;
+           }
+           row -= tablePtr->rowOffset;
+           col -= tablePtr->colOffset;
+           if (row == tablePtr->activeRow && col == tablePtr->activeCol) {
+               TableGetActiveBuf(tablePtr);
+           }
+           TableRefresh(tablePtr, row, col, CELL);
+       }
+    }
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_SpanSet --
+ *     Takes row,col in user coords and sets a span on the
+ *     cell if possible
+ *
+ * Results:
+ *     A standard Tcl result
+ *
+ * Side effects:
+ *     The span can be constrained
+ *
+ *--------------------------------------------------------------
+ */
+static int
+Table_SpanSet(register Table *tablePtr, int urow, int ucol, int rs, int cs)
+{
+    Tcl_Interp *interp = tablePtr->interp;
+    int i, j, new, ors, ocs, result = TCL_OK;
+    int row, col;
+    Tcl_HashEntry *entryPtr;
+    Tcl_HashSearch search;
+    char *dbuf, buf[INDEX_BUFSIZE], cell[INDEX_BUFSIZE], span[INDEX_BUFSIZE];
+
+    row = urow - tablePtr->rowOffset;
+    col = ucol - tablePtr->colOffset;
+
+    TableMakeArrayIndex(urow, ucol, cell);
+
+    if (tablePtr->spanTbl == NULL) {
+       tablePtr->spanTbl = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
+       Tcl_InitHashTable(tablePtr->spanTbl, TCL_STRING_KEYS);
+       tablePtr->spanAffTbl = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
+       Tcl_InitHashTable(tablePtr->spanAffTbl, TCL_STRING_KEYS);
+    }
+
+    /* first check in the affected cells table */
+    if ((entryPtr=Tcl_FindHashEntry(tablePtr->spanAffTbl, cell)) != NULL) {
+       /* We have to make sure this was not already hidden
+        * that's an error */
+       if ((char *)Tcl_GetHashValue(entryPtr) != NULL) {
+           Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+                                  "cannot set spanning on hidden cell ",
+                                  cell, (char *) NULL);
+           return TCL_ERROR;
+       }
+    }
+    /* do constraints on the spans
+     * title cells must not expand beyond the titles
+     * other cells can't expand negatively into title area
+     */
+    if ((row < tablePtr->titleRows) &&
+       (row + rs >= tablePtr->titleRows)) {
+       rs = tablePtr->titleRows - row - 1;
+    }
+    if ((col < tablePtr->titleCols) &&
+       (col + cs >= tablePtr->titleCols)) {
+       cs = tablePtr->titleCols - col - 1;
+    }
+    rs = MAX(0, rs);
+    cs = MAX(0, cs);
+
+    /* then work in the span cells table */
+    if ((entryPtr = Tcl_FindHashEntry(tablePtr->spanTbl, cell)) != NULL) {
+       /* We have to readjust for what was there first */
+       TableParseArrayIndex(&ors, &ocs, (char *)Tcl_GetHashValue(entryPtr));
+       ckfree((char *) Tcl_GetHashValue(entryPtr));
+       Tcl_DeleteHashEntry(entryPtr);
+       for (i = urow; i <= urow+ors; i++) {
+           for (j = ucol; j <= ucol+ocs; j++) {
+               TableMakeArrayIndex(i, j, buf);
+               entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl, buf);
+               if (entryPtr != NULL) {
+                   Tcl_DeleteHashEntry(entryPtr);
+               }
+               TableRefresh(tablePtr, i-tablePtr->rowOffset,
+                            j-tablePtr->colOffset, CELL);
+           }
+       }
     } else {
-      Tcl_AppendResult(tablePtr->interp, "no \"active\" cell in table", NULL);
-      return TCL_ERROR;
+       ors = ocs = 0;
+    }
+
+    /* calc to make sure that span is OK */
+    for (i = urow; i <= urow+rs; i++) {
+       for (j = ucol; j <= ucol+cs; j++) {
+           TableMakeArrayIndex(i, j, buf);
+           entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl, buf);
+           if (entryPtr != NULL) {
+               /* Something already spans here */
+               Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+                                      "cannot overlap already spanned cell ",
+                                      buf, (char *) NULL);
+               result = TCL_ERROR;
+               rs = ors;
+               cs = ocs;
+               break;
+           }
+       }
+       if (result == TCL_ERROR)
+           break;
+    }
+
+    /* 0,0 span means set to unspanned again */
+    if (rs == 0 && cs == 0) {
+       entryPtr = Tcl_FindHashEntry(tablePtr->spanTbl, cell);
+       if (entryPtr != NULL) {
+           ckfree((char *) Tcl_GetHashValue(entryPtr));
+           Tcl_DeleteHashEntry(entryPtr);
+       }
+       entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl, cell);
+       if (entryPtr != NULL) {
+           Tcl_DeleteHashEntry(entryPtr);
+       }
+       if (Tcl_FirstHashEntry(tablePtr->spanTbl, &search) == NULL) {
+           /* There are no more spans, so delete tables to improve
+            * performance of TableCellCoords */
+           Tcl_DeleteHashTable(tablePtr->spanTbl);
+           ckfree((char *) (tablePtr->spanTbl));
+           Tcl_DeleteHashTable(tablePtr->spanAffTbl);
+           ckfree((char *) (tablePtr->spanAffTbl));
+           tablePtr->spanTbl = NULL;
+           tablePtr->spanAffTbl = NULL;
+       }
+       return result;
+    }
+
+    /* Make sure there is no extra stuff */
+    TableMakeArrayIndex(rs, cs, span);
+
+    /* Set affected cell table to a NULL value */
+    entryPtr = Tcl_CreateHashEntry(tablePtr->spanAffTbl, cell, &new);
+    Tcl_SetHashValue(entryPtr, (char *) NULL);
+    /* set the spanning cells table with span value */
+    entryPtr = Tcl_CreateHashEntry(tablePtr->spanTbl, cell, &new);
+    dbuf = (char *)ckalloc(strlen(span)+1);
+    strcpy(dbuf, span);
+    Tcl_SetHashValue(entryPtr, dbuf);
+    dbuf = Tcl_GetHashKey(tablePtr->spanTbl, entryPtr);
+    /* Set other affected cells */
+    EmbWinUnmap(tablePtr, row, row + rs, col, col + cs);
+    for (i = urow; i <= urow+rs; i++) {
+       for (j = ucol; j <= ucol+cs; j++) {
+           TableMakeArrayIndex(i, j, buf);
+           entryPtr = Tcl_CreateHashEntry(tablePtr->spanAffTbl, buf, &new);
+           if (!(i == urow && j == ucol)) {
+               Tcl_SetHashValue(entryPtr, (char *) dbuf);
+           }
+       }
+    }
+    TableRefresh(tablePtr, row, col, CELL);
+    return result;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_SpanCmd --
+ *     This procedure is invoked to process the span method
+ *     that corresponds to a widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_SpanCmd(ClientData clientData, register Tcl_Interp *interp,
+             int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    int rs, cs, row, col, i;
+    Tcl_HashEntry *entryPtr;
+    Tcl_Obj *objPtr, *resultPtr;
+
+    if (objc < 2 || (objc > 4 && (objc&1))) {
+       Tcl_WrongNumArgs(interp, 2, objv,
+                        "?index? ?rows,cols index rows,cols ...?");
+       return TCL_ERROR;
     }
-  } else if (len > 1 && strncmp(str, "anchor", len) == 0) {    /* anchor */
-    if (tablePtr->flags & HAS_ANCHOR) {
-      r = tablePtr->anchorRow+tablePtr->rowOffset;
-      c = tablePtr->anchorCol+tablePtr->colOffset;
+
+    resultPtr = Tcl_GetObjResult(interp);
+
+    if (objc == 2) {
+       if (tablePtr->spanTbl) {
+           Tcl_HashSearch search;
+
+           for (entryPtr = Tcl_FirstHashEntry(tablePtr->spanTbl, &search);
+                entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
+               objPtr = Tcl_NewStringObj(Tcl_GetHashKey(tablePtr->spanTbl,
+                                                        entryPtr), -1);
+               Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
+               objPtr = Tcl_NewStringObj((char *) Tcl_GetHashValue(entryPtr),
+                                         -1);
+               Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
+           }
+       }
+       return TCL_OK;
+    } else if (objc == 3) {
+       if (TableGetIndexObj(tablePtr, objv[2], &row, &col) == TCL_ERROR) {
+           return TCL_ERROR;
+       }
+       /* Just return the spanning values of the one cell */
+       if (tablePtr->spanTbl &&
+           (entryPtr = Tcl_FindHashEntry(tablePtr->spanTbl,
+                                         Tcl_GetString(objv[2]))) != NULL) {
+           Tcl_SetStringObj(resultPtr,
+                            (char *)Tcl_GetHashValue(entryPtr), -1);
+       }
+       return TCL_OK;
     } else {
-      Tcl_AppendResult(tablePtr->interp, "no \"anchor\" cell in table", NULL);
-      return TCL_ERROR;
-    }
-  } else if (strncmp(str, "end", len) == 0) {          /* end */
-    r = tablePtr->rows-1+tablePtr->rowOffset;
-    c = tablePtr->cols-1+tablePtr->colOffset;
-  } else if (strncmp(str, "origin", len) == 0) {       /* origin */
-    r = tablePtr->titleRows+tablePtr->rowOffset;
-    c = tablePtr->titleCols+tablePtr->colOffset;
-  } else if (strncmp(str, "topleft", len) == 0) {      /* topleft */
-    r = tablePtr->topRow+tablePtr->rowOffset;
-    c = tablePtr->leftCol+tablePtr->colOffset;
-  } else if (strncmp(str, "bottomright", len) == 0) {  /* bottomright */
-    TableGetLastCell(tablePtr, &r, &c);
-    r += tablePtr->rowOffset;
-    c += tablePtr->colOffset;
-  } else {
-  IndexError:
-    Tcl_AppendResult(tablePtr->interp, "bad table index \"",
-                    str, "\"", (char *)NULL);
-    return TCL_ERROR;
-  }
-
-  /* Note: values are expected to be properly constrained 
-   * as a user index by this point */
-  if (row_p) *row_p = r;
-  if (col_p) *col_p = c;
-  return TCL_OK;
+       for (i = 2; i < objc-1; i += 2) {
+           if (TableGetIndexObj(tablePtr, objv[i], &row, &col) == TCL_ERROR ||
+               (TableParseArrayIndex(&rs, &cs,
+                                     Tcl_GetString(objv[i+1])) != 2) ||
+               Table_SpanSet(tablePtr, row, col, rs, cs) == TCL_ERROR) {
+               return TCL_ERROR;
+           }
+       }
+    }
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_HiddenCmd --
+ *     This procedure is invoked to process the hidden method
+ *     that corresponds to a widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_HiddenCmd(ClientData clientData, register Tcl_Interp *interp,
+               int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    int i, row, col;
+    Tcl_HashEntry *entryPtr;
+    char *span;
+
+    if (objc < 2) {
+       Tcl_WrongNumArgs(interp, 2, objv, "?index? ?index ...?");
+       return TCL_ERROR;
+    }
+    if (tablePtr->spanTbl == NULL) {
+       /* Avoid the whole thing if we have no spans */
+       if (objc > 3) {
+           Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 0);
+       }
+       return TCL_OK;
+    }
+    if (objc == 2) {
+       /* return all "hidden" cells */
+       Tcl_HashSearch search;
+       Tcl_Obj *objPtr = Tcl_NewObj();
+
+       for (entryPtr = Tcl_FirstHashEntry(tablePtr->spanAffTbl, &search);
+            entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
+           if ((span = (char *) Tcl_GetHashValue(entryPtr)) == NULL) {
+               /* this is actually a spanning cell */
+               continue;
+           }
+           Tcl_ListObjAppendElement(NULL, objPtr,
+                       Tcl_NewStringObj(Tcl_GetHashKey(tablePtr->spanAffTbl,
+                                                       entryPtr), -1));
+       }
+       Tcl_SetObjResult(interp, TableCellSortObj(interp, objPtr));
+       return TCL_OK;
+    }
+    if (objc == 3) {
+       if (TableGetIndexObj(tablePtr, objv[2], &row, &col) != TCL_OK) {
+           return TCL_ERROR;
+       }
+       /* Just return the spanning values of the one cell */
+       entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl,
+                                    Tcl_GetString(objv[2]));
+       if (entryPtr != NULL &&
+           (span = (char *)Tcl_GetHashValue(entryPtr)) != NULL) {
+           /* this is a hidden cell */
+           Tcl_SetStringObj(Tcl_GetObjResult(interp), span, -1);
+       }
+       return TCL_OK;
+    }
+    for (i = 2; i < objc; i++) {
+       if (TableGetIndexObj(tablePtr, objv[i], &row, &col) == TCL_ERROR) {
+           return TCL_ERROR;
+       }
+       entryPtr = Tcl_FindHashEntry(tablePtr->spanAffTbl,
+                                    Tcl_GetString(objv[i]));
+       if (entryPtr != NULL &&
+           (char *)Tcl_GetHashValue(entryPtr) != NULL) {
+           /* this is a hidden cell */
+           continue;
+       }
+       /* We only reach here if it doesn't satisfy "hidden" criteria */
+       Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 0);
+       return TCL_OK;
+    }
+    Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 1);
+    return TCL_OK;
 }
 
+/*
+ *--------------------------------------------------------------
+ *
+ * TableSpanSanCheck --
+ *     This procedure is invoked by TableConfigure to make sure
+ *     that spans are kept sane according to the docs.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     void.
+ *
+ * Side effects:
+ *     Spans in title areas can be reconstrained.
+ *
+ *--------------------------------------------------------------
+ */
+void
+TableSpanSanCheck(register Table *tablePtr)
+{
+    int rs, cs, row, col, reset;
+    Tcl_HashEntry *entryPtr;
+    Tcl_HashSearch search;
+
+    if (tablePtr->spanTbl == NULL) {
+       return;
+    }
+
+    for (entryPtr = Tcl_FirstHashEntry(tablePtr->spanTbl, &search);
+        entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
+       reset = 0;
+       TableParseArrayIndex(&row, &col,
+                            Tcl_GetHashKey(tablePtr->spanTbl, entryPtr));
+       TableParseArrayIndex(&rs, &cs,
+                            (char *) Tcl_GetHashValue(entryPtr));
+       if ((row-tablePtr->rowOffset < tablePtr->titleRows) &&
+           (row-tablePtr->rowOffset+rs >= tablePtr->titleRows)) {
+           rs = tablePtr->titleRows-(row-tablePtr->rowOffset)-1;
+           reset = 1;
+       }
+       if ((col-tablePtr->colOffset < tablePtr->titleCols) &&
+           (col-tablePtr->colOffset+cs >= tablePtr->titleCols)) {
+           cs = tablePtr->titleCols-(col-tablePtr->colOffset)-1;
+           reset = 1;
+       }
+       if (reset) {
+           Table_SpanSet(tablePtr, row, col, rs, cs);
+       }
+    }
+}
diff --git a/libgui/src/tkTableCellSort.c b/libgui/src/tkTableCellSort.c
new file mode 100644 (file)
index 0000000..7afc4d7
--- /dev/null
@@ -0,0 +1,400 @@
+/* 
+ * tkTableCell.c --
+ *
+ *     This module implements cell sort functions for table
+ *     widgets.  The MergeSort algorithm and other aux sorting
+ *     functions were taken from tclCmdIL.c lsort command:
+
+ * tclCmdIL.c --
+ *
+ *     This file contains the top-level command routines for most of
+ *     the Tcl built-in commands whose names begin with the letters
+ *     I through L.  It contains only commands in the generic core
+ *     (i.e. those that don't depend much upon UNIX facilities).
+ *
+ * Copyright (c) 1987-1993 The Regents of the University of California.
+ * Copyright (c) 1993-1997 Lucent Technologies.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ * Copyright (c) 1998-1999 by Scriptics Corporation.
+
+ *
+ * Copyright (c) 1998-1999 Jeffrey Hobbs
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#include "tkTable.h"
+
+#ifndef UCHAR
+#define UCHAR(c) ((unsigned char) (c))
+#endif
+
+/*
+ * During execution of the "lsort" command, structures of the following
+ * type are used to arrange the objects being sorted into a collection
+ * of linked lists.
+ */
+
+typedef struct SortElement {
+    Tcl_Obj *objPtr;                   /* Object being sorted. */
+    struct SortElement *nextPtr;        /* Next element in the list, or
+                                        * NULL for end of list. */
+} SortElement;
+
+static int             TableSortCompareProc _ANSI_ARGS_((CONST VOID *first,
+                                                         CONST VOID *second));
+static SortElement *    MergeSort _ANSI_ARGS_((SortElement *headPt));
+static SortElement *    MergeLists _ANSI_ARGS_((SortElement *leftPtr,
+                                               SortElement *rightPtr));
+static int             DictionaryCompare _ANSI_ARGS_((char *left,
+                                                      char *right));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableSortCompareProc --
+ *     This procedure is invoked by qsort to determine the proper
+ *     ordering between two elements.
+ *
+ * Results:
+ *     < 0 means first is "smaller" than "second", > 0 means "first"
+ *     is larger than "second", and 0 means they should be treated
+ *     as equal.
+ *
+ * Side effects:
+ *     None, unless a user-defined comparison command does something
+ *     weird.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+TableSortCompareProc(first, second)
+    CONST VOID *first, *second;                /* Elements to be compared. */
+{
+    char *str1 = *((char **) first);
+    char *str2 = *((char **) second);
+
+    return DictionaryCompare(str1, str2);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableCellSort --
+ *     Sort a list of table cell elements (of form row,col)
+ *
+ * Results:
+ *     Returns the sorted list of elements.  Because Tcl_Merge allocs
+ *     the space for result, it must later be Tcl_Free'd by caller.
+ *
+ * Side effects:
+ *     Behaviour undefined for ill-formed input list of elements.
+ *
+ *----------------------------------------------------------------------
+ */
+char *
+TableCellSort(Table *tablePtr, char *str)
+{
+    int listArgc;
+    char **listArgv;
+    char *result;
+
+    if (Tcl_SplitList(tablePtr->interp, str, &listArgc, &listArgv) != TCL_OK) {
+       return str;
+    }
+    /* Thread safety: qsort is reportedly not thread-safe... */
+    qsort((VOID *) listArgv, (size_t) listArgc, sizeof (char *),
+         TableSortCompareProc);
+    result = Tcl_Merge(listArgc, listArgv);
+    ckfree((char *) listArgv);
+    return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DictionaryCompare - Not the Unicode version
+ *
+ *     This function compares two strings as if they were being used in
+ *     an index or card catalog.  The case of alphabetic characters is
+ *     ignored, except to break ties.  Thus "B" comes before "b" but
+ *     after "a".  Also, integers embedded in the strings compare in
+ *     numerical order.  In other words, "x10y" comes after "x9y", not
+ *      before it as it would when using strcmp().
+ *
+ * Results:
+ *      A negative result means that the first element comes before the
+ *      second, and a positive result means that the second element
+ *      should come first.  A result of zero means the two elements
+ *      are equal and it doesn't matter which comes first.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DictionaryCompare(left, right)
+    char *left, *right;          /* The strings to compare */
+{
+    int diff, zeros;
+    int secondaryDiff = 0;
+
+    while (1) {
+       if (isdigit(UCHAR(*right)) && isdigit(UCHAR(*left))) {
+           /*
+            * There are decimal numbers embedded in the two
+            * strings.  Compare them as numbers, rather than
+            * strings.  If one number has more leading zeros than
+            * the other, the number with more leading zeros sorts
+            * later, but only as a secondary choice.
+            */
+
+           zeros = 0;
+           while ((*right == '0') && (isdigit(UCHAR(right[1])))) {
+               right++;
+               zeros--;
+           }
+           while ((*left == '0') && (isdigit(UCHAR(left[1])))) {
+               left++;
+               zeros++;
+           }
+           if (secondaryDiff == 0) {
+               secondaryDiff = zeros;
+           }
+
+           /*
+            * The code below compares the numbers in the two
+            * strings without ever converting them to integers.  It
+            * does this by first comparing the lengths of the
+            * numbers and then comparing the digit values.
+            */
+
+           diff = 0;
+           while (1) {
+               if (diff == 0) {
+                   diff = UCHAR(*left) - UCHAR(*right);
+               }
+               right++;
+               left++;
+               if (!isdigit(UCHAR(*right))) {
+                   if (isdigit(UCHAR(*left))) {
+                       return 1;
+                   } else {
+                       /*
+                        * The two numbers have the same length. See
+                        * if their values are different.
+                        */
+
+                       if (diff != 0) {
+                           return diff;
+                       }
+                       break;
+                   }
+               } else if (!isdigit(UCHAR(*left))) {
+                   return -1;
+               }
+           }
+           continue;
+       }
+        diff = UCHAR(*left) - UCHAR(*right);
+        if (diff) {
+            if (isupper(UCHAR(*left)) && islower(UCHAR(*right))) {
+                diff = UCHAR(tolower(*left)) - UCHAR(*right);
+                if (diff) {
+                   return diff;
+                } else if (secondaryDiff == 0) {
+                   secondaryDiff = -1;
+                }
+            } else if (isupper(UCHAR(*right)) && islower(UCHAR(*left))) {
+                diff = UCHAR(*left) - UCHAR(tolower(UCHAR(*right)));
+                if (diff) {
+                   return diff;
+                } else if (secondaryDiff == 0) {
+                   secondaryDiff = 1;
+                }
+            } else {
+                return diff;
+            }
+        }
+        if (*left == 0) {
+           break;
+       }
+        left++;
+        right++;
+    }
+    if (diff == 0) {
+       diff = secondaryDiff;
+    }
+    return diff;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MergeLists -
+ *
+ *     This procedure combines two sorted lists of SortElement structures
+ *     into a single sorted list.
+ *
+ * Results:
+ *      The unified list of SortElement structures.
+ *
+ * Side effects:
+ *     None, unless a user-defined comparison command does something
+ *     weird.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static SortElement *
+MergeLists(leftPtr, rightPtr)
+    SortElement *leftPtr;               /* First list to be merged; may be
+                                        * NULL. */
+    SortElement *rightPtr;              /* Second list to be merged; may be
+                                        * NULL. */
+{
+    SortElement *headPtr;
+    SortElement *tailPtr;
+
+    if (leftPtr == NULL) {
+        return rightPtr;
+    }
+    if (rightPtr == NULL) {
+        return leftPtr;
+    }
+    if (DictionaryCompare(Tcl_GetString(leftPtr->objPtr),
+                         Tcl_GetString(rightPtr->objPtr)) > 0) {
+       tailPtr = rightPtr;
+       rightPtr = rightPtr->nextPtr;
+    } else {
+       tailPtr = leftPtr;
+       leftPtr = leftPtr->nextPtr;
+    }
+    headPtr = tailPtr;
+    while ((leftPtr != NULL) && (rightPtr != NULL)) {
+       if (DictionaryCompare(Tcl_GetString(leftPtr->objPtr),
+                             Tcl_GetString(rightPtr->objPtr)) > 0) {
+           tailPtr->nextPtr = rightPtr;
+           tailPtr = rightPtr;
+           rightPtr = rightPtr->nextPtr;
+       } else {
+           tailPtr->nextPtr = leftPtr;
+           tailPtr = leftPtr;
+           leftPtr = leftPtr->nextPtr;
+       }
+    }
+    if (leftPtr != NULL) {
+       tailPtr->nextPtr = leftPtr;
+    } else {
+       tailPtr->nextPtr = rightPtr;
+    }
+    return headPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MergeSort -
+ *
+ *     This procedure sorts a linked list of SortElement structures
+ *     use the merge-sort algorithm.
+ *
+ * Results:
+ *      A pointer to the head of the list after sorting is returned.
+ *
+ * Side effects:
+ *     None, unless a user-defined comparison command does something
+ *     weird.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static SortElement *
+MergeSort(headPtr)
+    SortElement *headPtr;               /* First element on the list */
+{
+    /*
+     * The subList array below holds pointers to temporary lists built
+     * during the merge sort.  Element i of the array holds a list of
+     * length 2**i.
+     */
+
+#   define NUM_LISTS 30
+    SortElement *subList[NUM_LISTS];
+    SortElement *elementPtr;
+    int i;
+
+    for(i = 0; i < NUM_LISTS; i++){
+        subList[i] = NULL;
+    }
+    while (headPtr != NULL) {
+       elementPtr = headPtr;
+       headPtr = headPtr->nextPtr;
+       elementPtr->nextPtr = 0;
+       for (i = 0; (i < NUM_LISTS) && (subList[i] != NULL); i++){
+           elementPtr = MergeLists(subList[i], elementPtr);
+           subList[i] = NULL;
+       }
+       if (i >= NUM_LISTS) {
+           i = NUM_LISTS-1;
+       }
+       subList[i] = elementPtr;
+    }
+    elementPtr = NULL;
+    for (i = 0; i < NUM_LISTS; i++){
+        elementPtr = MergeLists(subList[i], elementPtr);
+    }
+    return elementPtr;
+}
+
+#ifndef NO_SORT_CELLS
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableCellSortObj --
+ *     Sorts a list of table cell elements (of form row,col) in place
+ *
+ * Results:
+ *     Sorts list of elements in place.
+ *
+ * Side effects:
+ *     Behaviour undefined for ill-formed input list of elements.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_Obj *
+TableCellSortObj(Tcl_Interp *interp, Tcl_Obj *listObjPtr)
+{
+    int length, i;
+    Tcl_Obj *sortedObjPtr, **listObjPtrs;
+    SortElement *elementArray;
+    SortElement *elementPtr;        
+
+    if (Tcl_ListObjGetElements(interp, listObjPtr,
+                              &length, &listObjPtrs) != TCL_OK) {
+       return NULL;
+    }
+    if (length <= 0) {
+       return listObjPtr;
+    }
+
+    elementArray = (SortElement *) ckalloc(length * sizeof(SortElement));
+    for (i=0; i < length; i++){
+       elementArray[i].objPtr = listObjPtrs[i];
+       elementArray[i].nextPtr = &elementArray[i+1];
+    }
+    elementArray[length-1].nextPtr = NULL;
+    elementPtr = MergeSort(elementArray);
+    sortedObjPtr = Tcl_NewObj();
+    for (; elementPtr != NULL; elementPtr = elementPtr->nextPtr){
+       Tcl_ListObjAppendElement(NULL, sortedObjPtr, elementPtr->objPtr);
+    }
+    ckfree((char*) elementArray);
+
+    return sortedObjPtr;
+}
+#endif
diff --git a/libgui/src/tkTableCmds.c b/libgui/src/tkTableCmds.c
new file mode 100644 (file)
index 0000000..4fc7d3b
--- /dev/null
@@ -0,0 +1,1293 @@
+/* 
+ * tkTableCmds.c --
+ *
+ *     This module implements general commands of a table widget,
+ *     based on the major/minor command structure.
+ *
+ * Copyright (c) 1998-2000 Jeffrey Hobbs
+ *
+ * See the file "license.txt" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#include "tkTable.h"
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_ActivateCmd --
+ *     This procedure is invoked to process the activate method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_ActivateCmd(ClientData clientData, register Tcl_Interp *interp,
+             int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    int result = TCL_OK;
+    int row, col;
+
+    if (objc != 3) {
+       Tcl_WrongNumArgs(interp, 2, objv, "index");
+       return TCL_ERROR;
+    } else if (TableGetIndexObj(tablePtr, objv[2], &row, &col) != TCL_OK) {
+       return TCL_ERROR;
+    } else {
+       int x, y, w, dummy;
+       char buf1[INDEX_BUFSIZE], buf2[INDEX_BUFSIZE];
+
+       /* convert to valid active index in real coords */
+       row -= tablePtr->rowOffset;
+       col -= tablePtr->colOffset;
+       /* we do this regardless, to avoid cell commit problems */
+       if ((tablePtr->flags & HAS_ACTIVE) &&
+           (tablePtr->flags & TEXT_CHANGED)) {
+           tablePtr->flags &= ~TEXT_CHANGED;
+           TableSetCellValue(tablePtr,
+                             tablePtr->activeRow+tablePtr->rowOffset,
+                             tablePtr->activeCol+tablePtr->colOffset,
+                             tablePtr->activeBuf);
+       }
+       if (row != tablePtr->activeRow || col != tablePtr->activeCol) {
+           if (tablePtr->flags & HAS_ACTIVE) {
+               TableMakeArrayIndex(tablePtr->activeRow+tablePtr->rowOffset,
+                                   tablePtr->activeCol+tablePtr->colOffset,
+                                   buf1);
+           } else {
+               buf1[0] = '\0';
+           }
+           tablePtr->flags |= HAS_ACTIVE;
+           tablePtr->flags &= ~ACTIVE_DISABLED;
+           tablePtr->activeRow = row;
+           tablePtr->activeCol = col;
+           if (tablePtr->activeTagPtr != NULL) {
+               ckfree((char *) (tablePtr->activeTagPtr));
+               tablePtr->activeTagPtr = NULL;
+           }
+           TableAdjustActive(tablePtr);
+           TableConfigCursor(tablePtr);
+           if (!(tablePtr->flags & BROWSE_CMD) &&
+               tablePtr->browseCmd != NULL) {
+               Tcl_DString script;
+               tablePtr->flags |= BROWSE_CMD;
+               row = tablePtr->activeRow+tablePtr->rowOffset;
+               col = tablePtr->activeCol+tablePtr->colOffset;
+               TableMakeArrayIndex(row, col, buf2);
+               Tcl_DStringInit(&script);
+               ExpandPercents(tablePtr, tablePtr->browseCmd, row, col,
+                              buf1, buf2, tablePtr->icursor, &script, 0);
+               result = Tcl_GlobalEval(interp, Tcl_DStringValue(&script));
+               if (result == TCL_OK || result == TCL_RETURN) {
+                   Tcl_ResetResult(interp);
+               }
+               Tcl_DStringFree(&script);
+               tablePtr->flags &= ~BROWSE_CMD;
+           }
+       } else {
+           char *p = Tcl_GetString(objv[2]);
+
+           if ((tablePtr->activeTagPtr != NULL) && *p == '@' &&
+               !(tablePtr->flags & ACTIVE_DISABLED) &&
+               TableCellVCoords(tablePtr, row, col, &x, &y, &w, &dummy, 0)) {
+               /* we are clicking into the same cell
+                * If it was activated with @x,y indexing,
+                * find the closest char */
+               Tk_TextLayout textLayout;
+               TableTag *tagPtr = tablePtr->activeTagPtr;
+
+               /* no error checking because GetIndex did it for us */
+               p++;
+               x = strtol(p, &p, 0) - x - tablePtr->activeX;
+               y = strtol(++p, &p, 0) - y - tablePtr->activeY;
+
+               textLayout = Tk_ComputeTextLayout(tagPtr->tkfont,
+                                       tablePtr->activeBuf, -1,
+                                       (tagPtr->wrap) ? w : 0,
+                                       tagPtr->justify, 0, &dummy, &dummy);
+
+               tablePtr->icursor = Tk_PointToChar(textLayout, x, y);
+               Tk_FreeTextLayout(textLayout);
+               TableRefresh(tablePtr, row, col, CELL|INV_FORCE);
+           }
+       }
+       tablePtr->flags |= HAS_ACTIVE;
+    }
+    return result;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_AdjustCmd --
+ *     This procedure is invoked to process the width/height method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_AdjustCmd(ClientData clientData, register Tcl_Interp *interp,
+               int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    Tcl_HashEntry *entryPtr;
+    Tcl_HashSearch search;
+    Tcl_HashTable *hashTablePtr;
+    int i, widthType, dummy, value, posn, offset;
+    char buf1[INDEX_BUFSIZE];
+
+    widthType = (*(Tcl_GetString(objv[1])) == 'w');
+    /* changes the width/height of certain selected columns */
+    if (objc != 3 && (objc & 1)) {
+       Tcl_WrongNumArgs(interp, 2, objv, widthType ?
+                        "?col? ?width col width ...?" :
+                        "?row? ?height row height ...?");
+       return TCL_ERROR;
+    }
+    if (widthType) {
+       hashTablePtr = tablePtr->colWidths;
+       offset = tablePtr->colOffset;
+    } else { 
+       hashTablePtr = tablePtr->rowHeights;
+       offset = tablePtr->rowOffset;
+    }
+
+    if (objc == 2) {
+       /* print out all the preset column widths or row heights */
+       entryPtr = Tcl_FirstHashEntry(hashTablePtr, &search);
+       while (entryPtr != NULL) {
+           posn = ((int) Tcl_GetHashKey(hashTablePtr, entryPtr)) + offset;
+           value = (int) Tcl_GetHashValue(entryPtr);
+           sprintf(buf1, "%d %d", posn, value);
+           /* OBJECTIFY */
+           Tcl_AppendElement(interp, buf1);
+           entryPtr = Tcl_NextHashEntry(&search);
+       }
+    } else if (objc == 3) {
+       /* get the width/height of a particular row/col */
+       if (Tcl_GetIntFromObj(interp, objv[2], &posn) != TCL_OK) {
+           return TCL_ERROR;
+       }
+       /* no range check is done, why bother? */
+       posn -= offset;
+       entryPtr = Tcl_FindHashEntry(hashTablePtr, (char *) posn);
+       if (entryPtr != NULL) {
+           Tcl_SetIntObj(Tcl_GetObjResult(interp),
+                         (int) Tcl_GetHashValue(entryPtr));
+       } else {
+           Tcl_SetIntObj(Tcl_GetObjResult(interp), widthType ?
+                         tablePtr->defColWidth : tablePtr->defRowHeight);
+       }
+    } else {
+       for (i=2; i<objc; i++) {
+           /* set new width|height here */
+           value = -999999;
+           if (Tcl_GetIntFromObj(interp, objv[i++], &posn) != TCL_OK ||
+               (strcmp(Tcl_GetString(objv[i]), "default") &&
+                Tcl_GetIntFromObj(interp, objv[i], &value) != TCL_OK)) {
+               return TCL_ERROR;
+           }
+           posn -= offset;
+           if (value == -999999) {
+               /* reset that field */
+               entryPtr = Tcl_FindHashEntry(hashTablePtr, (char *) posn);
+               if (entryPtr != NULL) {
+                   Tcl_DeleteHashEntry(entryPtr);
+               }
+           } else {
+               entryPtr = Tcl_CreateHashEntry(hashTablePtr,
+                                              (char *) posn, &dummy);
+               Tcl_SetHashValue(entryPtr, (ClientData) value);
+           }
+       }
+       TableAdjustParams(tablePtr);
+       /* rerequest geometry */
+       TableGeometryRequest(tablePtr);
+       /*
+        * Invalidate the whole window as TableAdjustParams
+        * will only check to see if the top left cell has moved
+        * FIX: should just move from lowest order visible cell
+        * to edge of window
+        */
+       TableInvalidateAll(tablePtr, 0);
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_BboxCmd --
+ *     This procedure is invoked to process the bbox method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_BboxCmd(ClientData clientData, register Tcl_Interp *interp,
+             int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    int x, y, w, h, row, col, key;
+    Tcl_Obj *resultPtr;
+
+    /* Returns bounding box of cell(s) */
+    if (objc < 3 || objc > 4) {
+       Tcl_WrongNumArgs(interp, 2, objv, "first ?last?");
+       return TCL_ERROR;
+    } else if (TableGetIndexObj(tablePtr, objv[2], &row, &col) == TCL_ERROR ||
+              (objc == 4 &&
+               TableGetIndexObj(tablePtr, objv[3], &x, &y) == TCL_ERROR)) {
+       return TCL_ERROR;
+    }
+
+    resultPtr = Tcl_GetObjResult(interp);
+    if (objc == 3) {
+       row -= tablePtr->rowOffset; col -= tablePtr->colOffset;
+       if (TableCellVCoords(tablePtr, row, col, &x, &y, &w, &h, 0)) {
+           Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(x));
+           Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(y));
+           Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(w));
+           Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(h));
+       }
+       return TCL_OK;
+    } else {
+       int r1, c1, r2, c2, minX = 99999, minY = 99999, maxX = 0, maxY = 0;
+
+       row -= tablePtr->rowOffset; col -= tablePtr->colOffset;
+       x -= tablePtr->rowOffset; y -= tablePtr->colOffset;
+       r1 = MIN(row,x); r2 = MAX(row,x);
+       c1 = MIN(col,y); c2 = MAX(col,y);
+       key = 0;
+       for (row = r1; row <= r2; row++) {
+           for (col = c1; col <= c2; col++) {
+               if (TableCellVCoords(tablePtr, row, col, &x, &y, &w, &h, 0)) {
+                   /* Get max bounding box */
+                   if (x < minX) minX = x;
+                   if (y < minY) minY = y;
+                   if (x+w > maxX) maxX = x+w;
+                   if (y+h > maxY) maxY = y+h;
+                   key++;
+               }
+           }
+       }
+       if (key) {
+           Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(minX));
+           Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewIntObj(minY));
+           Tcl_ListObjAppendElement(NULL, resultPtr,
+                                    Tcl_NewIntObj(maxX-minX));
+           Tcl_ListObjAppendElement(NULL, resultPtr,
+                                    Tcl_NewIntObj(maxY-minY));
+       }
+    }
+    return TCL_OK;
+}
+\f
+static char *bdCmdNames[] = {
+    "mark", "dragto", (char *)NULL
+};
+enum bdCmd {
+    BD_MARK, BD_DRAGTO
+};
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_BorderCmd --
+ *     This procedure is invoked to process the bbox method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_BorderCmd(ClientData clientData, register Tcl_Interp *interp,
+               int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    Tcl_HashEntry *entryPtr;
+    int x, y, w, h, row, col, key, dummy, value, cmdIndex;
+    char *rc = NULL;
+    Tcl_Obj *objPtr, *resultPtr;
+
+    if (objc < 5 || objc > 6) {
+       Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x y ?row|col?");
+       return TCL_ERROR;
+    }
+    if (Tcl_GetIndexFromObj(interp, objv[2], bdCmdNames,
+                           "option", 0, &cmdIndex) != TCL_OK ||
+       Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK ||
+       Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) {
+       return TCL_ERROR;
+    }
+    if (objc == 6) {
+       rc = Tcl_GetStringFromObj(objv[5], &w);
+       if ((w < 1) || (strncmp(rc, "row", w) && strncmp(rc, "col", w))) {
+           Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x y ?row|col?");
+           return TCL_ERROR;
+       }
+    }
+
+    resultPtr = Tcl_GetObjResult(interp);
+    switch ((enum bdCmd) cmdIndex) {
+    case BD_MARK:
+       /* Use x && y to determine if we are over a border */
+       value = TableAtBorder(tablePtr, x, y, &row, &col);
+       /* Cache the row && col for use in DRAGTO */
+       tablePtr->scanMarkRow = row;
+       tablePtr->scanMarkCol = col;
+       if (!value) {
+           return TCL_OK;
+       }
+       TableCellCoords(tablePtr, row, col, &x, &y, &dummy, &dummy);
+       tablePtr->scanMarkX = x;
+       tablePtr->scanMarkY = y;
+       if (objc == 5 || *rc == 'r') {
+           if (row < 0) {
+               objPtr = Tcl_NewStringObj("", 0);
+           } else {
+               objPtr = Tcl_NewIntObj(row+tablePtr->rowOffset);
+           }
+           Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
+       }
+       if (objc == 5 || *rc == 'c') {
+           if (col < 0) {
+               objPtr = Tcl_NewStringObj("", 0);
+           } else {
+               objPtr = Tcl_NewIntObj(col+tablePtr->colOffset);
+           }
+           Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
+       }
+       return TCL_OK;  /* BORDER MARK */
+
+    case BD_DRAGTO:
+       /* check to see if we want to resize any borders */
+       if (tablePtr->resize == SEL_NONE) { return TCL_OK; }
+       row = tablePtr->scanMarkRow;
+       col = tablePtr->scanMarkCol;
+       TableCellCoords(tablePtr, row, col, &w, &h, &dummy, &dummy);
+       key = 0;
+       if (row >= 0 && (tablePtr->resize & SEL_ROW)) {
+           /* row border was active, move it */
+           value = y-h;
+           if (value < -1) value = -1;
+           if (value != tablePtr->scanMarkY) {
+               entryPtr = Tcl_CreateHashEntry(tablePtr->rowHeights,
+                                              (char *) row, &dummy);
+               /* -value means rowHeight will be interp'd as pixels, not
+                   lines */
+               Tcl_SetHashValue(entryPtr, (ClientData) MIN(0,-value));
+               tablePtr->scanMarkY = value;
+               key++;
+           }
+       }
+       if (col >= 0 && (tablePtr->resize & SEL_COL)) {
+           /* col border was active, move it */
+           value = x-w;
+           if (value < -1) value = -1;
+           if (value != tablePtr->scanMarkX) {
+               entryPtr = Tcl_CreateHashEntry(tablePtr->colWidths,
+                                              (char *) col, &dummy);
+               /* -value means colWidth will be interp'd as pixels, not
+                   chars */
+               Tcl_SetHashValue(entryPtr, (ClientData) MIN(0,-value));
+               tablePtr->scanMarkX = value;
+               key++;
+           }
+       }
+       /* Only if something changed do we want to update */
+       if (key) {
+           TableAdjustParams(tablePtr);
+           /* Only rerequest geometry if the basis is the #rows &| #cols */
+           if (tablePtr->maxReqCols || tablePtr->maxReqRows)
+               TableGeometryRequest(tablePtr);
+           TableInvalidateAll(tablePtr, 0);
+       }
+       return TCL_OK;  /* BORDER DRAGTO */
+    }
+    return TCL_OK;
+}
+\f
+/* clear subcommands */
+static char *clearNames[] = {
+    "all", "cache", "sizes", "tags", (char *)NULL
+};
+enum clearCommand {
+    CLEAR_ALL, CLEAR_CACHE, CLEAR_SIZES, CLEAR_TAGS
+};
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_ClearCmd --
+ *     This procedure is invoked to process the clear method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     Cached info can be lost.  Returns valid Tcl result.
+ *
+ * Side effects:
+ *     Can cause redraw.
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_ClearCmd(ClientData clientData, register Tcl_Interp *interp,
+               int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    int cmdIndex, redraw = 0;
+
+    if (objc < 3 || objc > 5) {
+       Tcl_WrongNumArgs(interp, 2, objv, "option ?first? ?last?");
+       return TCL_ERROR;
+    }
+
+    if (Tcl_GetIndexFromObj(interp, objv[2], clearNames,
+                           "clear option", 0, &cmdIndex) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    if (objc == 3) {
+       if (cmdIndex == CLEAR_TAGS || cmdIndex == CLEAR_ALL) {
+           Tcl_DeleteHashTable(tablePtr->rowStyles);
+           Tcl_DeleteHashTable(tablePtr->colStyles);
+           Tcl_DeleteHashTable(tablePtr->cellStyles);
+           Tcl_DeleteHashTable(tablePtr->flashCells);
+           Tcl_DeleteHashTable(tablePtr->selCells);
+
+           /* style hash tables */
+           Tcl_InitHashTable(tablePtr->rowStyles, TCL_ONE_WORD_KEYS);
+           Tcl_InitHashTable(tablePtr->colStyles, TCL_ONE_WORD_KEYS);
+           Tcl_InitHashTable(tablePtr->cellStyles, TCL_STRING_KEYS);
+
+           /* special style hash tables */
+           Tcl_InitHashTable(tablePtr->flashCells, TCL_STRING_KEYS);
+           Tcl_InitHashTable(tablePtr->selCells, TCL_STRING_KEYS);
+       }
+
+       if (cmdIndex == CLEAR_SIZES || cmdIndex == CLEAR_ALL) {
+           Tcl_DeleteHashTable(tablePtr->colWidths);
+           Tcl_DeleteHashTable(tablePtr->rowHeights);
+
+           /* style hash tables */
+           Tcl_InitHashTable(tablePtr->colWidths, TCL_ONE_WORD_KEYS);
+           Tcl_InitHashTable(tablePtr->rowHeights, TCL_ONE_WORD_KEYS);
+       }
+
+       if (cmdIndex == CLEAR_CACHE || cmdIndex == CLEAR_ALL) {
+           Tcl_DeleteHashTable(tablePtr->cache);
+           Tcl_InitHashTable(tablePtr->cache, TCL_STRING_KEYS);
+           /* If we were caching and we have no other data source,
+            * invalidate all the cells */
+           if (tablePtr->dataSource == DATA_CACHE) {
+               TableGetActiveBuf(tablePtr);
+           }
+       }
+       redraw = 1;
+    } else {
+       int row, col, r1, r2, c1, c2;
+       Tcl_HashEntry *entryPtr;
+       char buf[INDEX_BUFSIZE];
+
+       if (TableGetIndexObj(tablePtr, objv[3], &row, &col) != TCL_OK ||
+           ((objc == 5) &&
+            TableGetIndexObj(tablePtr, objv[4], &r2, &c2) != TCL_OK)) {
+           return TCL_ERROR;
+       }
+       if (objc == 4) {
+           r1 = r2 = row;
+           c1 = c2 = col;
+       } else {
+           r1 = MIN(row,r2); r2 = MAX(row,r2);
+           c1 = MIN(col,c2); c2 = MAX(col,c2);
+       }
+       for (row = r1; row <= r2; row++) {
+           /* Note that *Styles entries are user based (no offset)
+            * while size entries are 0-based (real) */
+           if ((cmdIndex == CLEAR_TAGS || cmdIndex == CLEAR_ALL) &&
+               (entryPtr = Tcl_FindHashEntry(tablePtr->rowStyles,
+                                             (char *) row))) {
+               Tcl_DeleteHashEntry(entryPtr);
+               redraw = 1;
+           }
+
+           if ((cmdIndex == CLEAR_SIZES || cmdIndex == CLEAR_ALL) &&
+               (entryPtr = Tcl_FindHashEntry(tablePtr->rowHeights,
+                                             (char *) row-tablePtr->rowOffset))) {
+               Tcl_DeleteHashEntry(entryPtr);
+               redraw = 1;
+           }
+
+           for (col = c1; col <= c2; col++) {
+               TableMakeArrayIndex(row, col, buf);
+
+               if (cmdIndex == CLEAR_TAGS || cmdIndex == CLEAR_ALL) {
+                   if ((row == r1) &&
+                       (entryPtr = Tcl_FindHashEntry(tablePtr->colStyles,
+                                                     (char *) col))) {
+                       Tcl_DeleteHashEntry(entryPtr);
+                       redraw = 1;
+                   }
+                   if ((entryPtr = Tcl_FindHashEntry(tablePtr->cellStyles,
+                                                     buf))) {
+                       Tcl_DeleteHashEntry(entryPtr);
+                       redraw = 1;
+                   }
+                   if ((entryPtr = Tcl_FindHashEntry(tablePtr->flashCells,
+                                                     buf))) {
+                       Tcl_DeleteHashEntry(entryPtr);
+                       redraw = 1;
+                   }
+                   if ((entryPtr = Tcl_FindHashEntry(tablePtr->selCells,
+                                                     buf))) {
+                       Tcl_DeleteHashEntry(entryPtr);
+                       redraw = 1;
+                   }
+               }
+
+               if ((cmdIndex == CLEAR_SIZES || cmdIndex == CLEAR_ALL) &&
+                   row == r1 &&
+                   (entryPtr = Tcl_FindHashEntry(tablePtr->colWidths, (char *)
+                                                 col-tablePtr->colOffset))) {
+                   Tcl_DeleteHashEntry(entryPtr);
+                   redraw = 1;
+               }
+
+               if ((cmdIndex == CLEAR_CACHE || cmdIndex == CLEAR_ALL) &&
+                   (entryPtr = Tcl_FindHashEntry(tablePtr->cache, buf))) {
+                   Tcl_DeleteHashEntry(entryPtr);
+                   /* if the cache is our data source,
+                    * we need to invalidate the cells changed */
+                   if ((tablePtr->dataSource == DATA_CACHE) &&
+                       (row-tablePtr->rowOffset == tablePtr->activeRow &&
+                        col-tablePtr->colOffset == tablePtr->activeCol))
+                       TableGetActiveBuf(tablePtr);
+                   redraw = 1;
+               }
+           }
+       }
+    }
+    /* This could be more sensitive about what it updates,
+     * but that can actually be a lot more costly in some cases */
+    if (redraw) {
+       if (cmdIndex == CLEAR_SIZES || cmdIndex == CLEAR_ALL) {
+           TableAdjustParams(tablePtr);
+           /* rerequest geometry */
+           TableGeometryRequest(tablePtr);
+       }
+       TableInvalidateAll(tablePtr, 0);
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_CurselectionCmd --
+ *     This procedure is invoked to process the bbox method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_CurselectionCmd(ClientData clientData, register Tcl_Interp *interp,
+                     int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    Tcl_HashEntry *entryPtr;
+    Tcl_HashSearch search;
+    char *value = NULL;
+    int row, col;
+
+    if (objc > 3) {
+       Tcl_WrongNumArgs(interp, 2, objv, "?value?");
+       return TCL_ERROR;
+    }
+    if (objc == 3) {
+       /* make sure there is a data source to accept a set value */
+       if ((tablePtr->state == STATE_DISABLED) ||
+           (tablePtr->dataSource == DATA_NONE)) {
+           return TCL_OK;
+       }
+       value = Tcl_GetString(objv[2]);
+       for (entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
+            entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
+           TableParseArrayIndex(&row, &col,
+                                Tcl_GetHashKey(tablePtr->selCells, entryPtr));
+           TableSetCellValue(tablePtr, row, col, value);
+           row -= tablePtr->rowOffset;
+           col -= tablePtr->colOffset;
+           if (row == tablePtr->activeRow && col == tablePtr->activeCol) {
+               TableGetActiveBuf(tablePtr);
+           }
+           TableRefresh(tablePtr, row, col, CELL);
+       }
+    } else {
+       Tcl_Obj *objPtr = Tcl_NewObj();
+
+       for (entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
+            entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
+           value = Tcl_GetHashKey(tablePtr->selCells, entryPtr);
+           Tcl_ListObjAppendElement(NULL, objPtr,
+                                    Tcl_NewStringObj(value, -1));
+       }
+       Tcl_SetObjResult(interp, TableCellSortObj(interp, objPtr));
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_CurvalueCmd --
+ *     This procedure is invoked to process the curvalue method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_CurvalueCmd(ClientData clientData, register Tcl_Interp *interp,
+                 int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+
+    if (objc > 3) {
+       Tcl_WrongNumArgs(interp, 2, objv, "?<value>?");
+       return TCL_ERROR;
+    } else if (!(tablePtr->flags & HAS_ACTIVE)) {
+       return TCL_OK;
+    }
+
+    if (objc == 3) {
+       char *value;
+       int len;
+
+       value = Tcl_GetStringFromObj(objv[2], &len);
+       if (STREQ(value, tablePtr->activeBuf)) {
+           Tcl_SetObjResult(interp, objv[2]);
+           return TCL_OK;
+       }
+       /* validate potential new active buffer contents
+        * only accept if validation returns acceptance. */
+       if (tablePtr->validate &&
+           TableValidateChange(tablePtr,
+                               tablePtr->activeRow+tablePtr->rowOffset,
+                               tablePtr->activeCol+tablePtr->colOffset,
+                               tablePtr->activeBuf,
+                               value, tablePtr->icursor) != TCL_OK) {
+           return TCL_OK;
+       }
+       tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf, len+1);
+       strcpy(tablePtr->activeBuf, value);
+       /* mark the text as changed */
+       tablePtr->flags |= TEXT_CHANGED;
+       TableSetActiveIndex(tablePtr);
+       /* check for possible adjustment of icursor */
+       TableGetIcursor(tablePtr, "insert", (int *)0);
+       TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol, CELL);
+    }
+
+    Tcl_SetStringObj(Tcl_GetObjResult(interp), tablePtr->activeBuf, -1);
+    return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_GetCmd --
+ *     This procedure is invoked to process the bbox method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_GetCmd(ClientData clientData, register Tcl_Interp *interp,
+            int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    int result = TCL_OK;
+    int r1, c1, r2, c2, row, col;
+
+    if (objc < 3 || objc > 4) {
+       Tcl_WrongNumArgs(interp, 2, objv, "first ?last?");
+       result = TCL_ERROR;
+    } else if (TableGetIndexObj(tablePtr, objv[2], &row, &col) == TCL_ERROR) {
+       result = TCL_ERROR;
+    } else if (objc == 3) {
+       Tcl_SetObjResult(interp,
+               Tcl_NewStringObj(TableGetCellValue(tablePtr, row, col), -1));
+    } else if (TableGetIndexObj(tablePtr, objv[3], &r2, &c2) == TCL_ERROR) {
+       result = TCL_ERROR;
+    } else {
+       Tcl_Obj *objPtr = Tcl_NewObj();
+
+       r1 = MIN(row,r2); r2 = MAX(row,r2);
+       c1 = MIN(col,c2); c2 = MAX(col,c2);
+       for ( row = r1; row <= r2; row++ ) {
+           for ( col = c1; col <= c2; col++ ) {
+               Tcl_ListObjAppendElement(NULL, objPtr,
+                       Tcl_NewStringObj(TableGetCellValue(tablePtr,
+                               row, col), -1));
+           }
+       }
+       Tcl_SetObjResult(interp, objPtr);
+    }
+    return result;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_ScanCmd --
+ *     This procedure is invoked to process the scan method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_ScanCmd(ClientData clientData, register Tcl_Interp *interp,
+             int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    int x, y, row, col, cmdIndex;
+
+    if (objc != 5) {
+       Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x y");
+       return TCL_ERROR;
+    } else if (Tcl_GetIndexFromObj(interp, objv[2], bdCmdNames,
+           "option", 0, &cmdIndex) != TCL_OK ||
+           Tcl_GetIntFromObj(interp, objv[3], &x) == TCL_ERROR ||
+           Tcl_GetIntFromObj(interp, objv[4], &y) == TCL_ERROR) {
+       return TCL_ERROR;
+    }
+    switch ((enum bdCmd) cmdIndex) {
+       case BD_MARK:
+           TableWhatCell(tablePtr, x, y, &row, &col);
+           tablePtr->scanMarkRow = row-tablePtr->topRow;
+           tablePtr->scanMarkCol = col-tablePtr->leftCol;
+           tablePtr->scanMarkX = x;
+           tablePtr->scanMarkY = y;
+           break;
+
+       case BD_DRAGTO: {
+           int oldTop = tablePtr->topRow, oldLeft = tablePtr->leftCol;
+           y += (5*(y-tablePtr->scanMarkY));
+           x += (5*(x-tablePtr->scanMarkX));
+
+           TableWhatCell(tablePtr, x, y, &row, &col);
+
+           /* maintain appropriate real index */
+           tablePtr->topRow  = BETWEEN(row-tablePtr->scanMarkRow,
+                   tablePtr->titleRows, tablePtr->rows-1);
+           tablePtr->leftCol = BETWEEN(col-tablePtr->scanMarkCol,
+                   tablePtr->titleCols, tablePtr->cols-1);
+
+           /* Adjust the table if new top left */
+           if (oldTop != tablePtr->topRow || oldLeft != tablePtr->leftCol) {
+               TableAdjustParams(tablePtr);
+           }
+           break;
+       }
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_SelAnchorCmd --
+ *     This procedure is invoked to process the selection anchor method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_SelAnchorCmd(ClientData clientData, register Tcl_Interp *interp,
+                  int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    int row, col;
+
+    if (objc != 4) {
+       Tcl_WrongNumArgs(interp, 3, objv, "index");
+       return TCL_ERROR;
+    } else if (TableGetIndexObj(tablePtr, objv[3], &row, &col) != TCL_OK) {
+       return TCL_ERROR;
+    }
+    tablePtr->flags |= HAS_ANCHOR;
+    /* maintain appropriate real index */
+    if (tablePtr->selectTitles) {
+       tablePtr->anchorRow = BETWEEN(row-tablePtr->rowOffset,
+               0, tablePtr->rows-1);
+       tablePtr->anchorCol = BETWEEN(col-tablePtr->colOffset,
+               0, tablePtr->cols-1);
+    } else {
+       tablePtr->anchorRow = BETWEEN(row-tablePtr->rowOffset,
+               tablePtr->titleRows, tablePtr->rows-1);
+       tablePtr->anchorCol = BETWEEN(col-tablePtr->colOffset,
+               tablePtr->titleCols, tablePtr->cols-1);
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_SelClearCmd --
+ *     This procedure is invoked to process the selection clear method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_SelClearCmd(ClientData clientData, register Tcl_Interp *interp,
+                 int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    int result = TCL_OK;
+    char buf1[INDEX_BUFSIZE];
+    int row, col, key, clo=0,chi=0,r1,c1,r2,c2;
+    Tcl_HashEntry *entryPtr;
+
+    if (objc < 4 || objc > 5) {
+       Tcl_WrongNumArgs(interp, 3, objv, "all|<first> ?<last>?");
+       return TCL_ERROR;
+    }
+    if (STREQ(Tcl_GetString(objv[3]), "all")) {
+       Tcl_HashSearch search;
+       for(entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
+           entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
+           TableParseArrayIndex(&row, &col,
+                                Tcl_GetHashKey(tablePtr->selCells,entryPtr));
+           Tcl_DeleteHashEntry(entryPtr);
+           TableRefresh(tablePtr, row-tablePtr->rowOffset,
+                        col-tablePtr->colOffset, CELL);
+       }
+       return TCL_OK;
+    }
+    if (TableGetIndexObj(tablePtr, objv[3], &row, &col) == TCL_ERROR ||
+       (objc==5 &&
+        TableGetIndexObj(tablePtr, objv[4], &r2, &c2) == TCL_ERROR)) {
+       return TCL_ERROR;
+    }
+    key = 0;
+    if (objc == 4) {
+       r1 = r2 = row;
+       c1 = c2 = col;
+    } else {
+       r1 = MIN(row,r2); r2 = MAX(row,r2);
+       c1 = MIN(col,c2); c2 = MAX(col,c2);
+    }
+    switch (tablePtr->selectType) {
+    case SEL_BOTH:
+       clo = c1; chi = c2;
+       c1 = tablePtr->colOffset;
+       c2 = tablePtr->cols-1+c1;
+       key = 1;
+       goto CLEAR_CELLS;
+    CLEAR_BOTH:
+       key = 0;
+       c1 = clo; c2 = chi;
+    case SEL_COL:
+       r1 = tablePtr->rowOffset;
+       r2 = tablePtr->rows-1+r1;
+       break;
+    case SEL_ROW:
+       c1 = tablePtr->colOffset;
+       c2 = tablePtr->cols-1+c1;
+       break;
+    }
+    /* row/col are in user index coords */
+CLEAR_CELLS:
+    for ( row = r1; row <= r2; row++ ) {
+       for ( col = c1; col <= c2; col++ ) {
+           TableMakeArrayIndex(row, col, buf1);
+           entryPtr = Tcl_FindHashEntry(tablePtr->selCells, buf1);
+           if (entryPtr != NULL) {
+               Tcl_DeleteHashEntry(entryPtr);
+               TableRefresh(tablePtr, row-tablePtr->rowOffset,
+                            col-tablePtr->colOffset, CELL);
+           }
+       }
+    }
+    if (key) goto CLEAR_BOTH;
+    return result;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_SelIncludesCmd --
+ *     This procedure is invoked to process the selection includes method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_SelIncludesCmd(ClientData clientData, register Tcl_Interp *interp,
+                    int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    int row, col;
+
+    if (objc != 4) {
+       Tcl_WrongNumArgs(interp, 3, objv, "index");
+       return TCL_ERROR;
+    } else if (TableGetIndexObj(tablePtr, objv[3], &row, &col) == TCL_ERROR) {
+       return TCL_ERROR;
+    } else {
+       char buf[INDEX_BUFSIZE];
+       TableMakeArrayIndex(row, col, buf);
+       Tcl_SetBooleanObj(Tcl_GetObjResult(interp),
+                         (Tcl_FindHashEntry(tablePtr->selCells, buf)!=NULL));
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_SelSetCmd --
+ *     This procedure is invoked to process the selection set method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_SelSetCmd(ClientData clientData, register Tcl_Interp *interp,
+               int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    int row, col, dummy, key;
+    char buf1[INDEX_BUFSIZE];
+    Tcl_HashSearch search;
+    Tcl_HashEntry *entryPtr;
+
+    int clo=0, chi=0, r1, c1, r2, c2, firstRow, firstCol, lastRow, lastCol;
+    if (objc < 4 || objc > 5) {
+       Tcl_WrongNumArgs(interp, 3, objv, "first ?last?");
+       return TCL_ERROR;
+    }
+    if (TableGetIndexObj(tablePtr, objv[3], &row, &col) == TCL_ERROR ||
+       (objc==5 &&
+        TableGetIndexObj(tablePtr, objv[4], &r2, &c2) == TCL_ERROR)) {
+       return TCL_ERROR;
+    }
+    key = 0;
+    lastRow = tablePtr->rows-1+tablePtr->rowOffset;
+    lastCol = tablePtr->cols-1+tablePtr->colOffset;
+    if (tablePtr->selectTitles) {
+       firstRow = tablePtr->rowOffset;
+       firstCol = tablePtr->colOffset;
+    } else {
+       firstRow = tablePtr->titleRows+tablePtr->rowOffset;
+       firstCol = tablePtr->titleCols+tablePtr->colOffset;
+    }
+    /* maintain appropriate user index */
+    CONSTRAIN(row, firstRow, lastRow);
+    CONSTRAIN(col, firstCol, lastCol);
+    if (objc == 4) {
+       r1 = r2 = row;
+       c1 = c2 = col;
+    } else {
+       CONSTRAIN(r2, firstRow, lastRow);
+       CONSTRAIN(c2, firstCol, lastCol);
+       r1 = MIN(row,r2); r2 = MAX(row,r2);
+       c1 = MIN(col,c2); c2 = MAX(col,c2);
+    }
+    switch (tablePtr->selectType) {
+    case SEL_BOTH:
+       if (firstCol > lastCol) c2--; /* No selectable columns in table */
+       if (firstRow > lastRow) r2--; /* No selectable rows in table */
+       clo = c1; chi = c2;
+       c1 = firstCol;
+       c2 = lastCol;
+       key = 1;
+       goto SET_CELLS;
+    SET_BOTH:
+       key = 0;
+       c1 = clo; c2 = chi;
+    case SEL_COL:
+       r1 = firstRow;
+       r2 = lastRow;
+       if (firstCol > lastCol) c2--; /* No selectable columns in table */
+       break;
+    case SEL_ROW:
+       c1 = firstCol;
+       c2 = lastCol;
+       if (firstRow>lastRow) r2--; /* No selectable rows in table */
+       break;
+    }
+SET_CELLS:
+    entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
+    for ( row = r1; row <= r2; row++ ) {
+       for ( col = c1; col <= c2; col++ ) {
+           TableMakeArrayIndex(row, col, buf1);
+           if (Tcl_FindHashEntry(tablePtr->selCells, buf1) == NULL) {
+               Tcl_CreateHashEntry(tablePtr->selCells, buf1, &dummy);
+               TableRefresh(tablePtr, row-tablePtr->rowOffset,
+                            col-tablePtr->colOffset, CELL);
+           }
+       }
+    }
+    if (key) goto SET_BOTH;
+
+    /* Adjust the table for top left, selection on screen etc */
+    TableAdjustParams(tablePtr);
+
+    /* If the table was previously empty and we want to export the
+     * selection, we should grab it now */
+    if (entryPtr == NULL && tablePtr->exportSelection) {
+       Tk_OwnSelection(tablePtr->tkwin, XA_PRIMARY, TableLostSelection,
+                       (ClientData) tablePtr);
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_ViewCmd --
+ *     This procedure is invoked to process the x|yview method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_ViewCmd(ClientData clientData, register Tcl_Interp *interp,
+             int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    int row, col, value;
+    char *xy;
+
+    /* Check xview or yview */
+    if (objc > 5) {
+       Tcl_WrongNumArgs(interp, 2, objv, "?args?");
+       return TCL_ERROR;
+    }
+    xy = Tcl_GetString(objv[1]);
+
+    if (objc == 2) {
+       Tcl_Obj *resultPtr;
+       int diff, x, y, w, h;
+       double first, last;
+
+       resultPtr = Tcl_GetObjResult(interp);
+       TableGetLastCell(tablePtr, &row, &col);
+       TableCellVCoords(tablePtr, row, col, &x, &y, &w, &h, 0);
+       if (*xy == 'y') {
+           if (row < tablePtr->titleRows) {
+               first = 0;
+               last  = 1;
+           } else {
+               diff = tablePtr->rowStarts[tablePtr->titleRows];
+               last = (double) (tablePtr->rowStarts[tablePtr->rows]-diff);
+               first = (tablePtr->rowStarts[tablePtr->topRow]-diff) / last;
+               last  = (h+tablePtr->rowStarts[row]-diff) / last;
+           }
+       } else {
+           if (col < tablePtr->titleCols) {
+               first = 0;
+               last  = 1;
+           } else {
+               diff = tablePtr->colStarts[tablePtr->titleCols];
+               last = (double) (tablePtr->colStarts[tablePtr->cols]-diff);
+               first = (tablePtr->colStarts[tablePtr->leftCol]-diff) / last;
+               last  = (w+tablePtr->colStarts[col]-diff) / last;
+           }
+       }
+       Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewDoubleObj(first));
+       Tcl_ListObjAppendElement(NULL, resultPtr, Tcl_NewDoubleObj(last));
+    } else {
+       /* cache old topleft to see if it changes */
+       int oldTop = tablePtr->topRow, oldLeft = tablePtr->leftCol;
+
+       if (objc == 3) {
+           if (Tcl_GetIntFromObj(interp, objv[2], &value) != TCL_OK) {
+               return TCL_ERROR;
+           }
+           if (*xy == 'y') {
+               tablePtr->topRow  = value + tablePtr->titleRows;
+           } else {
+               tablePtr->leftCol = value + tablePtr->titleCols;
+           }
+       } else {
+           int result;
+           double frac;
+#if (TK_MINOR_VERSION > 0) /* 8.1+ */
+           result = Tk_GetScrollInfoObj(interp, objc, objv, &frac, &value);
+#else
+           int i;
+           char **argv = (char **) ckalloc((objc + 1) * sizeof(char *));
+           for (i = 0; i < objc; i++) {
+               argv[i] = Tcl_GetString(objv[i]);
+           }
+           argv[i] = NULL;
+           result = Tk_GetScrollInfo(interp, objc, argv, &frac, &value);
+           ckfree ((char *) argv);
+#endif
+           switch (result) {
+           case TK_SCROLL_ERROR:
+               return TCL_ERROR;
+           case TK_SCROLL_MOVETO:
+               if (frac < 0) frac = 0;
+               if (*xy == 'y') {
+                   tablePtr->topRow = (int)(frac*tablePtr->rows)
+                       +tablePtr->titleRows;
+               } else {
+                   tablePtr->leftCol = (int)(frac*tablePtr->cols)
+                       +tablePtr->titleCols;
+               }
+               break;
+           case TK_SCROLL_PAGES:
+               TableGetLastCell(tablePtr, &row, &col);
+               if (*xy == 'y') {
+                   tablePtr->topRow  += value * (row-tablePtr->topRow+1);
+               } else {
+                   tablePtr->leftCol += value * (col-tablePtr->leftCol+1);
+               }
+               break;
+           case TK_SCROLL_UNITS:
+               if (*xy == 'y') {
+                   tablePtr->topRow  += value;
+               } else {
+                   tablePtr->leftCol += value;
+               }
+               break;
+           }
+       }
+       /* maintain appropriate real index */
+       CONSTRAIN(tablePtr->topRow, tablePtr->titleRows, tablePtr->rows-1);
+       CONSTRAIN(tablePtr->leftCol, tablePtr->titleCols, tablePtr->cols-1);
+       /* Do the table adjustment if topRow || leftCol changed */      
+       if (oldTop != tablePtr->topRow || oldLeft != tablePtr->leftCol) {
+           TableAdjustParams(tablePtr);
+       }
+    }
+
+    return TCL_OK;
+}
+\f
+#if 0
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_Cmd --
+ *     This procedure is invoked to process the CMD method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_Cmd(ClientData clientData, register Tcl_Interp *interp,
+         int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    int result = TCL_OK;
+
+    return result;
+}
+#endif
diff --git a/libgui/src/tkTableEdit.c b/libgui/src/tkTableEdit.c
new file mode 100644 (file)
index 0000000..3fd6d48
--- /dev/null
@@ -0,0 +1,683 @@
+/* 
+ * tkTableEdit.c --
+ *
+ *     This module implements editing functions of a table widget.
+ *
+ * Copyright (c) 1998-2000 Jeffrey Hobbs
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#include "tkTable.h"
+
+static void    TableModifyRC _ANSI_ARGS_((register Table *tablePtr,
+                       int doRows, int movetag,
+                       Tcl_HashTable *tagTblPtr, Tcl_HashTable *dimTblPtr,
+                       int offset, int from, int to, int lo, int hi,
+                       int outOfBounds));
+
+/* insert/delete subcommands */
+static char *modCmdNames[] = {
+    "active", "cols", "rows", (char *)NULL
+};
+enum modCmd {
+    MOD_ACTIVE, MOD_COLS, MOD_ROWS
+};
+
+/* insert/delete row/col switches */
+static char *rcCmdNames[] = {
+    "-keeptitles",     "-holddimensions",      "-holdselection",
+    "-holdtags",       "-holdwindows", "--",
+    (char *) NULL
+};
+enum rcCmd {
+    OPT_TITLES,        OPT_DIMS,       OPT_SEL,
+    OPT_TAGS,  OPT_WINS,       OPT_LAST
+};
+
+#define HOLD_TITLES    1<<0
+#define HOLD_DIMS      1<<1
+#define HOLD_TAGS      1<<2
+#define HOLD_WINS      1<<3
+#define HOLD_SEL       1<<4
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_EditCmd --
+ *     This procedure is invoked to process the insert/delete method
+ *     that corresponds to a table widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_EditCmd(ClientData clientData, register Tcl_Interp *interp,
+             int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *) clientData;
+    int doInsert, cmdIndex, first, last;
+
+    if (objc < 4) {
+       Tcl_WrongNumArgs(interp, 2, objv,
+                        "option ?switches? arg ?arg?");
+       return TCL_ERROR;
+    }
+    if (Tcl_GetIndexFromObj(interp, objv[2], modCmdNames,
+                           "option", 0, &cmdIndex) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    doInsert = (*(Tcl_GetString(objv[1])) == 'i');
+    switch ((enum modCmd) cmdIndex) {
+    case MOD_ACTIVE:
+       if (doInsert) {
+           /* INSERT */
+           if (objc != 5) {
+               Tcl_WrongNumArgs(interp, 3, objv, "index string");
+               return TCL_ERROR;
+           }
+           if (TableGetIcursorObj(tablePtr, objv[3], &first) != TCL_OK) {
+               return TCL_ERROR;
+           } else if ((tablePtr->flags & HAS_ACTIVE) &&
+                      !(tablePtr->flags & ACTIVE_DISABLED) &&
+                      tablePtr->state == STATE_NORMAL) {
+               TableInsertChars(tablePtr, first, Tcl_GetString(objv[4]));
+           }
+       } else {
+           /* DELETE */
+           if (objc > 5) {
+               Tcl_WrongNumArgs(interp, 3, objv, "first ?last?");
+               return TCL_ERROR;
+           }
+           if (TableGetIcursorObj(tablePtr, objv[3], &first) != TCL_OK) {
+               return TCL_ERROR;
+           }
+           if (objc == 4) {
+               last = first+1;
+           } else if (TableGetIcursorObj(tablePtr, objv[4],
+                                         &last) != TCL_OK) {
+               return TCL_ERROR;
+           }
+           if ((last >= first) && (tablePtr->flags & HAS_ACTIVE) &&
+               !(tablePtr->flags & ACTIVE_DISABLED) &&
+               tablePtr->state == STATE_NORMAL) {
+               TableDeleteChars(tablePtr, first, last-first);
+           }
+       }
+       break;  /* EDIT ACTIVE */
+
+    case MOD_COLS:
+    case MOD_ROWS: {
+       /*
+        * ROW/COL INSERTION/DELETION
+        * FIX: This doesn't handle spans
+        */
+       int i, lo, hi, argsLeft, offset, minkeyoff, doRows;
+       int maxrow, maxcol, maxkey, minkey, flags, count, *dimPtr;
+       Tcl_HashTable *tagTblPtr, *dimTblPtr;
+       Tcl_HashSearch search;
+
+       doRows  = (cmdIndex == MOD_ROWS);
+       flags   = 0;
+       for (i = 3; i < objc; i++) {
+           if (*(Tcl_GetString(objv[i])) != '-') {
+               break;
+           }
+           if (Tcl_GetIndexFromObj(interp, objv[i], rcCmdNames,
+                                   "switch", 0, &cmdIndex) != TCL_OK) {
+               return TCL_ERROR;
+           }
+           if (cmdIndex == OPT_LAST) {
+               i++;
+               break;
+           }
+           switch (cmdIndex) {
+           case OPT_TITLES:
+               flags |= HOLD_TITLES;
+               break;
+           case OPT_DIMS:
+               flags |= HOLD_DIMS;
+               break;
+           case OPT_SEL:
+               flags |= HOLD_SEL;
+               break;
+           case OPT_TAGS:
+               flags |= HOLD_TAGS;
+               break;
+           case OPT_WINS:
+               flags |= HOLD_WINS;
+               break;
+           }
+       }
+       argsLeft = objc - i;
+       if (argsLeft < 1 || argsLeft > 2) {
+           Tcl_WrongNumArgs(interp, 3, objv, "?switches? index ?count?");
+           return TCL_ERROR;
+       }
+
+       count   = 1;
+       maxcol  = tablePtr->cols-1+tablePtr->colOffset;
+       maxrow  = tablePtr->rows-1+tablePtr->rowOffset;
+       if (strcmp(Tcl_GetString(objv[i]), "end") == 0) {
+           /* allow "end" to be specified as an index */
+           first = (doRows) ? maxrow : maxcol;
+       } else if (Tcl_GetIntFromObj(interp, objv[i], &first) != TCL_OK) {
+           return TCL_ERROR;
+       }
+       if (argsLeft == 2 &&
+           Tcl_GetIntFromObj(interp, objv[++i], &count) != TCL_OK) {
+           return TCL_ERROR;
+       }
+       if (count == 0 || (tablePtr->state == STATE_DISABLED)) {
+           return TCL_OK;
+       }
+
+       if (doRows) {
+           maxkey      = maxrow;
+           minkey      = tablePtr->rowOffset;
+           minkeyoff   = tablePtr->rowOffset+tablePtr->titleRows;
+           offset      = tablePtr->rowOffset;
+           tagTblPtr   = tablePtr->rowStyles;
+           dimTblPtr   = tablePtr->rowHeights;
+           dimPtr      = &(tablePtr->rows);
+           lo          = tablePtr->colOffset
+               + ((flags & HOLD_TITLES) ? tablePtr->titleCols : 0);
+           hi          = maxcol;
+       } else {
+           maxkey      = maxcol;
+           minkey      = tablePtr->colOffset;
+           minkeyoff   = tablePtr->colOffset+tablePtr->titleCols;
+           offset      = tablePtr->colOffset;
+           tagTblPtr   = tablePtr->colStyles;
+           dimTblPtr   = tablePtr->colWidths;
+           dimPtr      = &(tablePtr->cols);
+           lo          = tablePtr->rowOffset
+               + ((flags & HOLD_TITLES) ? tablePtr->titleRows : 0);
+           hi          = maxrow;
+       }
+
+       /* constrain the starting index */
+       if (first > maxkey) {
+           first = maxkey;
+       } else if (first < minkey) {
+           first = minkey;
+       }
+       if (doInsert) {
+           /* +count means insert after index,
+            * -count means insert before index */
+           if (count < 0) {
+               count = -count;
+           } else {
+               first++;
+           }
+           if ((flags & HOLD_TITLES) && (first < minkeyoff)) {
+               count -= minkeyoff-first;
+               if (count <= 0) {
+                   return TCL_OK;
+               }
+               first = minkeyoff;
+           }
+           if (!(flags & HOLD_DIMS)) {
+               maxkey += count;
+               *dimPtr += count;
+           }
+           for (i = maxkey; i >= first; i--) {
+               /* move row/col style && width/height here */
+               TableModifyRC(tablePtr, doRows, flags, tagTblPtr, dimTblPtr,
+                       offset, i, i-count, lo, hi, ((i-count) < first));
+           }
+       } else {
+           /* (index = i && count = 1) == (index = i && count = -1) */
+           if (count < 0) {
+               /* if the count is negative, make sure that the col count will
+                * delete no greater than the original index */
+               if (first+count < minkey) {
+                   if (first-minkey < abs(count)) {
+                       /*
+                        * In this case, the user is asking to delete more rows
+                        * than exist before the minkey, so we have to shrink
+                        * the count down to the existing rows up to index.
+                        */
+                       count = first-minkey;
+                   } else {
+                       count += first-minkey;
+                   }
+                   first = minkey;
+               } else {
+                   first += count;
+                   count = -count;
+               }
+           }
+           if ((flags & HOLD_TITLES) && (first <= minkeyoff)) {
+               count -= minkeyoff-first;
+               if (count <= 0) {
+                   return TCL_OK;
+               }
+               first = minkeyoff;
+           }
+           if (count > maxkey-first+1) {
+               count = maxkey-first+1;
+           }
+           if (!(flags & HOLD_DIMS)) {
+               *dimPtr -= count;
+           }
+           for (i = first; i <= maxkey; i++) {
+               TableModifyRC(tablePtr, doRows, flags, tagTblPtr, dimTblPtr,
+                       offset, i, i+count, lo, hi, ((i+count) > maxkey));
+           }
+       }
+       if (!(flags & HOLD_SEL) &&
+               Tcl_FirstHashEntry(tablePtr->selCells, &search) != NULL) {
+           /* clear selection - forceful, but effective */
+           Tcl_DeleteHashTable(tablePtr->selCells);
+           Tcl_InitHashTable(tablePtr->selCells, TCL_STRING_KEYS);
+       }
+
+       /*
+        * Make sure that the modified dimension is actually legal
+        * after removing all that stuff.
+        */
+       *dimPtr = MAX(1, *dimPtr);
+
+       TableAdjustParams(tablePtr);
+       /* change the geometry */
+       TableGeometryRequest(tablePtr);
+       /* FIX:
+        * This has to handle when the previous rows/cols resize because
+        * of the *stretchmode.  InvalidateAll does that, but could be
+        * more efficient.
+        */
+       TableInvalidateAll(tablePtr, 0);
+       break;
+    }
+
+    }
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableDeleteChars --
+ *     Remove one or more characters from an table widget.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Memory gets freed, the table gets modified and (eventually)
+ *     redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+TableDeleteChars(tablePtr, index, count)
+    register Table *tablePtr;  /* Table widget to modify. */
+    int index;                 /* Index of first character to delete. */
+    int count;                 /* How many characters to delete. */
+{
+#ifdef TCL_UTF_MAX
+    int byteIndex, byteCount, newByteCount, numBytes, numChars;
+    char *new, *string;
+
+    string = tablePtr->activeBuf;
+    numBytes = strlen(string);
+    numChars = Tcl_NumUtfChars(string, numBytes);
+    if ((index + count) > numChars) {
+       count = numChars - index;
+    }
+    if (count <= 0) {
+       return;
+    }
+
+    byteIndex = Tcl_UtfAtIndex(string, index) - string;
+    byteCount = Tcl_UtfAtIndex(string + byteIndex, count)
+       - (string + byteIndex);
+
+    newByteCount = numBytes + 1 - byteCount;
+    new = (char *) ckalloc((unsigned) newByteCount);
+    memcpy(new, string, (size_t) byteIndex);
+    strcpy(new + byteIndex, string + byteIndex + byteCount);
+#else
+    int oldlen;
+    char *new;
+
+    /* this gets the length of the string, as well as ensuring that
+     * the cursor isn't beyond the end char */
+    TableGetIcursor(tablePtr, "end", &oldlen);
+
+    if ((index+count) > oldlen)
+       count = oldlen-index;
+    if (count <= 0)
+       return;
+
+    new = (char *) ckalloc((unsigned)(oldlen-count+1));
+    strncpy(new, tablePtr->activeBuf, (size_t) index);
+    strcpy(new+index, tablePtr->activeBuf+index+count);
+    /* make sure this string is null terminated */
+    new[oldlen-count] = '\0';
+#endif
+    /* This prevents deletes on BREAK or validation error. */
+    if (tablePtr->validate &&
+       TableValidateChange(tablePtr, tablePtr->activeRow+tablePtr->rowOffset,
+                           tablePtr->activeCol+tablePtr->colOffset,
+                           tablePtr->activeBuf, new, index) != TCL_OK) {
+       ckfree(new);
+       return;
+    }
+
+    ckfree(tablePtr->activeBuf);
+    tablePtr->activeBuf = new;
+
+    /* mark the text as changed */
+    tablePtr->flags |= TEXT_CHANGED;
+
+    if (tablePtr->icursor >= index) {
+       if (tablePtr->icursor >= (index+count)) {
+           tablePtr->icursor -= count;
+       } else {
+           tablePtr->icursor = index;
+       }
+    }
+
+    TableSetActiveIndex(tablePtr);
+
+    TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol, CELL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableInsertChars --
+ *     Add new characters to the active cell of a table widget.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     New information gets added to tablePtr; it will be redisplayed
+ *     soon, but not necessarily immediately.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+TableInsertChars(tablePtr, index, value)
+    register Table *tablePtr;  /* Table that is to get the new elements. */
+    int index;                 /* Add the new elements before this element. */
+    char *value;               /* New characters to add (NULL-terminated
+                                * string). */
+{
+#ifdef TCL_UTF_MAX
+    int oldlen, byteIndex, byteCount;
+    char *new, *string;
+
+    byteCount = strlen(value);
+    if (byteCount == 0) {
+       return;
+    }
+
+    /* Is this an autoclear and this is the first update */
+    /* Note that this clears without validating */
+    if (tablePtr->autoClear && !(tablePtr->flags & TEXT_CHANGED)) {
+       /* set the buffer to be empty */
+       tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf, 1);
+       tablePtr->activeBuf[0] = '\0';
+       /* the insert position now has to be 0 */
+       index = 0;
+       tablePtr->icursor = 0;
+    }
+
+    string = tablePtr->activeBuf;
+    byteIndex = Tcl_UtfAtIndex(string, index) - string;
+
+    oldlen = strlen(string);
+    new = (char *) ckalloc((unsigned)(oldlen + byteCount + 1));
+    memcpy(new, string, (size_t) byteIndex);
+    strcpy(new + byteIndex, value);
+    strcpy(new + byteIndex + byteCount, string + byteIndex);
+
+    /* validate potential new active buffer */
+    /* This prevents inserts on either BREAK or validation error. */
+    if (tablePtr->validate &&
+       TableValidateChange(tablePtr, tablePtr->activeRow+tablePtr->rowOffset,
+                           tablePtr->activeCol+tablePtr->colOffset,
+                           tablePtr->activeBuf, new, byteIndex) != TCL_OK) {
+       ckfree(new);
+       return;
+    }
+
+    /*
+     * The following construction is used because inserting improperly
+     * formed UTF-8 sequences between other improperly formed UTF-8
+     * sequences could result in actually forming valid UTF-8 sequences;
+     * the number of characters added may not be Tcl_NumUtfChars(string, -1),
+     * because of context.  The actual number of characters added is how
+     * many characters were are in the string now minus the number that
+     * used to be there.
+     */
+
+    if (tablePtr->icursor >= index) {
+       tablePtr->icursor += Tcl_NumUtfChars(new, oldlen+byteCount)
+           - Tcl_NumUtfChars(tablePtr->activeBuf, oldlen);
+    }
+
+    ckfree(string);
+    tablePtr->activeBuf = new;
+
+#else
+    int oldlen, newlen;
+    char *new;
+
+    newlen = strlen(value);
+    if (newlen == 0) return;
+
+    /* Is this an autoclear and this is the first update */
+    /* Note that this clears without validating */
+    if (tablePtr->autoClear && !(tablePtr->flags & TEXT_CHANGED)) {
+       /* set the buffer to be empty */
+       tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf, 1);
+       tablePtr->activeBuf[0] = '\0';
+       /* the insert position now has to be 0 */
+       index = 0;
+    }
+    oldlen = strlen(tablePtr->activeBuf);
+    /* get the buffer to at least the right length */
+    new = (char *) ckalloc((unsigned)(oldlen+newlen+1));
+    strncpy(new, tablePtr->activeBuf, (size_t) index);
+    strcpy(new+index, value);
+    strcpy(new+index+newlen, (tablePtr->activeBuf)+index);
+    /* make sure this string is null terminated */
+    new[oldlen+newlen] = '\0';
+
+    /* validate potential new active buffer */
+    /* This prevents inserts on either BREAK or validation error. */
+    if (tablePtr->validate &&
+       TableValidateChange(tablePtr, tablePtr->activeRow+tablePtr->rowOffset,
+                           tablePtr->activeCol+tablePtr->colOffset,
+                           tablePtr->activeBuf, new, index) != TCL_OK) {
+       ckfree(new);
+       return;
+    }
+    ckfree(tablePtr->activeBuf);
+    tablePtr->activeBuf = new;
+
+    if (tablePtr->icursor >= index) {
+       tablePtr->icursor += newlen;
+    }
+#endif
+
+    /* mark the text as changed */
+    tablePtr->flags |= TEXT_CHANGED;
+
+    TableSetActiveIndex(tablePtr);
+
+    TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol, CELL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableModifyRC --
+ *     Helper function that does the core work of moving rows/cols
+ *     and associated tags.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Moves cell data and possibly tag data
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+TableModifyRC(tablePtr, doRows, flags, tagTblPtr, dimTblPtr,
+             offset, from, to, lo, hi, outOfBounds)
+    Table *tablePtr;   /* Information about text widget. */
+    int doRows;                /* rows (1) or cols (0) */
+    int flags;         /* flags indicating what to move */
+    Tcl_HashTable *tagTblPtr, *dimTblPtr; /* Pointers to the row/col tags
+                                          * and width/height tags */
+    int offset;                /* appropriate offset */
+    int from, to;      /* the from and to row/col */
+    int lo, hi;                /* the lo and hi col/row */
+    int outOfBounds;   /* the boundary check for shifting items */
+{
+    int j, new;
+    char buf[INDEX_BUFSIZE], buf1[INDEX_BUFSIZE];
+    Tcl_HashEntry *entryPtr, *newPtr;
+    TableEmbWindow *ewPtr;
+
+    /*
+     * move row/col style && width/height here
+     * If -holdtags is specified, we don't move the user-set widths/heights
+     * of the absolute rows/columns, otherwise we enter here to move the
+     * dimensions appropriately
+     */
+    if (!(flags & HOLD_TAGS)) {
+       entryPtr = Tcl_FindHashEntry(tagTblPtr, (char *)from);
+       if (entryPtr != NULL) {
+           Tcl_DeleteHashEntry(entryPtr);
+       }
+       entryPtr = Tcl_FindHashEntry(dimTblPtr, (char *)from-offset);
+       if (entryPtr != NULL) {
+           Tcl_DeleteHashEntry(entryPtr);
+       }
+       if (!outOfBounds) {
+           entryPtr = Tcl_FindHashEntry(tagTblPtr, (char *)to);
+           if (entryPtr != NULL) {
+               newPtr = Tcl_CreateHashEntry(tagTblPtr, (char *)from, &new);
+               Tcl_SetHashValue(newPtr, Tcl_GetHashValue(entryPtr));
+               Tcl_DeleteHashEntry(entryPtr);
+           }
+           entryPtr = Tcl_FindHashEntry(dimTblPtr, (char *)to-offset);
+           if (entryPtr != NULL) {
+               newPtr = Tcl_CreateHashEntry(dimTblPtr, (char *)from-offset,
+                       &new);
+               Tcl_SetHashValue(newPtr, Tcl_GetHashValue(entryPtr));
+               Tcl_DeleteHashEntry(entryPtr);
+           }
+       }
+    }
+    for (j = lo; j <= hi; j++) {
+       if (doRows /* rows */) {
+           TableMakeArrayIndex(from, j, buf);
+           TableMakeArrayIndex(to, j, buf1);
+           TableMoveCellValue(tablePtr, to, j, buf1, from, j, buf,
+                   outOfBounds);
+       } else {
+           TableMakeArrayIndex(j, from, buf);
+           TableMakeArrayIndex(j, to, buf1);
+           TableMoveCellValue(tablePtr, j, to, buf1, j, from, buf,
+                   outOfBounds);
+       }
+       /*
+        * If -holdselection is specified, we leave the selected cells in the
+        * absolute cell values, otherwise we enter here to move the
+        * selection appropriately
+        */
+       if (!(flags & HOLD_SEL)) {
+           entryPtr = Tcl_FindHashEntry(tablePtr->selCells, buf);
+           if (entryPtr != NULL) {
+               Tcl_DeleteHashEntry(entryPtr);
+           }
+           if (!outOfBounds) {
+               entryPtr = Tcl_FindHashEntry(tablePtr->selCells, buf1);
+               if (entryPtr != NULL) {
+                   Tcl_CreateHashEntry(tablePtr->selCells, buf, &new);
+                   Tcl_DeleteHashEntry(entryPtr);
+               }
+           }
+       }
+       /*
+        * If -holdtags is specified, we leave the tags in the
+        * absolute cell values, otherwise we enter here to move the
+        * tags appropriately
+        */
+       if (!(flags & HOLD_TAGS)) {
+           entryPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf);
+           if (entryPtr != NULL) {
+               Tcl_DeleteHashEntry(entryPtr);
+           }
+           if (!outOfBounds) {
+               entryPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf1);
+               if (entryPtr != NULL) {
+                   newPtr = Tcl_CreateHashEntry(tablePtr->cellStyles, buf,
+                           &new);
+                   Tcl_SetHashValue(newPtr, Tcl_GetHashValue(entryPtr));
+                   Tcl_DeleteHashEntry(entryPtr);
+               }
+           }
+       }
+       /*
+        * If -holdwindows is specified, we leave the windows in the
+        * absolute cell values, otherwise we enter here to move the
+        * windows appropriately
+        */
+       if (!(flags & HOLD_WINS)) {
+           /*
+            * Delete whatever window might be in our destination
+            */
+           Table_WinDelete(tablePtr, buf);
+           if (!outOfBounds) {
+               /*
+                * buf1 is where the window is
+                * buf is where we want it to be
+                *
+                * This is an adaptation of Table_WinMove, which we can't
+                * use because we are intermediately fiddling with boundaries
+                */
+               entryPtr = Tcl_FindHashEntry(tablePtr->winTable, buf1);
+               if (entryPtr != NULL) {
+                   /*
+                    * If there was a window in our source,
+                    * get the window pointer to move it
+                    */
+                   ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
+                   /* and free the old hash table entry */
+                   Tcl_DeleteHashEntry(entryPtr);
+
+                   entryPtr = Tcl_CreateHashEntry(tablePtr->winTable, buf,
+                           &new);
+                   /*
+                    * We needn't check if a window was in buf, since the
+                    * Table_WinDelete above should guarantee that no window
+                    * is there.  Just set the new entry's value.
+                    */
+                   Tcl_SetHashValue(entryPtr, (ClientData) ewPtr);
+                   ewPtr->hPtr = entryPtr;
+               }
+           }
+       }
+    }
+}
diff --git a/libgui/src/tkTableInitScript.h b/libgui/src/tkTableInitScript.h
new file mode 100644 (file)
index 0000000..1084717
--- /dev/null
@@ -0,0 +1,90 @@
+/* 
+ * tkTableInitScript.h --
+ *
+ *     This file contains common init script for tkTable
+ *
+ * Copyright (c) 1998 Jeffrey Hobbs
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+/*
+ * The following string is the startup script executed when the table is
+ * loaded.  It looks on disk in several different directories for a script
+ * "TBL_RUNTIME" (as defined in Makefile) that is compatible with this
+ * version of tkTable.  The sourced script has all key bindings defined.
+ */
+
+static char tkTableInitScript[] = "if {[info proc tkTableInit]==\"\"} {\n\
+  proc tkTableInit {} {\n\
+    global tk_library tcl_pkgPath errorInfo env\n\
+    rename tkTableInit {}\n\
+    set errors {}\n\
+    if {![info exists env(TK_TABLE_LIBRARY_FILE)]} {\n\
+       set env(TK_TABLE_LIBRARY_FILE) " TBL_RUNTIME "\n\
+    }\n\
+    if {[info exists env(TK_TABLE_LIBRARY)]} {\n\
+       lappend dirs $env(TK_TABLE_LIBRARY)\n\
+    }\n\
+    lappend dirs " TBL_RUNTIME_DIR "\n\
+    if {[info exists tcl_pkgPath]} {\n\
+       foreach i $tcl_pkgPath {\n\
+           lappend dirs [file join $i Tktable" TBL_VERSION "] \\\n\
+               [file join $i Tktable] $i\n\
+       }\n\
+    }\n\
+    lappend dirs $tk_library [pwd]\n\
+    foreach i $dirs {\n\
+       set try [file join $i $env(TK_TABLE_LIBRARY_FILE)]\n\
+       if {[file exists $try]} {\n\
+           if {![catch {uplevel #0 [list source $try]} msg]} {\n\
+               set env(TK_TABLE_LIBRARY) $i\n\
+               return\n\
+           } else {\n\
+               append errors \"$try: $msg\n$errorInfo\n\"\n\
+           }\n\
+       }\n\
+    }\n"
+#ifdef NO_EMBEDDED_RUNTIME
+"    set msg \"Can't find a $env(TK_TABLE_LIBRARY_FILE) in the following directories: \n\"\n\
+    append msg \"    $dirs\n\n$errors\n\n\"\n\
+    append msg \"This probably means that TkTable wasn't installed properly.\"\n\
+    return -code error $msg\n"
+#else
+"    set env(TK_TABLE_LIBRARY) EMBEDDED_RUNTIME\n"
+#   ifdef MAC_TCL
+"    source -rsrc tkTable"
+#   else
+"    uplevel #0 {"
+#      include "tkTable.tcl.h"
+"    }"
+#   endif
+#endif
+"  }\n\
+}\n\
+tkTableInit";
+
+/*
+ * The init script can't make certain calls in a safe interpreter,
+ * so we always have to use the embedded runtime for it
+ */
+static char tkTableSafeInitScript[] = "if {[info proc tkTableInit]==\"\"} {\n\
+  proc tkTableInit {} {\n\
+    set env(TK_TABLE_LIBRARY) EMBEDDED_RUNTIME\n"
+#ifdef NO_EMBEDDED_RUNTIME
+"    append msg \"tkTable requires embedded runtime to be compiled for\"\n\
+    append msg \" use in safe interpreters\"\n\
+    return -code error $msg\n"
+#endif
+#   ifdef MAC_TCL
+"    source -rsrc tkTable"
+#   else
+"    uplevel #0 {"
+#      include "tkTable.tcl.h"
+"    }"
+#   endif
+"  }\n\
+}\n\
+tkTableInit";
+
diff --git a/libgui/src/tkTablePs.c b/libgui/src/tkTablePs.c
new file mode 100644 (file)
index 0000000..018f079
--- /dev/null
@@ -0,0 +1,1299 @@
+/* 
+ * tkTablePs.c --
+ *
+ *     This module implements postscript output for table widgets.
+ *     Based off of Tk8.1a2 tkCanvPs.c.
+ *
+ * Copyright (c) 1991-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ * changes 1998 Copyright (c) 1998 Jeffrey Hobbs
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+#include "tkTable.h"
+
+/* This is for Tcl_DStringAppendAll */
+#if defined(__STDC__) || defined(HAS_STDARG)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#ifndef TCL_INTEGER_SPACE
+/* This appears in 8.1 */
+#define TCL_INTEGER_SPACE 24
+#endif
+
+/*
+ * One of the following structures is created to keep track of Postscript
+ * output being generated.  It consists mostly of information provided on
+ * the widget command line.
+ */
+
+typedef struct TkPostscriptInfo {
+  int x, y, width, height;     /* Area to print, in table pixel
+                                * coordinates. */
+  int x2, y2;                  /* x+width and y+height. */
+  char *pageXString;           /* String value of "-pagex" option or NULL. */
+  char *pageYString;           /* String value of "-pagey" option or NULL. */
+  double pageX, pageY;         /* Postscript coordinates (in points)
+                                * corresponding to pageXString and
+                                * pageYString. Don't forget that y-values
+                                * grow upwards for Postscript! */
+  char *pageWidthString;       /* Printed width of output. */
+  char *pageHeightString;      /* Printed height of output. */
+  double scale;                        /* Scale factor for conversion: each pixel
+                                * maps into this many points. */
+  Tk_Anchor pageAnchor;                /* How to anchor bbox on Postscript page. */
+  int rotate;                  /* Non-zero means output should be rotated
+                                * on page (landscape mode). */
+  char *fontVar;               /* If non-NULL, gives name of global variable
+                                * containing font mapping information.
+                                * Malloc'ed. */
+  char *colorVar;              /* If non-NULL, give name of global variable
+                                * containing color mapping information.
+                                * Malloc'ed. */
+  char *colorMode;             /* Mode for handling colors:  "monochrome",
+                                * "gray", or "color".  Malloc'ed. */
+  int colorLevel;              /* Numeric value corresponding to colorMode:
+                                * 0 for mono, 1 for gray, 2 for color. */
+  char *fileName;              /* Name of file in which to write Postscript;
+                                * NULL means return Postscript info as
+                                * result. Malloc'ed. */
+  char *channelName;           /* If -channel is specified, the name of
+                                 * the channel to use. */
+  Tcl_Channel chan;            /* Open channel corresponding to fileName. */
+  Tcl_HashTable fontTable;     /* Hash table containing names of all font
+                                * families used in output.  The hash table
+                                * values are not used. */
+  char *first, *last;          /* table indices to start and end at */
+} TkPostscriptInfo;
+
+/*
+ * The table below provides a template that's used to process arguments
+ * to the table "postscript" command and fill in TkPostscriptInfo
+ * structures.
+ */
+
+static Tk_ConfigSpec configSpecs[] = {
+  {TK_CONFIG_STRING, "-colormap", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, colorVar), 0},
+  {TK_CONFIG_STRING, "-colormode", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, colorMode), 0},
+  {TK_CONFIG_STRING, "-file", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, fileName), 0},
+  {TK_CONFIG_STRING, "-channel", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, channelName), 0},
+  {TK_CONFIG_STRING, "-first", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, first), 0},
+  {TK_CONFIG_STRING, "-fontmap", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, fontVar), 0},
+  {TK_CONFIG_PIXELS, "-height", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, height), 0},
+  {TK_CONFIG_STRING, "-last", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, last), 0},
+  {TK_CONFIG_ANCHOR, "-pageanchor", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, pageAnchor), 0},
+  {TK_CONFIG_STRING, "-pageheight", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, pageHeightString), 0},
+  {TK_CONFIG_STRING, "-pagewidth", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, pageWidthString), 0},
+  {TK_CONFIG_STRING, "-pagex", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, pageXString), 0},
+  {TK_CONFIG_STRING, "-pagey", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, pageYString), 0},
+  {TK_CONFIG_BOOLEAN, "-rotate", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, rotate), 0},
+  {TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, width), 0},
+  {TK_CONFIG_PIXELS, "-x", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, x), 0},
+  {TK_CONFIG_PIXELS, "-y", (char *) NULL, (char *) NULL, "",
+   Tk_Offset(TkPostscriptInfo, y), 0},
+  {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+   (char *) NULL, 0, 0}
+};
+
+/*
+ * The prolog data. Generated by str2c from prolog.ps
+ * This was split in small chunks by str2c because
+ * some C compiler have limitations on the size of static strings.
+ * (str2c is a small tcl script in tcl's tool directory (source release))
+ */
+/*
+ * This is a stripped down version of that found in tkCanvPs.c of Tk8.1a2.
+ * Comments, and stuff pertaining to stipples and other unused entities
+ * have been removed
+ */
+static CONST char * CONST  prolog[]= {
+       /* Start of part 1 */
+       "%%BeginProlog\n\
+50 dict begin\n\
+\n\
+% This is standard prolog for Postscript generated by Tk's table widget.\n\
+% Based of standard prolog for Tk's canvas widget.\n\
+\n\
+% INITIALIZING VARIABLES\n\
+\n\
+/baseline 0 def\n\
+/height 0 def\n\
+/justify 0 def\n\
+/cellHeight 0 def\n\
+/cellWidth 0 def\n\
+/spacing 0 def\n\
+/strings 0 def\n\
+/xoffset 0 def\n\
+/yoffset 0 def\n\
+/x 0 def\n\
+/y 0 def\n\
+\n\
+% Define the array ISOLatin1Encoding, if it isn't already present.\n\
+\n\
+systemdict /ISOLatin1Encoding known not {\n\
+    /ISOLatin1Encoding [\n\
+       /space /space /space /space /space /space /space /space\n\
+       /space /space /space /space /space /space /space /space\n\
+       /space /space /space /space /space /space /space /space\n\
+       /space /space /space /space /space /space /space /space\n\
+       /space /exclam /quotedbl /numbersign /dollar /percent /ampersand\n\
+           /quoteright\n\
+       /parenleft /parenright /asterisk /plus /comma /minus /period /slash\n\
+       /zero /one /two /three /four /five /six /seven\n\
+       /eight /nine /colon /semicolon /less /equal /greater /question\n\
+       /at /A /B /C /D /E /F /G\n\
+       /H /I /J /K /L /M /N /O\n\
+       /P /Q /R /S /T /U /V /W\n\
+       /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore\n\
+       /quoteleft /a /b /c /d /e /f /g\n\
+       /h /i /j /k /l /m /n /o\n\
+       /p /q /r /s /t /u /v /w\n\
+       /x /y /z /braceleft /bar /braceright /asciitilde /space\n\
+       /space /space /space /space /space /space /space /space\n\
+       /space /space /space /space /space /space /space /space\n\
+       /dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent\n\
+       /dieresis /space /ring /cedilla /space /hungarumlaut /ogonek /caron\n\
+       /space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n\
+       /dieresis /copyright /ordfem",
+
+       "inine /guillemotleft /logicalnot /hyphen\n\
+           /registered /macron\n\
+       /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph\n\
+           /periodcentered\n\
+       /cedillar /onesuperior /ordmasculine /guillemotright /onequarter\n\
+           /onehalf /threequarters /questiondown\n\
+       /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n\
+       /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex\n\
+           /Idieresis\n\
+       /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply\n\
+       /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn\n\
+           /germandbls\n\
+       /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n\
+       /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex\n\
+           /idieresis\n\
+       /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n\
+       /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn\n\
+           /ydieresis\n\
+    ] def\n\
+} if\n",
+
+       "\n\
+% font ISOEncode font\n\
+% This procedure changes the encoding of a font from the default\n\
+% Postscript encoding to ISOLatin1.  It's typically invoked just\n\
+% before invoking \"setfont\".  The body of this procedure comes from\n\
+% Section 5.6.1 of the Postscript book.\n\
+\n\
+/ISOEncode {\n\
+    dup length dict begin\n\
+       {1 index /FID ne {def} {pop pop} ifelse} forall\n\
+       /Encoding ISOLatin1Encoding def\n\
+       currentdict\n\
+    end\n\
+\n\
+    % I'm not sure why it's necessary to use \"definefont\" on this new\n\
+    % font, but it seems to be important; just use the name \"Temporary\"\n\
+    % for the font.\n\
+\n\
+    /Temporary exch definefont\n\
+} bind def\n\
+\n\
+% -- AdjustColor --\n\
+% Given a color value already set for output by the caller, adjusts\n\
+% that value to a grayscale or mono value if requested by the CL variable.\n\
+\n\
+/AdjustColor {\n\
+    setrgbcolor\n\
+    CL 2 lt {\n\
+       currentgray\n\
+       CL 0 eq {\n\
+           .5 lt {0} {1} ifelse\n\
+       } if\n\
+       setgray\n\
+    } if\n\
+} bind def\n\
+\n\
+% pointSize fontName SetFont\n\
+% The ISOEncode shouldn't be done to Symbol fonts...\n\
+/SetFont {\n\
+  findfont exch scalefont ISOEncode setfont\n\
+} def\n\
+\n",
+
+       "% x y strings spacing xoffset yoffset justify ... DrawText --\n\
+% This procedure does all of the real work of drawing text.  The\n\
+% color and font must already have been set by the caller, and the\n\
+% following arguments must be on the stack:\n\
+%\n\
+% x, y -       Coordinates at which to draw text.\n\
+% strings -    An array of strings, one for each line of the text item,\n\
+%              in order from top to bottom.\n\
+% spacing -    Spacing between lines.\n\
+% xoffset -    Horizontal offset for text bbox relative to x and y: 0 for\n\
+%              nw/w/sw anchor, -0.5 for n/center/s, and -1.0 for ne/e/se.\n\
+% yoffset -    Vertical offset for text bbox relative to x and y: 0 for\n\
+%              nw/n/ne anchor, +0.5 for w/center/e, and +1.0 for sw/s/se.\n\
+% justify -    0 for left justification, 0.5 for center, 1 for right justify.\n\
+% cellWidth -  width for this cell\n\
+% cellHeight - height for this cell\n\
+%\n\
+% Also, when this procedure is invoked, the color and font must already\n\
+% have been set for the text.\n\
+\n",
+
+       "/DrawCellText {\n\
+    /cellHeight exch def\n\
+    /cellWidth exch def\n\
+    /justify exch def\n\
+    /yoffset exch def\n\
+    /xoffset exch def\n\
+    /spacing exch def\n\
+    /strings exch def\n\
+    /y exch def\n\
+    /x exch def\n\
+\n\
+    % Compute the baseline offset and the actual font height.\n\
+\n\
+    0 0 moveto (TXygqPZ) false charpath\n\
+    pathbbox dup /baseline exch def\n\
+    exch pop exch sub /height exch def pop\n\
+    newpath\n\
+\n\
+    % Translate coordinates first so that the origin is at the upper-left\n\
+    % corner of the text's bounding box. Remember that x and y for\n\
+    % positioning are still on the stack.\n\
+\n\
+    col0 x sub row0 y sub translate\n\
+    cellWidth xoffset mul\n\
+    strings length 1 sub spacing mul height add yoffset mul translate\n\
+\n\
+    % Now use the baseline and justification information to translate so\n\
+    % that the origin is at the baseline and positioning point for the\n\
+    % first line of text.\n\
+\n\
+    justify cellWidth mul baseline neg translate\n\
+\n\
+    % Iterate over each of the lines to output it.  For each line,\n\
+    % compute its width again so it can be properly justified, then\n\
+    % display it.\n\
+\n\
+    strings {\n\
+       dup stringwidth pop\n\
+       justify neg mul 0 moveto\n\
+       show\n\
+       0 spacing neg translate\n\
+    } forall\n\
+} bind def\n\
+\n",
+
+       "%\n\
+% x, y -       Coordinates at which to draw text.\n\
+% strings -    An array of strings, one for each line of the text item,\n\
+%              in order from top to bottom.\n\
+% spacing -    Spacing between lines.\n\
+% xoffset -    Horizontal offset for text bbox relative to x and y: 0 for\n\
+%              nw/w/sw anchor, -0.5 for n/center/s, and -1.0 for ne/e/se.\n\
+% yoffset -    Vertical offset for text bbox relative to x and y: 0 for\n\
+%              nw/n/ne anchor, +0.5 for w/center/e, and +1.0 for sw/s/se.\n\
+% justify -    0 for left justification, 0.5 for center, 1 for right justify.\n\
+% cellWidth -  width for this cell\n\
+% cellHeight - height for this cell\n\
+%\n\
+% Also, when this procedure is invoked, the color and font must already\n\
+% have been set for the text.\n\
+\n\
+/DrawCellTextOld {\n\
+    /cellHeight exch def\n\
+    /cellWidth exch def\n\
+    /justify exch def\n\
+    /yoffset exch def\n\
+    /xoffset exch def\n\
+    /spacing exch def\n\
+    /strings exch def\n\
+\n\
+    % Compute the baseline offset and the actual font height.\n\
+\n\
+    0 0 moveto (TXygqPZ) false charpath\n\
+    pathbbox dup /baseline exch def\n\
+    exch pop exch sub /height exch def pop\n\
+    newpath\n\
+\n\
+    % Translate coordinates first so that the origin is at the upper-left\n\
+    % corner of the text's bounding box. Remember that x and y for\n\
+    % positioning are still on the stack.\n\
+\n\
+    translate\n\
+    cellWidth xoffset mul\n\
+    strings length 1 sub spacing mul height add yoffset mul translate\n\
+\n\
+    % Now use the baseline and justification information to translate so\n\
+    % that the origin is at the baseline and positioning point for the\n\
+    % first line of text.\n\
+\n\
+    justify cellWidth mul baseline neg translate\n\
+\n\
+    % Iterate over each of the lines to output it.  For each line,\n\
+    % compute its width again so it can be properly justified, then\n\
+    % display it.\n\
+\n\
+    strings {\n\
+       dup stringwidth pop\n\
+       justify neg mul 0 moveto\n\
+       show\n\
+       0 spacing neg translate\n\
+    } forall\n\
+} bind def\n\
+\n\
+%%EndProlog\n\
+",
+       /* End of part 5 */
+
+       NULL    /* End of data marker */
+};
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static int     GetPostscriptPoints _ANSI_ARGS_((Tcl_Interp *interp,
+                       char *string, double *doublePtr));
+int            Tk_TablePsFont _ANSI_ARGS_((Tcl_Interp *interp,
+                       Table *tablePtr, Tk_Font tkfont));
+int            Tk_TablePsColor _ANSI_ARGS_((Tcl_Interp *interp,
+                       Table *tablePtr, XColor *colorPtr));
+static int     TextToPostscript _ANSI_ARGS_((Tcl_Interp *interp,
+                       Table *tablePtr, TableTag *tagPtr, int tagX, int tagY,
+                       int width, int height, int row, int col,
+                       Tk_TextLayout textLayout));
+
+/*
+ * Tcl could really use some more convenience routines...
+ * This is just Tcl_DStringAppend for multiple lines, including
+ * the full text of each line
+ */
+void
+Tcl_DStringAppendAll TCL_VARARGS_DEF(Tcl_DString *, arg1)
+{
+    va_list argList;
+    Tcl_DString *dstringPtr;
+    char *string;
+
+    dstringPtr = TCL_VARARGS_START(Tcl_DString *, arg1, argList);
+    while ((string = va_arg(argList, char *)) != NULL) {
+      Tcl_DStringAppend(dstringPtr, string, -1);
+    }
+    va_end(argList);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_PostscriptCmd --
+ *
+ *     This procedure is invoked to process the "postscript" options
+ *     of the widget command for table widgets. See the user
+ *     documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+    /* ARGSUSED */
+int
+Table_PostscriptCmd(clientData, interp, objc, objv)
+     ClientData clientData;    /* Information about table widget. */
+     Tcl_Interp *interp;       /* Current interpreter. */
+     int objc;                 /* Number of argument objects. */
+     Tcl_Obj *CONST objv[];
+{
+#ifdef _WIN32
+    /*
+     * At the moment, it just doesn't like this code...
+     */
+    return TCL_OK;
+#else
+    register Table *tablePtr = (Table *) clientData;
+    TkPostscriptInfo psInfo, *oldInfoPtr;
+    int result;
+    int row, col, firstRow, firstCol, lastRow, lastCol;
+    /* dimensions of first and last cell to output */
+    int x0, y0, w0, h0, xn, yn, wn, hn;
+    int x, y, w, h, i;
+#define STRING_LENGTH 400
+    char string[STRING_LENGTH+1], *p, **argv;
+    size_t length;
+    int deltaX = 0, deltaY = 0;        /* Offset of lower-left corner of area to
+                                * be marked up, measured in table units
+                                * from the positioning point on the page
+                                * (reflects anchor position).  Initial
+                                * values needed only to stop compiler
+                                * warnings. */
+    Tcl_HashSearch search;
+    Tcl_HashEntry *hPtr;
+    CONST char * CONST *chunk;
+    Tk_TextLayout textLayout = NULL;
+    char *value;
+    int rowHeight, total, *colWidths, iW, iH;
+    TableTag *tagPtr, *colPtr, *rowPtr, *titlePtr;
+    Tcl_DString postscript, buffer;
+
+    if (objc < 2) {
+       Tcl_WrongNumArgs(interp, 2, objv, "?option value ...?");
+       return TCL_ERROR;
+    }
+
+    /*
+     *----------------------------------------------------------------
+     * Initialize the data structure describing Postscript generation,
+     * then process all the arguments to fill the data structure in.
+     *----------------------------------------------------------------
+     */
+
+    Tcl_DStringInit(&postscript);
+    Tcl_DStringInit(&buffer);
+    oldInfoPtr = tablePtr->psInfoPtr;
+    tablePtr->psInfoPtr = &psInfo;
+    /* This is where in the window that we start printing from */
+    psInfo.x                   = 0;
+    psInfo.y                   = 0;
+    psInfo.width               = -1;
+    psInfo.height              = -1;
+    psInfo.pageXString         = NULL;
+    psInfo.pageYString         = NULL;
+    psInfo.pageX               = 72*4.25;
+    psInfo.pageY               = 72*5.5;
+    psInfo.pageWidthString     = NULL;
+    psInfo.pageHeightString    = NULL;
+    psInfo.scale               = 1.0;
+    psInfo.pageAnchor          = TK_ANCHOR_CENTER;
+    psInfo.rotate              = 0;
+    psInfo.fontVar             = NULL;
+    psInfo.colorVar            = NULL;
+    psInfo.colorMode           = NULL;
+    psInfo.colorLevel          = 0;
+    psInfo.fileName            = NULL;
+    psInfo.channelName         = NULL;
+    psInfo.chan                        = NULL;
+    psInfo.first               = NULL;
+    psInfo.last                        = NULL;
+    Tcl_InitHashTable(&psInfo.fontTable, TCL_STRING_KEYS);
+
+    /*
+     * The magic StringifyObjects
+     */
+    argv = (char **) ckalloc((objc + 1) * sizeof(char *));
+    for (i = 0; i < objc; i++)
+       argv[i] = Tcl_GetString(objv[i]);
+    argv[i] = NULL;
+
+    result = Tk_ConfigureWidget(interp, tablePtr->tkwin, configSpecs,
+                               objc-2, argv+2, (char *) &psInfo,
+                               TK_CONFIG_ARGV_ONLY);
+    if (result != TCL_OK) {
+       goto cleanup;
+    }
+
+    if (psInfo.first == NULL) {
+       firstRow = 0;
+       firstCol = 0;
+    } else if (TableGetIndex(tablePtr, psInfo.first, &firstRow, &firstCol)
+              != TCL_OK) {
+       result = TCL_ERROR;
+       goto cleanup;
+    }
+    if (psInfo.last == NULL) {
+       lastRow = tablePtr->rows-1;
+       lastCol = tablePtr->cols-1;
+    } else if (TableGetIndex(tablePtr, psInfo.last, &lastRow, &lastCol)
+              != TCL_OK) {
+       result = TCL_ERROR;
+       goto cleanup;
+    }
+
+    if (psInfo.fileName != NULL) {
+       /* Check that -file and -channel are not both specified. */
+       if (psInfo.channelName != NULL) {
+           Tcl_AppendResult(interp, "can't specify both -file",
+                            " and -channel", (char *) NULL);
+           result = TCL_ERROR;
+           goto cleanup;
+       }
+
+       /*
+        * Check that we are not in a safe interpreter. If we are, disallow
+        * the -file specification.
+        */
+       if (Tcl_IsSafe(interp)) {
+           Tcl_AppendResult(interp, "can't specify -file in a",
+                            " safe interpreter", (char *) NULL);
+           result = TCL_ERROR;
+           goto cleanup;
+       }
+
+       p = Tcl_TranslateFileName(interp, psInfo.fileName, &buffer);
+       if (p == NULL) {
+           result = TCL_ERROR;
+           goto cleanup;
+       }
+       psInfo.chan = Tcl_OpenFileChannel(interp, p, "w", 0666);
+       Tcl_DStringFree(&buffer);
+       Tcl_DStringInit(&buffer);
+       if (psInfo.chan == NULL) {
+           result = TCL_ERROR;
+           goto cleanup;
+       }
+    }
+
+    if (psInfo.channelName != NULL) {
+       int mode;
+       /*
+        * Check that the channel is found in this interpreter and that it
+        * is open for writing.
+        */
+       psInfo.chan = Tcl_GetChannel(interp, psInfo.channelName, &mode);
+       if (psInfo.chan == (Tcl_Channel) NULL) {
+           result = TCL_ERROR;
+           goto cleanup;
+       }
+       if ((mode & TCL_WRITABLE) == 0) {
+           Tcl_AppendResult(interp, "channel \"", psInfo.channelName,
+                            "\" wasn't opened for writing", (char *) NULL);
+           result = TCL_ERROR;
+           goto cleanup;
+       }
+    }
+
+    if (psInfo.colorMode == NULL) {
+       psInfo.colorLevel = 2;
+    } else {
+       length = strlen(psInfo.colorMode);
+       if (strncmp(psInfo.colorMode, "monochrome", length) == 0) {
+           psInfo.colorLevel = 0;
+       } else if (strncmp(psInfo.colorMode, "gray", length) == 0) {
+           psInfo.colorLevel = 1;
+       } else if (strncmp(psInfo.colorMode, "color", length) == 0) {
+           psInfo.colorLevel = 2;
+       } else {
+           Tcl_AppendResult(interp, "bad color mode \"", psInfo.colorMode,
+                            "\": must be monochrome, gray or color", (char *) NULL);
+           goto cleanup;
+       }
+    }
+
+    TableCellCoords(tablePtr, firstRow, firstCol, &x0, &y0, &w0, &h0);
+    TableCellCoords(tablePtr, lastRow, lastCol, &xn, &yn, &wn, &hn);
+    psInfo.x = x0;
+    psInfo.y = y0;
+    if (psInfo.width == -1) {
+       psInfo.width = xn+wn;
+    }
+    if (psInfo.height == -1) {
+       psInfo.height = yn+hn;
+    }
+    psInfo.x2 = psInfo.x + psInfo.width;
+    psInfo.y2 = psInfo.y + psInfo.height;
+
+    if (psInfo.pageXString != NULL) {
+       if (GetPostscriptPoints(interp, psInfo.pageXString,
+                               &psInfo.pageX) != TCL_OK) {
+           goto cleanup;
+       }
+    }
+    if (psInfo.pageYString != NULL) {
+       if (GetPostscriptPoints(interp, psInfo.pageYString,
+                               &psInfo.pageY) != TCL_OK) {
+           goto cleanup;
+       }
+    }
+    if (psInfo.pageWidthString != NULL) {
+       if (GetPostscriptPoints(interp, psInfo.pageWidthString,
+                               &psInfo.scale) != TCL_OK) {
+           goto cleanup;
+       }
+       psInfo.scale /= psInfo.width;
+    } else if (psInfo.pageHeightString != NULL) {
+       if (GetPostscriptPoints(interp, psInfo.pageHeightString,
+                               &psInfo.scale) != TCL_OK) {
+           goto cleanup;
+       }
+       psInfo.scale /= psInfo.height;
+    } else {
+       psInfo.scale = (72.0/25.4)*WidthMMOfScreen(Tk_Screen(tablePtr->tkwin))
+           / WidthOfScreen(Tk_Screen(tablePtr->tkwin));
+    }
+    switch (psInfo.pageAnchor) {
+    case TK_ANCHOR_NW:
+    case TK_ANCHOR_W:
+    case TK_ANCHOR_SW:
+       deltaX = 0;
+       break;
+    case TK_ANCHOR_N:
+    case TK_ANCHOR_CENTER:
+    case TK_ANCHOR_S:
+       deltaX = -psInfo.width/2;
+       break;
+    case TK_ANCHOR_NE:
+    case TK_ANCHOR_E:
+    case TK_ANCHOR_SE:
+       deltaX = -psInfo.width;
+       break;
+    }
+    switch (psInfo.pageAnchor) {
+    case TK_ANCHOR_NW:
+    case TK_ANCHOR_N:
+    case TK_ANCHOR_NE:
+       deltaY = - psInfo.height;
+       break;
+    case TK_ANCHOR_W:
+    case TK_ANCHOR_CENTER:
+    case TK_ANCHOR_E:
+       deltaY = -psInfo.height/2;
+       break;
+    case TK_ANCHOR_SW:
+    case TK_ANCHOR_S:
+    case TK_ANCHOR_SE:
+       deltaY = 0;
+       break;
+    }
+
+    /*
+     *--------------------------------------------------------
+     * Make a PREPASS over all of the tags
+     * to collect information about all the fonts in use, so that
+     * we can output font information in the proper form required
+     * by the Document Structuring Conventions.
+     *--------------------------------------------------------
+     */
+
+    Tk_TablePsFont(interp, tablePtr, tablePtr->defaultTag.tkfont);
+    Tcl_ResetResult(interp);
+    for (hPtr = Tcl_FirstHashEntry(tablePtr->tagTable, &search);
+        hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+       tagPtr = (TableTag *) Tcl_GetHashValue(hPtr);
+       if (tagPtr->tkfont != NULL) {
+           Tk_TablePsFont(interp, tablePtr, tagPtr->tkfont);
+       }
+    }
+    Tcl_ResetResult(interp);
+
+    /*
+     *--------------------------------------------------------
+     * Generate the header and prolog for the Postscript.
+     *--------------------------------------------------------
+     */
+
+    sprintf(string, " %d,%d => %d,%d\n", firstRow, firstCol, lastRow, lastCol);
+    Tcl_DStringAppendAll(&postscript,
+                        "%!PS-Adobe-3.0 EPSF-3.0\n",
+                        "%%Creator: Tk Table Widget ", TBL_VERSION, "\n",
+                        "%%Title: Window ",
+                        Tk_PathName(tablePtr->tkwin), string,
+                        "%%BoundingBox: ",
+                        (char *) NULL);
+    if (!psInfo.rotate) {
+       sprintf(string, "%d %d %d %d\n",
+               (int) (psInfo.pageX + psInfo.scale*deltaX),
+               (int) (psInfo.pageY + psInfo.scale*deltaY),
+               (int) (psInfo.pageX + psInfo.scale*(deltaX + psInfo.width)
+                      + 1.0),
+               (int) (psInfo.pageY + psInfo.scale*(deltaY + psInfo.height)
+                      + 1.0));
+    } else {
+       sprintf(string, "%d %d %d %d\n",
+               (int) (psInfo.pageX - psInfo.scale*(deltaY + psInfo.height)),
+               (int) (psInfo.pageY + psInfo.scale*deltaX),
+               (int) (psInfo.pageX - psInfo.scale*deltaY + 1.0),
+               (int) (psInfo.pageY + psInfo.scale*(deltaX + psInfo.width)
+                      + 1.0));
+    }
+    Tcl_DStringAppendAll(&postscript, string,
+                        "%%Pages: 1\n%%DocumentData: Clean7Bit\n",
+                        "%%Orientation: ",
+                        psInfo.rotate?"Landscape\n":"Portrait\n",
+                        (char *) NULL);
+    p = "%%DocumentNeededResources: font ";
+    for (hPtr = Tcl_FirstHashEntry(&psInfo.fontTable, &search);
+        hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+       sprintf(string, "%s%s\n", p, Tcl_GetHashKey(&psInfo.fontTable, hPtr));
+       Tcl_DStringAppend(&postscript, string, -1);
+       p = "%%+ font ";
+    }
+    Tcl_DStringAppend(&postscript, "%%EndComments\n\n", -1);
+
+    /*
+     * Insert the prolog
+     */
+    for (chunk=prolog; *chunk; chunk++) {
+       Tcl_DStringAppend(&postscript, *chunk, -1);
+    }
+
+    if (psInfo.chan != NULL) {
+       Tcl_Write(psInfo.chan, Tcl_DStringValue(&postscript), -1);
+       Tcl_DStringFree(&postscript);
+       Tcl_DStringInit(&postscript);
+    }
+
+    /*
+     * Document setup:  set the color level and include fonts.
+     * This is where we start using &postscript
+     */
+
+    sprintf(string, "/CL %d def\n", psInfo.colorLevel);
+    Tcl_DStringAppendAll(&postscript, "%%BeginSetup\n", string, (char *) NULL);
+    for (hPtr = Tcl_FirstHashEntry(&psInfo.fontTable, &search);
+        hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+       sprintf(string, "%s%s\n", "%%IncludeResource: font ",
+               Tcl_GetHashKey(&psInfo.fontTable, hPtr));
+       Tcl_DStringAppend(&postscript, string, -1);
+    }
+    Tcl_DStringAppend(&postscript, "%%EndSetup\n\n", -1);
+
+    /*
+     * Page setup:  move to page positioning point, rotate if
+     * needed, set scale factor, offset for proper anchor position,
+     * and set clip region.
+     */
+
+    sprintf(string, "%.1f %.1f translate\n",
+           psInfo.pageX, psInfo.pageY);
+    Tcl_DStringAppendAll(&postscript, "%%Page: 1 1\nsave\n",
+                        string, psInfo.rotate?"90 rotate\n":"",
+                        (char *) NULL);
+    sprintf(string, "%.4g %.4g scale\n%d %d translate\n",
+           psInfo.scale, psInfo.scale, deltaX - psInfo.x, deltaY);
+    Tcl_DStringAppend(&postscript, string, -1);
+    sprintf(string, "%d %.15g moveto %d %.15g lineto %d %.15g lineto %d %.15g",
+           psInfo.x, (double) psInfo.y2-psInfo.y,
+           psInfo.x2,(double) psInfo.y2-psInfo.y,
+           psInfo.x2, 0.0, psInfo.x, 0.0);
+    Tcl_DStringAppend(&postscript, string, -1);
+    Tcl_DStringAppend(&postscript, " lineto closepath clip newpath\n", -1);
+    if (psInfo.chan != NULL) {
+       Tcl_Write(psInfo.chan, Tcl_DStringValue(&postscript), -1);
+       Tcl_DStringFree(&postscript);
+       Tcl_DStringInit(&postscript);
+    }
+
+    /*
+     * Go through each cell, calculating full desired height
+     */
+    result = TCL_OK;
+
+    hPtr = Tcl_FindHashEntry(tablePtr->tagTable, "title");
+    titlePtr = (TableTag *) Tcl_GetHashValue(hPtr);
+
+    total = 0;
+    colWidths = (int *) ckalloc((lastCol-firstCol) * sizeof(int));
+    for (col = 0; col <= lastCol-firstCol; col++) colWidths[col] = 0;
+    Tcl_DStringAppend(&buffer, "gsave\n", -1);
+    for (row = firstRow; row <= lastRow; row++) {
+       rowHeight = 0;
+       rowPtr = FindRowColTag(tablePtr, row+tablePtr->rowOffset, ROW);
+       for (col = firstCol; col <= lastCol; col++) {
+           /* get the coordinates for the cell */
+           TableCellCoords(tablePtr, row, col, &x, &y, &w, &h);
+           if ((x >= psInfo.x2) || (x+w < psInfo.x) ||
+               (y >= psInfo.y2) || (y+h < psInfo.y)) {
+               continue;
+           }
+
+           if (row == tablePtr->activeRow && col == tablePtr->activeCol) {
+               value = tablePtr->activeBuf;
+           } else {
+               value = TableGetCellValue(tablePtr, row+tablePtr->rowOffset,
+                                         col+tablePtr->colOffset);
+           }
+           if (!strlen(value)) {
+               continue;
+           }
+
+           /* Create the tag here */
+           tagPtr = TableNewTag();
+           /* First, merge in the default tag */
+           TableMergeTag(tagPtr, &(tablePtr->defaultTag));
+
+           colPtr = FindRowColTag(tablePtr, col+tablePtr->colOffset, COL);
+           if (colPtr != (TableTag *) NULL) TableMergeTag(tagPtr, colPtr);
+           if (rowPtr != (TableTag *) NULL) TableMergeTag(tagPtr, rowPtr);
+           /* Am I in the titles */
+           if (row < tablePtr->topRow || col < tablePtr->leftCol) {
+               TableMergeTag(tagPtr, titlePtr);
+           }
+           /* Does this have a cell tag */
+           TableMakeArrayIndex(row+tablePtr->rowOffset,
+                               col+tablePtr->colOffset, string);
+           hPtr = Tcl_FindHashEntry(tablePtr->cellStyles, string);
+           if (hPtr != NULL) {
+               TableMergeTag(tagPtr, (TableTag *) Tcl_GetHashValue(hPtr));
+           }
+
+           /*
+            * the use of -1 instead of Tcl_NumUtfChars means we don't
+            * pass NULLs to postscript
+            */
+           textLayout = Tk_ComputeTextLayout(tagPtr->tkfont, value, -1,
+                                             (tagPtr->wrap>0) ? w : 0,
+                                             tagPtr->justify,
+                                             (tagPtr->multiline>0) ? 0 :
+                                             TK_IGNORE_NEWLINES, &iW, &iH);
+
+           rowHeight = MAX(rowHeight, iH);
+           colWidths[col-firstCol] = MAX(colWidths[col-firstCol], iW);
+
+           result = TextToPostscript(interp, tablePtr, tagPtr,
+                                     x, y, iW, iH, row, col, textLayout);
+           Tk_FreeTextLayout(textLayout);
+           if (result != TCL_OK) {
+               char msg[64 + TCL_INTEGER_SPACE];
+
+               sprintf(msg, "\n    (generating Postscript for cell %s)",
+                       string);
+               Tcl_AddErrorInfo(interp, msg);
+               goto cleanup;
+           }
+           Tcl_DStringAppend(&buffer, Tcl_GetStringResult(interp), -1);
+       }
+       sprintf(string, "/row%d %d def\n",
+               row, tablePtr->psInfoPtr->y2 - total);
+       Tcl_DStringAppend(&postscript, string, -1);
+       total += rowHeight + 2*tablePtr->defaultTag.bd;
+    }
+    Tcl_DStringAppend(&buffer, "grestore\n", -1);
+    sprintf(string, "/row%d %d def\n", row, tablePtr->psInfoPtr->y2 - total);
+    Tcl_DStringAppend(&postscript, string, -1);
+
+    total = tablePtr->defaultTag.bd;
+    for (col = firstCol; col <= lastCol; col++) {
+       sprintf(string, "/col%d %d def\n", col, total);
+       Tcl_DStringAppend(&postscript, string, -1);
+       total += colWidths[col-firstCol] + 2*tablePtr->defaultTag.bd;
+    }
+    sprintf(string, "/col%d %d def\n", col, total);
+    Tcl_DStringAppend(&postscript, string, -1);
+
+    Tcl_DStringAppend(&postscript, Tcl_DStringValue(&buffer), -1);
+
+    /*
+     * Output to channel at the end of it all
+     * This should more incremental, but that can't be avoided in order
+     * to post-define width/height of the cols/rows
+     */
+    if (psInfo.chan != NULL) {
+       Tcl_Write(psInfo.chan, Tcl_DStringValue(&postscript), -1);
+       Tcl_DStringFree(&postscript);
+       Tcl_DStringInit(&postscript);
+    }
+
+    /*
+     *---------------------------------------------------------------------
+     * Output page-end information, such as commands to print the page
+     * and document trailer stuff.
+     *---------------------------------------------------------------------
+     */
+
+    Tcl_DStringAppend(&postscript,
+                     "restore showpage\n\n%%Trailer\nend\n%%EOF\n", -1);
+    if (psInfo.chan != NULL) {
+       Tcl_Write(psInfo.chan, Tcl_DStringValue(&postscript), -1);
+       Tcl_DStringFree(&postscript);
+       Tcl_DStringInit(&postscript);
+    }
+
+    /*
+   * Clean up psInfo to release malloc'ed stuff.
+   */
+
+cleanup:
+    ckfree((char *) argv);
+    Tcl_DStringResult(interp, &postscript);
+    Tcl_DStringFree(&postscript);
+    Tcl_DStringFree(&buffer);
+    if (psInfo.first != NULL) {
+       ckfree(psInfo.first);
+    }
+    if (psInfo.last != NULL) {
+       ckfree(psInfo.last);
+    }
+    if (psInfo.pageXString != NULL) {
+       ckfree(psInfo.pageXString);
+    }
+    if (psInfo.pageYString != NULL) {
+       ckfree(psInfo.pageYString);
+    }
+    if (psInfo.pageWidthString != NULL) {
+       ckfree(psInfo.pageWidthString);
+    }
+    if (psInfo.pageHeightString != NULL) {
+       ckfree(psInfo.pageHeightString);
+    }
+    if (psInfo.fontVar != NULL) {
+       ckfree(psInfo.fontVar);
+    }
+    if (psInfo.colorVar != NULL) {
+       ckfree(psInfo.colorVar);
+    }
+    if (psInfo.colorMode != NULL) {
+       ckfree(psInfo.colorMode);
+    }
+    if (psInfo.fileName != NULL) {
+       ckfree(psInfo.fileName);
+    }
+    if ((psInfo.chan != NULL) && (psInfo.channelName == NULL)) {
+       Tcl_Close(interp, psInfo.chan);
+    }
+    if (psInfo.channelName != NULL) {
+       ckfree(psInfo.channelName);
+    }
+    Tcl_DeleteHashTable(&psInfo.fontTable);
+    tablePtr->psInfoPtr = oldInfoPtr;
+    return result;
+#endif
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_TablePsColor --
+ *
+ *     This procedure is called by individual table items when
+ *     they want to set a color value for output.  Given information
+ *     about an X color, this procedure will generate Postscript
+ *     commands to set up an appropriate color in Postscript.
+ *
+ * Results:
+ *     Returns a standard Tcl return value.  If an error occurs
+ *     then an error message will be left in the interp's result.
+ *     If no error occurs, then additional Postscript will be
+ *     appended to the interp's result.
+ *
+ * Side effects:
+ *     None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Tk_TablePsColor(interp, tablePtr, colorPtr)
+     Tcl_Interp *interp;               /* Interpreter for returning Postscript
+                                        * or error message. */
+     Table *tablePtr;                  /* Information about table. */
+     XColor *colorPtr;                 /* Information about color. */
+{
+    TkPostscriptInfo *psInfoPtr = tablePtr->psInfoPtr;
+    int tmp;
+    double red, green, blue;
+    char string[200];
+
+    /*
+     * If there is a color map defined, then look up the color's name
+     * in the map and use the Postscript commands found there, if there
+     * are any.
+     */
+
+    if (psInfoPtr->colorVar != NULL) {
+       char *cmdString;
+
+       cmdString = Tcl_GetVar2(interp, psInfoPtr->colorVar,
+                               Tk_NameOfColor(colorPtr), 0);
+       if (cmdString != NULL) {
+           Tcl_AppendResult(interp, cmdString, "\n", (char *) NULL);
+           return TCL_OK;
+       }
+    }
+
+    /*
+     * No color map entry for this color.  Grab the color's intensities
+     * and output Postscript commands for them.  Special note:  X uses
+     * a range of 0-65535 for intensities, but most displays only use
+     * a range of 0-255, which maps to (0, 256, 512, ... 65280) in the
+     * X scale.  This means that there's no way to get perfect white,
+     * since the highest intensity is only 65280 out of 65535.  To
+     * work around this problem, rescale the X intensity to a 0-255
+     * scale and use that as the basis for the Postscript colors.  This
+     * scheme still won't work if the display only uses 4 bits per color,
+     * but most diplays use at least 8 bits.
+     */
+
+    tmp = colorPtr->red;
+    red = ((double) (tmp >> 8))/255.0;
+    tmp = colorPtr->green;
+    green = ((double) (tmp >> 8))/255.0;
+    tmp = colorPtr->blue;
+    blue = ((double) (tmp >> 8))/255.0;
+    sprintf(string, "%.3f %.3f %.3f AdjustColor\n",
+           red, green, blue);
+    Tcl_AppendResult(interp, string, (char *) NULL);
+    return TCL_OK;
+}
+\f
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_TablePsFont --
+ *
+ *     This procedure is called by individual table items when
+ *     they want to output text.  Given information about an X
+ *     font, this procedure will generate Postscript commands
+ *     to set up an appropriate font in Postscript.
+ *
+ * Results:
+ *     Returns a standard Tcl return value.  If an error occurs
+ *     then an error message will be left in the interp's result.
+ *     If no error occurs, then additional Postscript will be
+ *     appended to the interp's result.
+ *
+ * Side effects:
+ *     The Postscript font name is entered into psInfoPtr->fontTable
+ *     if it wasn't already there.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+Tk_TablePsFont(interp, tablePtr, tkfont)
+     Tcl_Interp *interp;               /* Interpreter for returning Postscript
+                                        * or error message. */
+     Table *tablePtr;                  /* Information about table. */
+     Tk_Font tkfont;                   /* Information about font in which text
+                                        * is to be printed. */
+{
+    TkPostscriptInfo *psInfoPtr = tablePtr->psInfoPtr;
+    char *end;
+    char pointString[TCL_INTEGER_SPACE];
+    Tcl_DString ds;
+    int i, points;
+
+    /*
+     * First, look up the font's name in the font map, if there is one.
+     * If there is an entry for this font, it consists of a list
+     * containing font name and size.  Use this information.
+     */
+
+    Tcl_DStringInit(&ds);
+    
+    if (psInfoPtr->fontVar != NULL) {
+       char *list, **argv;
+       int objc;
+       double size;
+       char *name;
+
+       name = Tk_NameOfFont(tkfont);
+       list = Tcl_GetVar2(interp, psInfoPtr->fontVar, name, 0);
+       if (list != NULL) {
+           if (Tcl_SplitList(interp, list, &objc, &argv) != TCL_OK) {
+           badMapEntry:
+               Tcl_ResetResult(interp);
+               Tcl_AppendResult(interp, "bad font map entry for \"", name,
+                                "\": \"", list, "\"", (char *) NULL);
+               return TCL_ERROR;
+           }
+           if (objc != 2) {
+               goto badMapEntry;
+           }
+           size = strtod(argv[1], &end);
+           if ((size <= 0) || (*end != 0)) {
+               goto badMapEntry;
+           }
+
+           Tcl_DStringAppend(&ds, argv[0], -1);
+           points = (int) size;
+           
+           ckfree((char *) argv);
+           goto findfont;
+       }
+    } 
+
+    points = Tk_PostscriptFontName(tkfont, &ds);
+
+findfont:
+    sprintf(pointString, "%d", points);
+    Tcl_AppendResult(interp, pointString, " /", Tcl_DStringValue(&ds),
+                    " SetFont\n", (char *) NULL);
+    Tcl_CreateHashEntry(&psInfoPtr->fontTable, Tcl_DStringValue(&ds), &i);
+    Tcl_DStringFree(&ds);
+
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * GetPostscriptPoints --
+ *
+ *     Given a string, returns the number of Postscript points
+ *     corresponding to that string.
+ *
+ * Results:
+ *     The return value is a standard Tcl return result.  If
+ *     TCL_OK is returned, then everything went well and the
+ *     screen distance is stored at *doublePtr;  otherwise
+ *     TCL_ERROR is returned and an error message is left in
+ *     the interp's result.
+ *
+ * Side effects:
+ *     None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+GetPostscriptPoints(interp, string, doublePtr)
+     Tcl_Interp *interp;               /* Use this for error reporting. */
+     char *string;             /* String describing a screen distance. */
+     double *doublePtr;                /* Place to store converted result. */
+{
+    char *end;
+    double d;
+
+    d = strtod(string, &end);
+    if (end == string) {
+    error:
+       Tcl_AppendResult(interp, "bad distance \"", string,
+                        "\"", (char *) NULL);
+       return TCL_ERROR;
+    }
+#define UCHAR(c) ((unsigned char) (c))
+    while ((*end != '\0') && isspace(UCHAR(*end))) {
+       end++;
+    }
+    switch (*end) {
+    case 'c':
+       d *= 72.0/2.54;
+       end++;
+       break;
+    case 'i':
+       d *= 72.0;
+       end++;
+       break;
+    case 'm':
+       d *= 72.0/25.4;
+       end++;
+       break;
+    case 0:
+       break;
+    case 'p':
+       end++;
+       break;
+    default:
+       goto error;
+    }
+    while ((*end != '\0') && isspace(UCHAR(*end))) {
+       end++;
+    }
+    if (*end != 0) {
+       goto error;
+    }
+    *doublePtr = d;
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TextToPostscript --
+ *
+ *     This procedure is called to generate Postscript for
+ *     text items.
+ *
+ * Results:
+ *     The return value is a standard Tcl result.  If an error
+ *     occurs in generating Postscript then an error message is
+ *     left in the interp's result, replacing whatever used
+ *     to be there.  If no error occurs, then Postscript for the
+ *     item is appended to the result.
+ *
+ * Side effects:
+ *     None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+TextToPostscript(interp, tablePtr, tagPtr, tagX, tagY, width, height,
+                row, col, textLayout)
+     Tcl_Interp *interp;       /* Leave Postscript or error message here. */
+     Table *tablePtr;          /* Information about overall canvas. */
+     TableTag *tagPtr;         /*  */
+     int tagX, tagY;           /*  */
+     int width, height;                /*  */
+     int row, col;             /*  */
+     Tk_TextLayout textLayout; /*  */
+{
+    int x, y;
+    Tk_FontMetrics fm;
+    char *justify;
+    char buffer[500];
+    Tk_3DBorder fg = tagPtr->fg;
+
+    if (fg == NULL) {
+       fg = tablePtr->defaultTag.fg;
+    }
+
+    if (Tk_TablePsFont(interp, tablePtr, tagPtr->tkfont) != TCL_OK) {
+       return TCL_ERROR;
+    }
+    if (Tk_TablePsColor(interp, tablePtr, Tk_3DBorderColor(fg)) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    sprintf(buffer, "%% %.15g %.15g [\n", (tagX+width)/2.0,
+           tablePtr->psInfoPtr->y2 - ((tagY+height)/2.0));
+    Tcl_AppendResult(interp, buffer, (char *) NULL);
+    sprintf(buffer, "col%d row%d [\n", col, row);
+    Tcl_AppendResult(interp, buffer, (char *) NULL);
+
+    Tk_TextLayoutToPostscript(interp, textLayout);
+
+    x = 0;  y = 0;  justify = NULL;    /* lint. */
+    switch (tagPtr->anchor) {
+    case TK_ANCHOR_NW:         x = 0; y = 0;   break;
+    case TK_ANCHOR_N:          x = 1; y = 0;   break;
+    case TK_ANCHOR_NE:         x = 2; y = 0;   break;
+    case TK_ANCHOR_E:          x = 2; y = 1;   break;
+    case TK_ANCHOR_SE:         x = 2; y = 2;   break;
+    case TK_ANCHOR_S:          x = 1; y = 2;   break;
+    case TK_ANCHOR_SW:         x = 0; y = 2;   break;
+    case TK_ANCHOR_W:          x = 0; y = 1;   break;
+    case TK_ANCHOR_CENTER:     x = 1; y = 1;   break;
+    }
+    switch (tagPtr->justify) {
+    case TK_JUSTIFY_RIGHT:     justify = "1";  break;
+    case TK_JUSTIFY_CENTER:    justify = "0.5";break;
+    case TK_JUSTIFY_LEFT:      justify = "0";
+    }
+
+    Tk_GetFontMetrics(tagPtr->tkfont, &fm);
+    sprintf(buffer, "] %d %g %g %s %d %d DrawCellText\n",
+           fm.linespace, (x / -2.0), (y / 2.0), justify,
+           width, height);
+    Tcl_AppendResult(interp, buffer, (char *) NULL);
+
+    return TCL_OK;
+}
index 4ba5413..ad43fad 100644 (file)
@@ -3,54 +3,44 @@
  *
  *     This module implements tags for table widgets.
  *
- * Copyright (c) 1998 Jeffrey Hobbs
+ * Copyright (c) 1998-2001 Jeffrey Hobbs
  *
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  *
+ * RCS: @(#) $Id$
  */
 
 #include "tkTable.h"
 
-static void    CreateTagEntry _ANSI_ARGS_((Table *tablePtr, char *name,
-                                           int argc, char **argv));
+static TableTag *TableTagGetEntry _ANSI_ARGS_((Table *tablePtr, char *name,
+       int objc, char **argv));
+static unsigned int    TableTagGetPriority _ANSI_ARGS_((Table *tablePtr,
+       TableTag *tagPtr));
 static void    TableImageProc _ANSI_ARGS_((ClientData clientData, int x,
-                                           int y, int width, int height,
-                                           int imageWidth, int imageHeight));
-
-/* tag subcommands */
-#define TAG_CELLTAG    1       /* tag a cell */
-#define TAG_CGET       2       /* get a config value */
-#define TAG_COLTAG     3       /* tag a column */
-#define        TAG_CONFIGURE   4       /* config/create a new tag */
-#define        TAG_DELETE      5       /* delete a tag */
-#define TAG_EXISTS     6       /* does a tag exist? */
-#define        TAG_NAMES       7       /* print the tag names */
-#define TAG_ROWTAG     8       /* tag a row */
-#define TAG_INCLUDES   9       /* does an index have a particular tag */
-
-static Cmd_Struct tag_cmds[] = {
-  {"celltag",  TAG_CELLTAG},
-  {"coltag",   TAG_COLTAG},
-  {"configure",        TAG_CONFIGURE},
-  {"cget",     TAG_CGET},
-  {"delete",   TAG_DELETE},
-  {"exists",   TAG_EXISTS},
-  {"names",    TAG_NAMES},
-  {"rowtag",   TAG_ROWTAG},
-  {"includes", TAG_INCLUDES},
-  {"", 0}
+       int y, int width, int height, int imageWidth, int imageHeight));
+
+static char *tagCmdNames[] = {
+    "celltag", "cget", "coltag", "configure", "delete", "exists",
+    "includes", "lower", "names", "raise", "rowtag", (char *) NULL
+};
+
+enum tagCmd {
+    TAG_CELLTAG, TAG_CGET, TAG_COLTAG, TAG_CONFIGURE, TAG_DELETE, TAG_EXISTS,
+    TAG_INCLUDES, TAG_LOWER, TAG_NAMES, TAG_RAISE, TAG_ROWTAG
 };
 
 static Cmd_Struct tagState_vals[]= {
-  {"unknown",   STATE_UNKNOWN},
-  {"normal",    STATE_NORMAL},
-  {"disabled",  STATE_DISABLED},
-  {"",          0 }
+    {"unknown",         STATE_UNKNOWN},
+    {"normal",  STATE_NORMAL},
+    {"disabled", STATE_DISABLED},
+    {"",        0 }
 };
 
-static Tk_CustomOption tagStateOpt = { Cmd_OptionSet, Cmd_OptionGet,
-                                      (ClientData)(&tagState_vals) };
+static Tk_CustomOption tagStateOpt     = { Cmd_OptionSet, Cmd_OptionGet,
+                                           (ClientData)(&tagState_vals) };
+static Tk_CustomOption tagBdOpt                = { TableOptionBdSet, TableOptionBdGet,
+                                           (ClientData) BD_TABLE_TAG };
 
 /*
  * The default specification for configuring tags
@@ -59,40 +49,48 @@ static Tk_CustomOption tagStateOpt = { Cmd_OptionSet, Cmd_OptionGet,
 
 static Tk_ConfigSpec tagConfig[] = {
   {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", "center",
-   Tk_Offset(TableTag, anchor), TK_CONFIG_DONT_SET_DEFAULT },
+   Tk_Offset(TableTag, anchor), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
   {TK_CONFIG_BORDER, "-background", "background", "Background", NULL,
-   Tk_Offset(TableTag, bg),
-   TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
-  {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
-   (char *) NULL, 0, 0 },
+   Tk_Offset(TableTag, bg), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
+  {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+  {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+  {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth", "",
+   0 /* no offset */,
+   TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK, &tagBdOpt },
   {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground", NULL,
-   Tk_Offset(TableTag, fg),
-   TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
-  {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
-   (char *) NULL, 0, 0 },
+   Tk_Offset(TableTag, fg), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
+  {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
   {TK_CONFIG_FONT, "-font", "font", "Font", NULL,
-   Tk_Offset(TableTag, tkfont),
-   TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
+   Tk_Offset(TableTag, tkfont), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
   {TK_CONFIG_STRING, "-image", "image", "Image", NULL,
    Tk_Offset(TableTag, imageStr),
    TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
   {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify", "left",
-   Tk_Offset(TableTag, justify), TK_CONFIG_DONT_SET_DEFAULT },
-  {TK_CONFIG_INT, "-multiline", "multiline", "Multiline", "1",
+   Tk_Offset(TableTag, justify), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
+  {TK_CONFIG_INT, "-multiline", "multiline", "Multiline", "-1",
    Tk_Offset(TableTag, multiline), TK_CONFIG_DONT_SET_DEFAULT },
   {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", "flat",
-   Tk_Offset(TableTag, relief),
-   TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
-  {TK_CONFIG_INT, "-showtext", "showText", "ShowText", "0",
+   Tk_Offset(TableTag, relief), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
+  {TK_CONFIG_INT, "-showtext", "showText", "ShowText", "-1",
    Tk_Offset(TableTag, showtext), TK_CONFIG_DONT_SET_DEFAULT },
   {TK_CONFIG_CUSTOM, "-state", "state", "State", "unknown",
    Tk_Offset(TableTag, state), TK_CONFIG_DONT_SET_DEFAULT, &tagStateOpt },
-  {TK_CONFIG_INT, "-wrap", "wrap", "Wrap", "0",
+  {TK_CONFIG_INT, "-wrap", "wrap", "Wrap", "-1",
    Tk_Offset(TableTag, wrap), TK_CONFIG_DONT_SET_DEFAULT },
-  {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
-   (char *) NULL, 0, 0 }
+  {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL, 0, 0}
 };
 
+/*
+ * The join tag structure is used to create a combined tag, so it
+ * keeps priority info.
+ */
+typedef struct {
+    TableTag   tag;            /* must be first */
+    unsigned int magic;
+    unsigned int pbg, pfg, pborders, prelief, ptkfont, panchor, pimage;
+    unsigned int pstate, pjustify, pmultiline, pwrap, pshowtext;
+} TableJoinTag;
+\f
 /* 
  *----------------------------------------------------------------------
  *
@@ -104,7 +102,8 @@ static Tk_ConfigSpec tagConfig[] = {
  *
  * Side effects:
  *     Invalidates the whole table.
- *     FIX - should only invalidate affected cells.
+ *     This should only invalidate affected cells, but that info
+ *     is not managed...
  *
  *----------------------------------------------------------------------
  */
@@ -112,9 +111,9 @@ static void
 TableImageProc(ClientData clientData, int x, int y, int width, int height,
               int imageWidth, int imageHeight)
 {
-  TableInvalidateAll((Table *)clientData, 0);
+    TableInvalidateAll((Table *)clientData, 0);
 }
-
+\f
 /*
  *----------------------------------------------------------------------
  *
@@ -130,24 +129,110 @@ TableImageProc(ClientData clientData, int x, int y, int width, int height,
  *----------------------------------------------------------------------
  */
 TableTag *
-TableNewTag(void)
+TableNewTag(Table *tablePtr)
 {
-  TableTag *tagPtr = (TableTag *) ckalloc(sizeof(TableTag));
-  tagPtr->anchor       = (Tk_Anchor)-1;
-  tagPtr->bg           = NULL;
-  tagPtr->fg           = NULL;
-  tagPtr->tkfont       = NULL;
-  tagPtr->image                = NULL;
-  tagPtr->imageStr     = NULL;
-  tagPtr->justify      = (Tk_Justify)-1;
-  tagPtr->multiline    = -1;
-  tagPtr->relief       = -1;
-  tagPtr->showtext     = -1;
-  tagPtr->state                = STATE_UNKNOWN;
-  tagPtr->wrap         = -1;
-  return tagPtr;
+    TableTag *tagPtr;
+
+    /*
+     * If tablePtr is NULL, make a regular tag, otherwise make a join tag.
+     */
+    if (tablePtr == NULL) {
+       tagPtr = (TableTag *) ckalloc(sizeof(TableTag));
+       memset((VOID *) tagPtr, 0, sizeof(TableTag));
+
+       /*
+        * Set the values that aren't 0/NULL by default
+        */
+       tagPtr->anchor          = (Tk_Anchor)-1;
+       tagPtr->justify         = (Tk_Justify)-1;
+       tagPtr->multiline       = -1;
+       tagPtr->relief          = -1;
+       tagPtr->showtext        = -1;
+       tagPtr->state           = STATE_UNKNOWN;
+       tagPtr->wrap            = -1;
+    } else {
+       TableJoinTag *jtagPtr = (TableJoinTag *) ckalloc(sizeof(TableJoinTag));
+       memset((VOID *) jtagPtr, 0, sizeof(TableJoinTag));
+       tagPtr = (TableTag *) jtagPtr;
+
+       tagPtr->anchor          = (Tk_Anchor)-1;
+       tagPtr->justify         = (Tk_Justify)-1;
+       tagPtr->multiline       = -1;
+       tagPtr->relief          = -1;
+       tagPtr->showtext        = -1;
+       tagPtr->state           = STATE_UNKNOWN;
+       tagPtr->wrap            = -1;
+       jtagPtr->magic          = 0x99ABCDEF;
+       jtagPtr->pbg            = -1;
+       jtagPtr->pfg            = -1;
+       jtagPtr->pborders       = -1;
+       jtagPtr->prelief        = -1;
+       jtagPtr->ptkfont        = -1;
+       jtagPtr->panchor        = -1;
+       jtagPtr->pimage         = -1;
+       jtagPtr->pstate         = -1;
+       jtagPtr->pjustify       = -1;
+       jtagPtr->pmultiline     = -1;
+       jtagPtr->pwrap          = -1;
+       jtagPtr->pshowtext      = -1;
+    }
+
+    return (TableTag *) tagPtr;
 }
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableResetTag --
+ *     This routine resets a given tag to the table defaults.
+ *
+ * Results:
+ *     Tag will have values changed.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+TableResetTag(Table *tablePtr, TableTag *tagPtr)
+{
+    TableJoinTag *jtagPtr = (TableJoinTag *) tagPtr;
+
+    if (jtagPtr->magic != 0x99ABCDEF) {
+       panic("bad mojo in TableResetTag");
+    }
 
+    memset((VOID *) jtagPtr, 0, sizeof(TableJoinTag));
+
+    tagPtr->anchor     = (Tk_Anchor)-1;
+    tagPtr->justify    = (Tk_Justify)-1;
+    tagPtr->multiline  = -1;
+    tagPtr->relief     = -1;
+    tagPtr->showtext   = -1;
+    tagPtr->state      = STATE_UNKNOWN;
+    tagPtr->wrap       = -1;
+    jtagPtr->magic     = 0x99ABCDEF;
+    jtagPtr->pbg       = -1;
+    jtagPtr->pfg       = -1;
+    jtagPtr->pborders  = -1;
+    jtagPtr->prelief   = -1;
+    jtagPtr->ptkfont   = -1;
+    jtagPtr->panchor   = -1;
+    jtagPtr->pimage    = -1;
+    jtagPtr->pstate    = -1;
+    jtagPtr->pjustify  = -1;
+    jtagPtr->pmultiline        = -1;
+    jtagPtr->pwrap     = -1;
+    jtagPtr->pshowtext = -1;
+
+    /*
+     * Merge in the default tag.
+     */
+    memcpy((VOID *) jtagPtr, (VOID *) &(tablePtr->defaultTag),
+           sizeof(TableTag));
+}
+\f
 /*
  *----------------------------------------------------------------------
  *
@@ -165,24 +250,101 @@ TableNewTag(void)
  *----------------------------------------------------------------------
  */
 void
-TableMergeTag(TableTag *baseTag, TableTag *addTag)
+TableMergeTag(Table *tablePtr, TableTag *baseTag, TableTag *addTag)
 {
-  if (addTag->anchor != (Tk_Anchor)-1) baseTag->anchor = addTag->anchor;
-  if (addTag->bg != NULL)              baseTag->bg = addTag->bg;
-  if (addTag->fg != NULL)              baseTag->fg = addTag->fg;
-  if (addTag->tkfont != NULL)          baseTag->tkfont = addTag->tkfont;
-  if (addTag->imageStr != NULL) {
-    baseTag->imageStr = addTag->imageStr;
-    baseTag->image = addTag->image;
-  }
-  if (addTag->multiline >= 0)          baseTag->multiline = addTag->multiline;
-  if (addTag->relief != -1)            baseTag->relief = addTag->relief;
-  if (addTag->showtext >= 0)           baseTag->showtext = addTag->showtext;
-  if (addTag->state != STATE_UNKNOWN)  baseTag->state = addTag->state;
-  if (addTag->justify != (Tk_Justify)-1) baseTag->justify = addTag->justify;
-  if (addTag->wrap >= 0)               baseTag->wrap = addTag->wrap;
-}
+    TableJoinTag *jtagPtr = (TableJoinTag *) baseTag;
+    unsigned int prio;
+
+    if (jtagPtr->magic != 0x99ABCDEF) {
+       panic("bad mojo in TableMergeTag");
+    }
+
+#ifndef NO_TAG_PRIORITIES
+    /*
+     * Find priority for the tag to merge
+     */
+    prio = TableTagGetPriority(tablePtr, addTag);
 
+    if ((addTag->anchor != -1) && (prio < jtagPtr->panchor)) {
+       baseTag->anchor         = addTag->anchor;
+       jtagPtr->panchor        = prio;
+    }
+    if ((addTag->bg != NULL) && (prio < jtagPtr->pbg)) {
+       baseTag->bg             = addTag->bg;
+       jtagPtr->pbg            = prio;
+    }
+    if ((addTag->fg != NULL) && (prio < jtagPtr->pfg)) {
+       baseTag->fg             = addTag->fg;
+       jtagPtr->pfg            = prio;
+    }
+    if ((addTag->tkfont != NULL) && (prio < jtagPtr->ptkfont)) {
+       baseTag->tkfont         = addTag->tkfont;
+       jtagPtr->ptkfont        = prio;
+    }
+    if ((addTag->imageStr != NULL) && (prio < jtagPtr->pimage)) {
+       baseTag->imageStr       = addTag->imageStr;
+       baseTag->image          = addTag->image;
+       jtagPtr->pimage         = prio;
+    }
+    if ((addTag->multiline >= 0) && (prio < jtagPtr->pmultiline)) {
+       baseTag->multiline      = addTag->multiline;
+       jtagPtr->pmultiline     = prio;
+    }
+    if ((addTag->relief != -1) && (prio < jtagPtr->prelief)) {
+       baseTag->relief         = addTag->relief;
+       jtagPtr->prelief        = prio;
+    }
+    if ((addTag->showtext >= 0) && (prio < jtagPtr->pshowtext)) {
+       baseTag->showtext       = addTag->showtext;
+       jtagPtr->pshowtext      = prio;
+    }
+    if ((addTag->state != STATE_UNKNOWN) && (prio < jtagPtr->pstate)) {
+       baseTag->state          = addTag->state;
+       jtagPtr->pstate         = prio;
+    }
+    if ((addTag->justify != -1) && (prio < jtagPtr->pjustify)) {
+       baseTag->justify        = addTag->justify;
+       jtagPtr->pjustify       = prio;
+    }
+    if ((addTag->wrap >= 0) && (prio < jtagPtr->pwrap)) {
+       baseTag->wrap           = addTag->wrap;
+       jtagPtr->pwrap          = prio;
+    }
+    if ((addTag->borders) && (prio < jtagPtr->pborders)) {
+       baseTag->borderStr      = addTag->borderStr;
+       baseTag->borders        = addTag->borders;
+       baseTag->bd[0]          = addTag->bd[0];
+       baseTag->bd[1]          = addTag->bd[1];
+       baseTag->bd[2]          = addTag->bd[2];
+       baseTag->bd[3]          = addTag->bd[3];
+       jtagPtr->pborders       = prio;
+    }
+#else
+    if (addTag->anchor != -1)  baseTag->anchor = addTag->anchor;
+    if (addTag->bg != NULL)    baseTag->bg     = addTag->bg;
+    if (addTag->fg != NULL)    baseTag->fg     = addTag->fg;
+    if (addTag->tkfont != NULL)        baseTag->tkfont = addTag->tkfont;
+    if (addTag->imageStr != NULL) {
+       baseTag->imageStr       = addTag->imageStr;
+       baseTag->image          = addTag->image;
+    }
+    if (addTag->multiline >= 0)        baseTag->multiline      = addTag->multiline;
+    if (addTag->relief != -1)  baseTag->relief         = addTag->relief;
+    if (addTag->showtext >= 0) baseTag->showtext       = addTag->showtext;
+    if (addTag->state != STATE_UNKNOWN)        baseTag->state  = addTag->state;
+    if (addTag->justify != -1) baseTag->justify        = addTag->justify;
+    if (addTag->wrap >= 0)     baseTag->wrap           = addTag->wrap;
+    if (addTag->borders) {
+       baseTag->borderStr      = addTag->borderStr;
+       baseTag->borders        = addTag->borders;
+       baseTag->bd[0]          = addTag->bd[0];
+       baseTag->bd[1]          = addTag->bd[1];
+       baseTag->bd[2]          = addTag->bd[2];
+       baseTag->bd[3]          = addTag->bd[3];
+    }
+#endif
+}
+\f
 /*
  *----------------------------------------------------------------------
  *
@@ -200,40 +362,143 @@ TableMergeTag(TableTag *baseTag, TableTag *addTag)
 void
 TableInvertTag(TableTag *baseTag)
 {
-  Tk_3DBorder tmpBg;
+    Tk_3DBorder tmpBg;
 
-  tmpBg = baseTag->fg;
-  baseTag->fg = baseTag->bg;
-  baseTag->bg = tmpBg;
+    tmpBg      = baseTag->fg;
+    baseTag->fg        = baseTag->bg;
+    baseTag->bg        = tmpBg;
 }
-
+\f
 /*
  *----------------------------------------------------------------------
  *
- * CreateTagEntry --
- *     Takes a name and optional args and create a tag entry in the
- *     table's tag table.
+ * TableGetTagBorders --
+ *     This routine gets the border values based on a tag.
  *
  * Results:
- *     A new tag entry will be created.
+ *     It returns the values in the int*'s (if not NULL), and the
+ *     total number of defined borders as a result.
  *
  * Side effects:
  *     None.
  *
  *----------------------------------------------------------------------
  */
-static void
-CreateTagEntry(Table *tablePtr, char *name, int argc, char **argv)
+int
+TableGetTagBorders(TableTag *tagPtr,
+       int *left, int *right, int *top, int *bottom)
 {
-  Tcl_HashEntry *entryPtr;
-  TableTag *tagPtr = TableNewTag();
-  int dummy;
-  Tk_ConfigureWidget(tablePtr->interp, tablePtr->tkwin, tagConfig,
-                    argc, argv, (char *)tagPtr, TK_CONFIG_ARGV_ONLY);
-  entryPtr = Tcl_CreateHashEntry(tablePtr->tagTable, name, &dummy);
-  Tcl_SetHashValue(entryPtr, (ClientData) tagPtr);
+    switch (tagPtr->borders) {
+       case 0:
+           if (left)   { *left         = 0; }
+           if (right)  { *right        = 0; }
+           if (top)    { *top          = 0; }
+           if (bottom) { *bottom       = 0; }
+           break;
+       case 1:
+           if (left)   { *left         = tagPtr->bd[0]; }
+           if (right)  { *right        = tagPtr->bd[0]; }
+           if (top)    { *top          = tagPtr->bd[0]; }
+           if (bottom) { *bottom       = tagPtr->bd[0]; }
+           break;
+       case 2:
+           if (left)   { *left         = tagPtr->bd[0]; }
+           if (right)  { *right        = tagPtr->bd[1]; }
+           if (top)    { *top          = 0; }
+           if (bottom) { *bottom       = 0; }
+           break;
+       case 4:
+           if (left)   { *left         = tagPtr->bd[0]; }
+           if (right)  { *right        = tagPtr->bd[1]; }
+           if (top)    { *top          = tagPtr->bd[2]; }
+           if (bottom) { *bottom       = tagPtr->bd[3]; }
+           break;
+       default:
+           panic("invalid border value '%d'\n", tagPtr->borders);
+           break;
+    }
+    return tagPtr->borders;
 }
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableTagGetEntry --
+ *     Takes a name and optional args and creates a tag entry in the
+ *     table's tag table.
+ *
+ * Results:
+ *     A new tag entry will be created and returned.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+static TableTag *
+TableTagGetEntry(Table *tablePtr, char *name, int objc, char **argv)
+{
+    Tcl_HashEntry *entryPtr;
+    TableTag *tagPtr = NULL;
+    int new;
 
+    entryPtr = Tcl_CreateHashEntry(tablePtr->tagTable, name, &new);
+    if (new) {
+       tagPtr = TableNewTag(NULL);
+       Tcl_SetHashValue(entryPtr, (ClientData) tagPtr);
+       if (tablePtr->tagPrioSize >= tablePtr->tagPrioMax) {
+           int i;
+           /*
+            * Increase the priority list size in blocks of 10
+            */
+           tablePtr->tagPrioMax += 10;
+           tablePtr->tagPrioNames = (char **) ckrealloc(
+               (char *) tablePtr->tagPrioNames,
+               sizeof(TableTag *) * tablePtr->tagPrioMax);
+           tablePtr->tagPrios = (TableTag **) ckrealloc(
+               (char *) tablePtr->tagPrios,
+               sizeof(TableTag *) * tablePtr->tagPrioMax);
+           for (i = tablePtr->tagPrioSize; i < tablePtr->tagPrioMax; i++) {
+               tablePtr->tagPrioNames[i] = (char *) NULL;
+               tablePtr->tagPrios[i] = (TableTag *) NULL;
+           }
+       }
+       tablePtr->tagPrioNames[tablePtr->tagPrioSize] =
+           (char *) Tcl_GetHashKey(tablePtr->tagTable, entryPtr);
+       tablePtr->tagPrios[tablePtr->tagPrioSize] = tagPtr;
+       tablePtr->tagPrioSize++;
+    } else {
+       tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
+    }
+    if (objc) {
+       Tk_ConfigureWidget(tablePtr->interp, tablePtr->tkwin, tagConfig,
+               objc, argv, (char *)tagPtr, TK_CONFIG_ARGV_ONLY);
+    }
+    return tagPtr;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableTagGetPriority --
+ *     Get the priority value for a tag.
+ *
+ * Results:
+ *     returns the priority.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+static unsigned int
+TableTagGetPriority(Table *tablePtr, TableTag *tagPtr)
+{
+    unsigned int prio = 0;
+    while (tagPtr != tablePtr->tagPrios[prio]) { prio++; }
+    return prio;
+}
+\f
 /*
  *----------------------------------------------------------------------
  *
@@ -251,17 +516,21 @@ CreateTagEntry(Table *tablePtr, char *name, int argc, char **argv)
 void
 TableInitTags(Table *tablePtr)
 {
-  static char *activeArgs[]    = {"-bg", ACTIVE_BG, "-relief", "flat" };
-  static char *selArgs[]       = {"-bg", SELECT_BG, "-relief", "sunken" };
-  static char *titleArgs[]     = {"-bg", DISABLED,  "-relief", "flat",
-                                  "-fg", "white", "-state", "disabled" };
-  static char *flashArgs[]     = {"-bg", "red" };
-  CreateTagEntry(tablePtr, "active", ARSIZE(activeArgs), activeArgs);
-  CreateTagEntry(tablePtr, "sel", ARSIZE(selArgs), selArgs);
-  CreateTagEntry(tablePtr, "title", ARSIZE(titleArgs), titleArgs);
-  CreateTagEntry(tablePtr, "flash", ARSIZE(flashArgs), flashArgs);
+    static char *activeArgs[]  = {"-bg", ACTIVE_BG, "-relief", "flat" };
+    static char *selArgs[]     = {"-bg", SELECT_BG, "-fg", SELECT_FG,
+                                  "-relief", "sunken" };
+    static char *titleArgs[]   = {"-bg", DISABLED, "-fg", "white",
+                                  "-relief", "flat", "-state", "disabled" };
+    static char *flashArgs[]   = {"-bg", "red" };
+    /*
+     * The order of creation is important to priority.
+     */
+    TableTagGetEntry(tablePtr, "flash", ARSIZE(flashArgs), flashArgs);
+    TableTagGetEntry(tablePtr, "active", ARSIZE(activeArgs), activeArgs);
+    TableTagGetEntry(tablePtr, "sel", ARSIZE(selArgs), selArgs);
+    TableTagGetEntry(tablePtr, "title", ARSIZE(titleArgs), titleArgs);
 }
-
+\f
 /*
  *----------------------------------------------------------------------
  *
@@ -273,38 +542,55 @@ TableInitTags(Table *tablePtr)
  *
  * Side effects:
  *     Possible side effects from eval of tagCommand.
+ *     IMPORTANT: This plays with the interp result object,
+ *     so use of resultPtr in prior command may be invalid after
+ *     calling this function.
  *
  *----------------------------------------------------------------------
  */
 TableTag *
 FindRowColTag(Table *tablePtr, int cell, int mode)
 {
-  Tcl_HashTable *hash;
-  Tcl_HashEntry *entryPtr;
-
-  hash = (mode == ROW) ? tablePtr->rowStyles : tablePtr->colStyles;
-  if ((entryPtr = Tcl_FindHashEntry(hash, (char *)cell)) == NULL) {
-    char *cmd = (mode == ROW) ? tablePtr->rowTagCmd : tablePtr->colTagCmd;
-    if (cmd) {
-      register Tcl_Interp *interp = tablePtr->interp;
-      char buf[INDEX_BUFSIZE];
-      /* Since it does not exist, eval command with row/col appended */
-      sprintf(buf, " %d", cell);
-      Tcl_Preserve((ClientData) interp);
-      if (Tcl_VarEval(interp, cmd, buf, (char *)NULL) == TCL_OK) {
-       char *name = Tcl_GetStringResult(interp);
-       if (name && *name) {
-         /* If a result was returned, check to see if it is a known tag */
-         entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, name);
+    Tcl_HashEntry *entryPtr;
+    TableTag *tagPtr = NULL;
+
+    entryPtr = Tcl_FindHashEntry((mode == ROW) ? tablePtr->rowStyles
+                                : tablePtr->colStyles, (char *) cell);
+    if (entryPtr == NULL) {
+       char *cmd = (mode == ROW) ? tablePtr->rowTagCmd : tablePtr->colTagCmd;
+       if (cmd) {
+           register Tcl_Interp *interp = tablePtr->interp;
+           char buf[INDEX_BUFSIZE];
+           /*
+            * Since no specific row/col tag exists, eval the given command
+            * with row/col appended
+            */
+           sprintf(buf, " %d", cell);
+           Tcl_Preserve((ClientData) interp);
+           if (Tcl_VarEval(interp, cmd, buf, (char *)NULL) == TCL_OK) {
+               char *name = Tcl_GetStringResult(interp);
+               if (name && *name) {
+                   /*
+                    * If a result was returned, check to see if it is
+                    * a valid tag.
+                    */
+                   entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, name);
+               }
+           }
+           Tcl_Release((ClientData) interp);
+           Tcl_ResetResult(interp);
        }
-      }
-      Tcl_Release((ClientData) interp);
-      Tcl_ResetResult(interp);
     }
-  }
-  return (TableTag *) (entryPtr ? Tcl_GetHashValue(entryPtr) : NULL);
+    if (entryPtr != NULL) {
+       /*
+        * This can be either the one in row|colStyles,
+        * or that returned by eval'ing the row|colTagCmd
+        */
+       tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
+    }
+    return tagPtr;
 }
-
+\f
 /* 
  *----------------------------------------------------------------------
  *
@@ -322,16 +608,20 @@ FindRowColTag(Table *tablePtr, int cell, int mode)
 void
 TableCleanupTag(Table *tablePtr, TableTag *tagPtr)
 {
-  if (tagPtr->image)
-    Tk_FreeImage(tagPtr->image);
-  /* free the options in the widget */
-  Tk_FreeOptions(tagConfig, (char *) tagPtr, tablePtr->display, 0);
-}
+    /*
+     * Free resources that the optionSpec doesn't specifically know about
+     */
+    if (tagPtr->image) {
+       Tk_FreeImage(tagPtr->image);
+    }
 
+    Tk_FreeOptions(tagConfig, (char *) tagPtr, tablePtr->display, 0);
+}
+\f
 /*
  *--------------------------------------------------------------
  *
- * TableTagCmd --
+ * Table_TagCmd --
  *     This procedure is invoked to process the tag method
  *     that corresponds to a widget managed by this module.
  *     See the user documentation for details on what it does.
@@ -345,412 +635,638 @@ TableCleanupTag(Table *tablePtr, TableTag *tagPtr)
  *--------------------------------------------------------------
  */
 int
-TableTagCmd(Table * tablePtr, register Tcl_Interp *interp,
-           int argc, char *argv[])
+Table_TagCmd(ClientData clientData, register Tcl_Interp *interp,
+           int objc, Tcl_Obj *CONST objv[])
 {
-  int result = TCL_OK, retval, i, newEntry, value;
-  int row, col;
-  TableTag *tagPtr;
-  Tcl_HashEntry *entryPtr, *scanPtr, *newEntryPtr, *oldEntryPtr;
-  Tcl_HashTable *hashTblPtr;
-  Tcl_HashSearch search;
-  Tk_Image image;
-  char buf[INDEX_BUFSIZE], *keybuf, *yes = "1", *no = "0";
-
-  /* parse the next argument */
-  retval = Cmd_Parse(interp, tag_cmds, argv[2]);
-  switch (retval) {
-    /* failed to parse the argument, error */
-  case 0:
-    return TCL_ERROR;
-
-  case TAG_CELLTAG:
-    /* tag a (group of) cell(s) */
-    if (argc < 4) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " tag cell tag ?arg arg ...?\"", (char *) NULL);
-      return TCL_ERROR;
-    }
-    /* are we deleting */
-    if (!(*argv[3]))
-      tagPtr = NULL;
-    else {
-      /* check to see if the tag actually exists */
-      if ((entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, argv[3]))==NULL) {
-       /* Unknown tag, just return empty string */
-       Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
-       return TCL_OK;
-      }
-      /* get the pointer to the tag structure */
-      tagPtr = (TableTag *) Tcl_GetHashValue (entryPtr);
-    }
+    register Table *tablePtr = (Table *)clientData;
+    int result = TCL_OK, cmdIndex, i, newEntry, value, len;
+    int row, col, tagPrio, refresh = 0;
+    TableTag *tagPtr, *tag2Ptr;
+    Tcl_HashEntry *entryPtr, *scanPtr;
+    Tcl_HashTable *hashTblPtr;
+    Tcl_HashSearch search;
+    Tk_Image image;
+    Tcl_Obj *objPtr, *resultPtr;
+    char buf[INDEX_BUFSIZE], *keybuf, *tagname;
 
-    /* No more args -> display only */
-    if (argc == 4) {
-      /* Added special handling for tags: active, flash, sel, title */
-
-      if ((tablePtr->flags & HAS_ACTIVE) && strcmp(argv[3], "active") == 0) {
-       TableMakeArrayIndex(tablePtr->activeRow+tablePtr->rowOffset,
-                           tablePtr->activeCol+tablePtr->colOffset, buf);
-       Tcl_AppendElement(interp, buf);
-      } else if (tablePtr->flashMode && strcmp(argv[3], "flash") == 0) {
-       for (scanPtr = Tcl_FirstHashEntry(tablePtr->flashCells, &search);
-            scanPtr != NULL; scanPtr = Tcl_NextHashEntry(&search)) {
-         Tcl_AppendElement(interp,
-                           Tcl_GetHashKey(tablePtr->flashCells, scanPtr));
-       }
-      } else if (strcmp(argv[3], "sel") == 0) {
-       for (scanPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
-            scanPtr != NULL; scanPtr = Tcl_NextHashEntry(&search)) {
-         Tcl_AppendElement(interp,
-                           Tcl_GetHashKey(tablePtr->selCells, scanPtr));
-       }
-      } else if (strcmp(argv[3], "title") == 0 &&
-                (tablePtr->titleRows || tablePtr->titleCols)) {
-       for (row = tablePtr->rowOffset;
-            row < tablePtr->rowOffset+tablePtr->rows; row++) {
-         for (col = tablePtr->colOffset;
-              col < tablePtr->colOffset+tablePtr->titleCols; col++) {
-           TableMakeArrayIndex(row, col, buf);
-           Tcl_AppendElement(interp, buf);
-         }
-       }
-       for (row = tablePtr->rowOffset;
-            row < tablePtr->rowOffset+tablePtr->titleRows; row++) {
-         for (col = tablePtr->colOffset+tablePtr->titleCols;
-              col < tablePtr->colOffset+tablePtr->cols; col++) {
-           TableMakeArrayIndex(row, col, buf);
-           Tcl_AppendElement(interp, buf);
-         }
-       }
-      } else {
-       for (scanPtr = Tcl_FirstHashEntry(tablePtr->cellStyles, &search);
-            scanPtr != NULL; scanPtr = Tcl_NextHashEntry(&search)) {
-         /* is this the tag pointer for this cell */
-         if ((TableTag *) Tcl_GetHashValue(scanPtr) == tagPtr) {
-           Tcl_AppendElement(interp,
-                             Tcl_GetHashKey(tablePtr->cellStyles, scanPtr));
-         }
-       }
-      }
-      return TCL_OK;
-    }
-    /* Now loop through the arguments and fill in the hash table */
-    for (i = 4; i < argc; i++) {
-      /* can I parse this argument */
-      if (TableGetIndex(tablePtr, argv[i], &row, &col) != TCL_OK) {
+    if (objc < 3) {
+       Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?");
        return TCL_ERROR;
-      }
-      /* get the hash key ready */
-      TableMakeArrayIndex(row, col, buf);
-
-      /* is this a deletion */
-      if (tagPtr == NULL) {
-       oldEntryPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf);
-       if (oldEntryPtr != NULL)
-         Tcl_DeleteHashEntry(oldEntryPtr);
-      } else {
-       /* add a key to the hash table */
-       newEntryPtr = Tcl_CreateHashEntry(tablePtr->cellStyles, buf,
-                                          &newEntry);
-
-       /* and set it to point to the Tag structure */
-       Tcl_SetHashValue (newEntryPtr, (ClientData) tagPtr);
-      }
-      /* now invalidate the area */
-      TableRefresh(tablePtr, row-tablePtr->rowOffset,
-                  col-tablePtr->colOffset, CELL);
     }
-    return TCL_OK;
 
-  case TAG_COLTAG:
-  case TAG_ROWTAG:
-    /* tag a row or a column */
-    if (argc < 4) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"",
-                       argv[0], " tag ", (retval == TAG_ROWTAG) ? "row" :
-                       "col", " tag ?arg arg ..?\"", (char *) NULL);
-      return TCL_ERROR;
-    }
-    /* if the tag is null, we are deleting */
-    if (!(*argv[3]))
-      tagPtr = NULL;
-    else {                     /* check to see if the tag actually exists */
-      if ((entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, argv[3]))==NULL) {
-       /* Unknown tag, just return empty string */
-       Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
-       return TCL_OK;
-      }
-      /* get the pointer to the tag structure */
-      tagPtr = (TableTag *) Tcl_GetHashValue (entryPtr);
+    result = Tcl_GetIndexFromObj(interp, objv[2], tagCmdNames,
+                                "tag option", 0, &cmdIndex);
+    if (result != TCL_OK) {
+       return result;
     }
+    /*
+     * Before using this object, make sure there aren't any calls that
+     * could have changed the interp result, thus freeing the object.
+     */
+    resultPtr = Tcl_GetObjResult(interp);
+
+    switch ((enum tagCmd) cmdIndex) {
+       case TAG_CELLTAG:       /* add named tag to a (group of) cell(s) */
+           if (objc < 4) {
+               Tcl_WrongNumArgs(interp, 3, objv, "tag ?arg arg ...?");
+               return TCL_ERROR;
+           }
+           tagname = Tcl_GetStringFromObj(objv[3], &len);
+           if (len == 0) {
+               /*
+                * An empty string was specified, so just delete the tag.
+                */
+               tagPtr = NULL;
+           } else {
+               /*
+                * Get the pointer to the tag structure.  If it doesn't
+                * exist, it will be created.
+                */
+               tagPtr = TableTagGetEntry(tablePtr, tagname, 0, NULL);
+           }
+
+           if (objc == 4) {
+               /*
+                * The user just wants the cells with this tag returned.
+                * Handle specially tags named: active, flash, sel, title
+                */
+
+               if ((tablePtr->flags & HAS_ACTIVE) &&
+                       STREQ(tagname, "active")) {
+                   TableMakeArrayIndex(
+                       tablePtr->activeRow+tablePtr->rowOffset,
+                       tablePtr->activeCol+tablePtr->colOffset, buf);
+                   Tcl_SetStringObj(resultPtr, buf, -1);
+               } else if ((tablePtr->flashMode && STREQ(tagname, "flash"))
+                       || STREQ(tagname, "sel")) {
+                   hashTblPtr = (*tagname == 's') ?
+                       tablePtr->selCells : tablePtr->flashCells;
+                   for (scanPtr = Tcl_FirstHashEntry(hashTblPtr, &search);
+                        scanPtr != NULL;
+                        scanPtr = Tcl_NextHashEntry(&search)) {
+                       keybuf = (char *) Tcl_GetHashKey(hashTblPtr, scanPtr);
+                       Tcl_ListObjAppendElement(NULL, resultPtr,
+                               Tcl_NewStringObj(keybuf, -1));
+                   }
+               } else if (STREQ(tagname, "title") &&
+                       (tablePtr->titleRows || tablePtr->titleCols)) {
+                   for (row = tablePtr->rowOffset;
+                        row < tablePtr->rowOffset+tablePtr->rows; row++) {
+                       for (col = tablePtr->colOffset;
+                            col < tablePtr->colOffset+tablePtr->titleCols;
+                            col++) {
+                           TableMakeArrayIndex(row, col, buf);
+                           Tcl_ListObjAppendElement(NULL, resultPtr,
+                                   Tcl_NewStringObj(buf, -1));
+                       }
+                   }
+                   for (row = tablePtr->rowOffset;
+                        row < tablePtr->rowOffset+tablePtr->titleRows;
+                        row++) {
+                       for (col = tablePtr->colOffset+tablePtr->titleCols;
+                            col < tablePtr->colOffset+tablePtr->cols; col++) {
+                           TableMakeArrayIndex(row, col, buf);
+                           Tcl_ListObjAppendElement(NULL, resultPtr,
+                                   Tcl_NewStringObj(buf, -1));
+                       }
+                   }
+               } else {
+                   /*
+                    * Check this tag pointer amongst all tagged cells
+                    */
+                   for (scanPtr = Tcl_FirstHashEntry(tablePtr->cellStyles,
+                           &search);
+                        scanPtr != NULL;
+                        scanPtr = Tcl_NextHashEntry(&search)) {
+                       if ((TableTag *) Tcl_GetHashValue(scanPtr) == tagPtr) {
+                           keybuf = (char *) Tcl_GetHashKey(
+                               tablePtr->cellStyles, scanPtr);
+                           Tcl_ListObjAppendElement(NULL, resultPtr,
+                                   Tcl_NewStringObj(keybuf, -1));
+                       }
+                   }
+               }
+               return TCL_OK;
+           }
+
+           /*
+            * Loop through the arguments and fill in the hash table
+            */
+           for (i = 4; i < objc; i++) {
+               /*
+                * Try and parse the index
+                */
+               if (TableGetIndexObj(tablePtr, objv[i], &row, &col)
+                       != TCL_OK) {
+                   return TCL_ERROR;
+               }
+               /*
+                * Get the hash key ready
+                */
+               TableMakeArrayIndex(row, col, buf);
+
+               if (tagPtr == NULL) {
+                   /*
+                    * This is a deletion
+                    */
+                   entryPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf);
+                   if (entryPtr != NULL) {
+                       Tcl_DeleteHashEntry(entryPtr);
+                       refresh = 1;
+                   }
+               } else {
+                   /*
+                    * Add a key to the hash table and set it to point to the
+                    * Tag structure if it wasn't the same as an existing one
+                    */
+                   entryPtr = Tcl_CreateHashEntry(tablePtr->cellStyles,
+                           buf, &newEntry);
+                   if (newEntry || (tagPtr !=
+                           (TableTag *) Tcl_GetHashValue(entryPtr))) {
+                       Tcl_SetHashValue(entryPtr, (ClientData) tagPtr);
+                       refresh = 1;
+                   }
+               }
+               /*
+                * Now invalidate this cell for redraw
+                */
+               if (refresh) {
+                   TableRefresh(tablePtr, row-tablePtr->rowOffset,
+                           col-tablePtr->colOffset, CELL);
+               }
+           }
+           return TCL_OK;
+
+       case TAG_COLTAG:
+       case TAG_ROWTAG: {          /* tag a row or a column */
+           int forRows = (cmdIndex == TAG_ROWTAG);
+
+           if (objc < 4) {
+               Tcl_WrongNumArgs(interp, 3, objv, "tag ?arg arg ..?");
+               return TCL_ERROR;
+           }
+           tagname = Tcl_GetStringFromObj(objv[3], &len);
+           if (len == 0) {
+               /*
+                * Empty string, so we want to delete this element
+                */
+               tagPtr = NULL;
+           } else {
+               /*
+                * Get the pointer to the tag structure.  If it doesn't
+                * exist, it will be created.
+                */
+               tagPtr = TableTagGetEntry(tablePtr, tagname, 0, NULL);
+           }
 
-    /* and choose the correct hash table */
-    hashTblPtr = (retval == TAG_ROWTAG) ?
-      tablePtr->rowStyles : tablePtr->colStyles;
-
-    /* No more args -> display only */
-    if (argc == 4) {
-      for (scanPtr = Tcl_FirstHashEntry(hashTblPtr, &search);
-          scanPtr != NULL; scanPtr = Tcl_NextHashEntry(&search)) {
-       /* is this the tag pointer on this row */
-       if ((TableTag *) Tcl_GetHashValue (scanPtr) == tagPtr) {
-         sprintf(buf, "%d", (int) Tcl_GetHashKey (hashTblPtr, scanPtr));
-         Tcl_AppendElement(interp, buf);
+           /*
+            * Choose the correct hash table based on args
+            */
+           hashTblPtr = forRows ? tablePtr->rowStyles : tablePtr->colStyles;
+
+           if (objc == 4) {
+               /* the user just wants the tagged cells to be returned */
+               /* Special handling for tags: active, flash, sel, title */
+
+               if ((tablePtr->flags & HAS_ACTIVE) &&
+                       strcmp(tagname, "active") == 0) {
+                   Tcl_SetIntObj(resultPtr,
+                           (forRows ?
+                                   tablePtr->activeRow+tablePtr->rowOffset :
+                                   tablePtr->activeCol+tablePtr->colOffset));
+               } else if ((tablePtr->flashMode && STREQ(tagname, "flash"))
+                       || STREQ(tagname, "sel")) {
+                   Tcl_HashTable *cacheTblPtr;
+
+                   cacheTblPtr = (Tcl_HashTable *)
+                       ckalloc(sizeof(Tcl_HashTable));
+                   Tcl_InitHashTable(cacheTblPtr, TCL_ONE_WORD_KEYS);
+
+                   hashTblPtr = (*tagname == 's') ?
+                       tablePtr->selCells : tablePtr->flashCells;
+                   for (scanPtr = Tcl_FirstHashEntry(hashTblPtr, &search);
+                        scanPtr != NULL;
+                        scanPtr = Tcl_NextHashEntry(&search)) {
+                       TableParseArrayIndex(&row, &col,
+                               Tcl_GetHashKey(hashTblPtr, scanPtr));
+                       value = forRows ? row : col;
+                       entryPtr = Tcl_CreateHashEntry(cacheTblPtr,
+                               (char *)value, &newEntry);
+                       if (newEntry) {
+                           Tcl_ListObjAppendElement(NULL, resultPtr,
+                                   Tcl_NewIntObj(value));
+                       }
+                   }
+
+                   Tcl_DeleteHashTable(cacheTblPtr);
+                   ckfree((char *) (cacheTblPtr));
+               } else if (STREQ(tagname, "title") &&
+                       (forRows?tablePtr->titleRows:tablePtr->titleCols)) {
+                   if (forRows) {
+                       for (row = tablePtr->rowOffset;
+                            row < tablePtr->rowOffset+tablePtr->titleRows;
+                            row++) {
+                           Tcl_ListObjAppendElement(NULL, resultPtr,
+                                   Tcl_NewIntObj(row));
+                       }
+                   } else {
+                       for (col = tablePtr->colOffset;
+                            col < tablePtr->colOffset+tablePtr->titleCols;
+                            col++) {
+                           Tcl_ListObjAppendElement(NULL, resultPtr,
+                                   Tcl_NewIntObj(col));
+                       }
+                   }
+               } else {
+                   for (scanPtr = Tcl_FirstHashEntry(hashTblPtr, &search);
+                        scanPtr != NULL;
+                        scanPtr = Tcl_NextHashEntry(&search)) {
+                       /* is this the tag pointer on this row */
+                       if ((TableTag *) Tcl_GetHashValue(scanPtr) == tagPtr) {
+                           objPtr = Tcl_NewIntObj(
+                               (int) Tcl_GetHashKey(hashTblPtr, scanPtr));
+                           Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
+                       }
+                   }
+               }
+               return TCL_OK;
+           }
+
+           /*
+            * Loop through the arguments and fill in the hash table
+            */
+           for (i = 4; i < objc; i++) {
+               /*
+                * Try and parse the index
+                */
+               if (Tcl_GetIntFromObj(interp, objv[i], &value) != TCL_OK) {
+                   return TCL_ERROR;
+               }
+               if (tagPtr == NULL) {
+                   /*
+                    * This is a deletion
+                    */
+                   entryPtr = Tcl_FindHashEntry(hashTblPtr, (char *)value);
+                   if (entryPtr != NULL) {
+                       Tcl_DeleteHashEntry(entryPtr);
+                       refresh = 1;
+                   }
+               } else {
+                   /*
+                    * Add a key to the hash table and set it to point to the
+                    * Tag structure if it wasn't the same as an existing one
+                    */
+                   entryPtr = Tcl_CreateHashEntry(hashTblPtr,
+                           (char *) value, &newEntry);
+                   if (newEntry || (tagPtr !=
+                           (TableTag *) Tcl_GetHashValue(entryPtr))) {
+                       Tcl_SetHashValue(entryPtr, (ClientData) tagPtr);
+                       refresh = 1;
+                   }
+               }
+               /* and invalidate the row or column affected */
+               if (refresh) {
+                   if (cmdIndex == TAG_ROWTAG) {
+                       TableRefresh(tablePtr, value-tablePtr->rowOffset, 0,
+                               ROW);
+                   } else {
+                       TableRefresh(tablePtr, 0, value-tablePtr->colOffset,
+                               COL);
+                   }
+               }
+           }
+           return TCL_OK;      /* COLTAG && ROWTAG */
        }
-      }
-      return TCL_OK;
-    }
-    /* Now loop through the arguments and fill in the hash table */
-    for (i = 4; i < argc; i++) {
-      /* can I parse this argument */
-      if (Tcl_GetInt(interp, argv[i], &value) != TCL_OK) {
-       return TCL_ERROR;
-      }
-      /* deleting or adding */
-      if (tagPtr == NULL) {
-       oldEntryPtr = Tcl_FindHashEntry(hashTblPtr, (char *) value);
-       if (oldEntryPtr != NULL)
-         Tcl_DeleteHashEntry(oldEntryPtr);
-      } else {
-       /* add a key to the hash table */
-       newEntryPtr = Tcl_CreateHashEntry(hashTblPtr, (char *) value,
-                                         &newEntry);
-
-       /* and set it to point to the Tag structure */
-       Tcl_SetHashValue (newEntryPtr, (ClientData) tagPtr);
-      }
-      /* and invalidate the row or column affected */
-      if (retval == TAG_ROWTAG) {
-       TableRefresh(tablePtr, value-tablePtr->rowOffset, 0, ROW);
-      } else {
-       TableRefresh(tablePtr, 0, value-tablePtr->colOffset, COL);
-      }
-    }
-    return TCL_OK;     /* COLTAG && ROWTAG */
 
-  case TAG_CGET:
-    if (argc != 5) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"",
-                       argv[0], " tag cget tagName option\"", (char *) NULL);
-      return TCL_ERROR;
-    }
-    if ((entryPtr=Tcl_FindHashEntry(tablePtr->tagTable, argv[3])) == NULL) {
-      Tcl_AppendResult(interp, "invalid tag name \"", argv[3],
-                      "\"", (char *) NULL);
-      return TCL_ERROR;
-    } else {
-      tagPtr = (TableTag *) Tcl_GetHashValue (entryPtr);
-      result = Tk_ConfigureValue(interp, tablePtr->tkwin, tagConfig,
-                                (char *) tagPtr, argv[4], 0);
-    }
-    return result;     /* CGET */
-
-  case TAG_CONFIGURE:
-    if (argc < 4) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"",
-                      argv[0], " tag configure tagName ?arg arg  ...?\"",
-                      (char *) NULL);
-      return TCL_ERROR;
-    }
-    /* first see if this is a reconfiguration */
-    entryPtr = Tcl_CreateHashEntry(tablePtr->tagTable, argv[3], &newEntry);
-    if (newEntry) {
-      /* create the structure */
-      tagPtr = TableNewTag();
-
-      /* insert it into the table */
-      Tcl_SetHashValue(entryPtr, (ClientData) tagPtr);
-
-      /* configure the tag structure */
-      result = Tk_ConfigureWidget(interp, tablePtr->tkwin, tagConfig,
-                                 argc - 4, argv + 4, (char *) tagPtr, 0);
-      if (result == TCL_ERROR)
-       return TCL_ERROR;
-    } else {
-      /* pointer wasn't null, do a reconfig if we have enough arguments */
-      /* get the tag pointer from the table */
-      tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
-
-      /* 5 args means that there are values to replace */
-      if (argc > 5) {
-       /* and do a reconfigure */
-       result = Tk_ConfigureWidget(interp, tablePtr->tkwin,
-                                   tagConfig, argc - 4, argv + 4,
-                                   (char *) tagPtr, TK_CONFIG_ARGV_ONLY);
-       if (result == TCL_ERROR)
-         return TCL_ERROR;
-      }
-    }
+       case TAG_CGET:
+           if (objc != 5) {
+               Tcl_WrongNumArgs(interp, 3, objv, "tagName option");
+               return TCL_ERROR;
+           }
+           tagname  = Tcl_GetString(objv[3]);
+           entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname);
+           if (entryPtr == NULL) {
+               goto invalidtag;
+           } else {
+               tagPtr = (TableTag *) Tcl_GetHashValue (entryPtr);
+               result = Tk_ConfigureValue(interp, tablePtr->tkwin, tagConfig,
+                       (char *) tagPtr, Tcl_GetString(objv[4]), 0);
+           }
+           return result;      /* CGET */
 
-    /* handle change of image name */
-    if (tagPtr->imageStr) {
-      image = Tk_GetImage(interp, tablePtr->tkwin, tagPtr->imageStr,
-                         TableImageProc, (ClientData)tablePtr);
-      if (image == NULL)
-       result = TCL_ERROR;
-    } else {
-      image = NULL;
-    }
-    if (tagPtr->image) {
-      Tk_FreeImage(tagPtr->image);
-    }
-    tagPtr->image = image;
+       case TAG_CONFIGURE:
+           if (objc < 4) {
+               Tcl_WrongNumArgs(interp, 3, objv, "tagName ?arg arg  ...?");
+               return TCL_ERROR;
+           }
 
-    /* 
-     * If there were less than 6 args, we need
-     * to do a printout of the config, even for new tags
-     */
-    if (argc < 6) {
-      result = Tk_ConfigureInfo(interp, tablePtr->tkwin, tagConfig,
-                               (char *) tagPtr, (argc == 5)?argv[4]:0, 0);
-    } else {
-      /* Otherwise we reconfigured so invalidate the table for a redraw */
-      TableInvalidateAll(tablePtr, 0);
-    }
-    return result;
-
-  case TAG_DELETE:
-    /* delete a tag */
-    if (argc < 4) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"",
-              argv[0], " tag delete tagName ?tagName ...?\"", (char *) NULL);
-      return TCL_ERROR;
-    }
-    /* run through the remaining arguments */
-    for (i = 3; i < argc; i++) {
-      /* cannot delete the title tag */
-      if (strcmp(argv[i], "title") == 0 || strcmp (argv[i], "sel") == 0 ||
-         strcmp(argv[i], "flash") == 0 || strcmp (argv[i], "active") == 0) {
-       Tcl_AppendResult(interp, "cannot delete ", argv[i],
-                         " tag", (char *) NULL);
-       return TCL_ERROR;
-      }
-      if ((entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, argv[i]))!=NULL) {
-       /* get the tag pointer */
-       tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
+           /*
+            * Get the pointer to the tag structure.  If it doesn't
+            * exist, it will be created.
+            */
+           tagPtr = TableTagGetEntry(tablePtr, Tcl_GetString(objv[3]),
+                   0, NULL);
 
-       /* delete all references to this tag in rows */
-       scanPtr = Tcl_FirstHashEntry(tablePtr->rowStyles, &search);
-       for (; scanPtr != NULL; scanPtr = Tcl_NextHashEntry(&search))
-         if ((TableTag *)Tcl_GetHashValue(scanPtr) == tagPtr)
-           Tcl_DeleteHashEntry(scanPtr);
-
-       /* delete all references to this tag in cols */
-       scanPtr = Tcl_FirstHashEntry(tablePtr->colStyles, &search);
-       for (; scanPtr != NULL; scanPtr = Tcl_NextHashEntry(&search))
-         if ((TableTag *)Tcl_GetHashValue(scanPtr) == tagPtr)
-           Tcl_DeleteHashEntry(scanPtr);
-
-       /* delete all references to this tag in cells */
-       scanPtr = Tcl_FirstHashEntry(tablePtr->cellStyles, &search);
-       for (; scanPtr != NULL; scanPtr = Tcl_NextHashEntry(&search))
-         if ((TableTag *)Tcl_GetHashValue(scanPtr) == tagPtr)
-           Tcl_DeleteHashEntry(scanPtr);
-
-       /* release the structure */
-       TableCleanupTag(tablePtr, tagPtr);
-       ckfree((char *) tagPtr);
-
-       /* and free the hash table entry */
-       Tcl_DeleteHashEntry(entryPtr);
-      }
-    }
-    /* since we deleted a tag, redraw the screen */
-    TableInvalidateAll(tablePtr, 0);
-    return result;
-
-  case TAG_EXISTS:
-    if (argc != 4) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"",
-                       argv[0], " tag exists tagName\"", (char *) NULL);
-      return TCL_ERROR;
-    }
-    if (Tcl_FindHashEntry(tablePtr->tagTable, argv[3]) != NULL) {
-      Tcl_SetResult(interp, yes, TCL_VOLATILE);
-    } else {
-      Tcl_SetResult(interp, no, TCL_VOLATILE);
-    }
-    return TCL_OK;
+           /* 
+            * If there were less than 6 args, we return the configuration
+            * (for all or just one option), even for new tags
+            */
+           if (objc < 6) {
+               result = Tk_ConfigureInfo(interp, tablePtr->tkwin, tagConfig,
+                       (char *) tagPtr, (objc == 5) ?
+                       Tcl_GetString(objv[4]) : NULL, 0);
+           } else {
+               char **argv;
 
-  case TAG_INCLUDES:
-    /* does a tag contain a index ? */
-    if (argc != 5) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                       " tag includes tag index\"", (char *) NULL);
-      return TCL_ERROR;
-    }
-    /* check to see if the tag actually exists */
-    if ((entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, argv[3])) == NULL) {
-      /* Unknown tag, just return no */
-      Tcl_SetResult(interp, no, TCL_VOLATILE);
-      return TCL_OK;
-    }
-    /* parse index */
-    if (TableGetIndex (tablePtr, argv[4], &row, &col) != TCL_OK) {
-      return TCL_ERROR;
-    }
-    /* create hash key */
-    TableMakeArrayIndex(row, col, buf);
+               /* Stringify */
+               argv = (char **) ckalloc((objc + 1) * sizeof(char *));
+               for (i = 0; i < objc; i++)
+                   argv[i] = Tcl_GetString(objv[i]);
+               argv[objc] = NULL;
+
+               result = Tk_ConfigureWidget(interp, tablePtr->tkwin,
+                       tagConfig, objc-4, argv+4, (char *) tagPtr,
+                       TK_CONFIG_ARGV_ONLY);
+               ckfree((char *) argv);
+               if (result == TCL_ERROR) {
+                   return TCL_ERROR;
+               }
+
+               /*
+                * Handle change of image name
+                */
+               if (tagPtr->imageStr) {
+                   image = Tk_GetImage(interp, tablePtr->tkwin,
+                           tagPtr->imageStr,
+                           TableImageProc, (ClientData)tablePtr);
+                   if (image == NULL) {
+                       result = TCL_ERROR;
+                   }
+               } else {
+                   image = NULL;
+               }
+               if (tagPtr->image) {
+                   Tk_FreeImage(tagPtr->image);
+               }
+               tagPtr->image = image;
+
+               /*
+                * We reconfigured, so invalidate the table to redraw
+                */
+               TableInvalidateAll(tablePtr, 0);
+           }
+           return result;
+
+       case TAG_DELETE:
+           /* delete a tag */
+           if (objc < 4) {
+               Tcl_WrongNumArgs(interp, 3, objv, "tagName ?tagName ...?");
+               return TCL_ERROR;
+           }
+           /* run through the remaining arguments */
+           for (i = 3; i < objc; i++) {
+               tagname  = Tcl_GetString(objv[i]);
+               /* cannot delete the title tag */
+               if (STREQ(tagname, "title") ||
+                       STREQ(tagname, "sel") ||
+                       STREQ(tagname, "flash") ||
+                       STREQ(tagname, "active")) {
+                   Tcl_AppendStringsToObj(resultPtr, "cannot delete ",
+                           tagname, " tag", (char *) NULL);
+                   return TCL_ERROR;
+               }
+               entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname);
+               if (entryPtr != NULL) {
+                   /* get the tag pointer */
+                   tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
+
+                   /* delete all references to this tag in rows */
+                   scanPtr = Tcl_FirstHashEntry(tablePtr->rowStyles, &search);
+                   for (; scanPtr != NULL;
+                        scanPtr = Tcl_NextHashEntry(&search)) {
+                       if ((TableTag *)Tcl_GetHashValue(scanPtr) == tagPtr) {
+                           Tcl_DeleteHashEntry(scanPtr);
+                           refresh = 1;
+                       }
+                   }
+
+                   /* delete all references to this tag in cols */
+                   scanPtr = Tcl_FirstHashEntry(tablePtr->colStyles, &search);
+                   for (; scanPtr != NULL;
+                        scanPtr = Tcl_NextHashEntry(&search)) {
+                       if ((TableTag *)Tcl_GetHashValue(scanPtr) == tagPtr) {
+                           Tcl_DeleteHashEntry(scanPtr);
+                           refresh = 1;
+                       }
+                   }
+
+                   /* delete all references to this tag in cells */
+                   scanPtr = Tcl_FirstHashEntry(tablePtr->cellStyles,
+                           &search);
+                   for (; scanPtr != NULL;
+                        scanPtr = Tcl_NextHashEntry(&search)) {
+                       if ((TableTag *)Tcl_GetHashValue(scanPtr) == tagPtr) {
+                           Tcl_DeleteHashEntry(scanPtr);
+                           refresh = 1;
+                       }
+                   }
+
+                   /*
+                    * Remove the tag from the prio list and collapse
+                    * the rest of the tags.  We could check for shrinking
+                    * the prio list as well.
+                    */
+                   for (i = 0; i < tablePtr->tagPrioSize; i++) {
+                       if (tablePtr->tagPrios[i] == tagPtr) break;
+                   }
+                   for ( ; i < tablePtr->tagPrioSize; i++) {
+                       tablePtr->tagPrioNames[i] =
+                           tablePtr->tagPrioNames[i+1];
+                       tablePtr->tagPrios[i] = tablePtr->tagPrios[i+1];
+                   }
+                   tablePtr->tagPrioSize--;
+
+                   /* Release the tag structure */
+                   TableCleanupTag(tablePtr, tagPtr);
+                   ckfree((char *) tagPtr);
+
+                   /* And free the hash table entry */
+                   Tcl_DeleteHashEntry(entryPtr);
+               }
+           }
+           /* since we deleted a tag, redraw the screen */
+           if (refresh) {
+               TableInvalidateAll(tablePtr, 0);
+           }
+           return result;
+
+       case TAG_EXISTS:
+           if (objc != 4) {
+               Tcl_WrongNumArgs(interp, 3, objv, "tagName");
+               return TCL_ERROR;
+           }
+           Tcl_SetBooleanObj(resultPtr,
+                   (Tcl_FindHashEntry(tablePtr->tagTable,
+                           Tcl_GetString(objv[3])) != NULL));
+           return TCL_OK;
+
+       case TAG_INCLUDES:
+           /* does a tag contain a index ? */
+           if (objc != 5) {
+               Tcl_WrongNumArgs(interp, 3, objv, "tag index");
+               return TCL_ERROR;
+           }
+           tagname  = Tcl_GetString(objv[3]);
+           /* check to see if the tag actually exists */
+           entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname);
+           if (entryPtr == NULL) {
+               /* Unknown tag, just return 0 */
+               Tcl_SetBooleanObj(resultPtr, 0);
+               return TCL_OK;
+           }
+           /* parse index */
+           if (TableGetIndexObj(tablePtr, objv[4], &row, &col) != TCL_OK) {
+               return TCL_ERROR;
+           }
+           /* create hash key */
+           TableMakeArrayIndex(row, col, buf);
     
-    if (strcmp(argv[3], "active") == 0) {
-      if (tablePtr->activeRow+tablePtr->rowOffset == row &&
-         tablePtr->activeCol+tablePtr->colOffset == col)
-       Tcl_SetResult(interp, yes, TCL_VOLATILE);
-      else
-       Tcl_SetResult(interp, no, TCL_VOLATILE);
-      return TCL_OK;
-    } else if (strcmp(argv[3], "flash") == 0) {
-      if (tablePtr->flashMode && Tcl_FindHashEntry(tablePtr->flashCells, buf))
-       Tcl_SetResult(interp, yes, TCL_VOLATILE);
-      else
-       Tcl_SetResult(interp, no, TCL_VOLATILE);
-      return TCL_OK;
-    } else if (strcmp(argv[3], "sel") == 0) {
-      if (Tcl_FindHashEntry(tablePtr->selCells, buf))
-       Tcl_SetResult(interp, yes, TCL_VOLATILE);
-      else
-       Tcl_SetResult(interp, no, TCL_VOLATILE);
-      return TCL_OK;
-    } else if (strcmp(argv[3], "title") == 0) {
-      if (row < tablePtr->titleRows+tablePtr->rowOffset ||
-         col < tablePtr->titleCols+tablePtr->colOffset)
-       Tcl_SetResult(interp, yes, TCL_VOLATILE);
-      else
-       Tcl_SetResult(interp, no, TCL_VOLATILE);
-      return TCL_OK;
-    }
+           if (STREQ(tagname, "active")) {
+               result = (tablePtr->activeRow+tablePtr->rowOffset==row &&
+                       tablePtr->activeCol+tablePtr->colOffset==col);
+           } else if (STREQ(tagname, "flash")) {
+               result = (tablePtr->flashMode &&
+                       (Tcl_FindHashEntry(tablePtr->flashCells, buf)
+                               != NULL));
+           } else if (STREQ(tagname, "sel")) {
+               result = (Tcl_FindHashEntry(tablePtr->selCells, buf) != NULL);
+           } else if (STREQ(tagname, "title")) {
+               result = (row < tablePtr->titleRows+tablePtr->rowOffset ||
+                       col < tablePtr->titleCols+tablePtr->colOffset);
+           } else {
+               /* get the pointer to the tag structure */
+               tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
+               scanPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf);
+               /*
+                * Look to see if there is a cell, row, or col tag
+                * for this cell
+                */
+               result = ((scanPtr &&
+                       (tagPtr == (TableTag *) Tcl_GetHashValue(scanPtr))) ||
+                       (tagPtr == FindRowColTag(tablePtr, row, ROW)) ||
+                       (tagPtr == FindRowColTag(tablePtr, col, COL)));
+           }
+           /*
+            * Because we may call FindRowColTag above, we can't use
+            * the resultPtr, but this is almost equivalent, and is SAFE
+            */
+           Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result));
+           return TCL_OK;
 
-    /* get the pointer to the tag structure */
-    tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
-    scanPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf);
-    /* look to see if there is a cell, row, or col tag for this cell */
-    if ((scanPtr && ((TableTag *) Tcl_GetHashValue(scanPtr) == tagPtr)) ||
-        (tagPtr == FindRowColTag(tablePtr, row, ROW)) ||
-        (tagPtr == FindRowColTag(tablePtr, col, COL))) {
-      /* yes there is - return true */
-      Tcl_SetResult(interp, yes, TCL_VOLATILE);
-      return TCL_OK;
-    }
-    Tcl_SetResult(interp, no, TCL_VOLATILE);
-    return TCL_OK;
+       case TAG_NAMES:
+           /*
+            * Print out the tag names in priority order
+            */
+           if (objc < 3 || objc > 4) {
+               Tcl_WrongNumArgs(interp, 3, objv, "?pattern?");
+               return TCL_ERROR;
+           }
+           tagname = (objc == 4) ? Tcl_GetString(objv[3]) : NULL;
+           for (i = 0; i < tablePtr->tagPrioSize; i++) {
+               keybuf = tablePtr->tagPrioNames[i];
+               if (objc == 3 || Tcl_StringMatch(keybuf, tagname)) {
+                   objPtr = Tcl_NewStringObj(keybuf, -1);
+                   Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
+               }
+           }
+           return TCL_OK;
+
+       case TAG_LOWER:
+       case TAG_RAISE:
+           /*
+            * Change priority of the named tag
+            */
+           if (objc != 4 && objc != 5) {
+               Tcl_WrongNumArgs(interp, 3, objv, (cmdIndex == TAG_LOWER) ?
+                       "tagName ?belowThis?" : "tagName ?aboveThis?");
+               return TCL_ERROR;
+           }
+           tagname  = Tcl_GetString(objv[3]);
+           /* check to see if the tag actually exists */
+           entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname);
+           if (entryPtr == NULL) {
+               goto invalidtag;
+           }
+           tagPtr  = (TableTag *) Tcl_GetHashValue(entryPtr);
+           tagPrio = TableTagGetPriority(tablePtr, tagPtr);
+           keybuf  = tablePtr->tagPrioNames[tagPrio];
+           /*
+            * In the RAISE case, the priority is one higher (-1) because
+            * we want the named tag to move above the other in priority.
+            */
+           if (objc == 5) {
+               tagname  = Tcl_GetString(objv[4]);
+               entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname);
+               if (entryPtr == NULL) {
+                   goto invalidtag;
+               }
+               tag2Ptr  = (TableTag *) Tcl_GetHashValue(entryPtr);
+               if (cmdIndex == TAG_LOWER) {
+                   value = TableTagGetPriority(tablePtr, tag2Ptr);
+               } else {
+                   value = TableTagGetPriority(tablePtr, tag2Ptr) - 1;
+               }
+           } else {
+               if (cmdIndex == TAG_LOWER) {
+                   /*
+                    * Lower this tag's priority to the bottom.
+                    */
+                   value = tablePtr->tagPrioSize - 1;
+               } else {
+                   /*
+                    * Raise this tag's priority to the top.
+                    */
+                   value = -1;
+               }
+           }
+           if (value < tagPrio) {
+               /*
+                * Move tag up in priority.
+                */
+               for (i = tagPrio; i > value; i--) {
+                   tablePtr->tagPrioNames[i] = tablePtr->tagPrioNames[i-1];
+                   tablePtr->tagPrios[i]     = tablePtr->tagPrios[i-1];
+               }
+               i++;
+               tablePtr->tagPrioNames[i] = keybuf;
+               tablePtr->tagPrios[i]     = tagPtr;
+               refresh = 1;
+           } else if (value > tagPrio) {
+               /*
+                * Move tag down in priority.
+                */
+               for (i = tagPrio; i < value; i++) {
+                   tablePtr->tagPrioNames[i] = tablePtr->tagPrioNames[i+1];
+                   tablePtr->tagPrios[i]     = tablePtr->tagPrios[i+1];
+               }
+               tablePtr->tagPrioNames[i] = keybuf;
+               tablePtr->tagPrios[i]     = tagPtr;
+               refresh = 1;
+           }
+           /* since we deleted a tag, redraw the screen */
+           if (refresh) {
+               TableInvalidateAll(tablePtr, 0);
+           }
+           return TCL_OK;
 
-  case TAG_NAMES:
-    /* just print out the tag names */
-    if (argc != 3 && argc != 4) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " tag names ?pattern?\"", (char *) NULL);
-      return TCL_ERROR;
-    }
-    entryPtr = Tcl_FirstHashEntry(tablePtr->tagTable, &search);
-    while (entryPtr != NULL) {
-      keybuf = Tcl_GetHashKey(tablePtr->tagTable, entryPtr);
-      if (argc == 3 || Tcl_StringMatch(keybuf, argv[3]))
-       Tcl_AppendElement(interp, keybuf);
-      entryPtr = Tcl_NextHashEntry(&search);
     }
     return TCL_OK;
-  }
-  return TCL_OK;
-}
 
+    invalidtag:
+    /*
+     * When jumping here, ensure the invalid 'tagname' is set already.
+     */
+    Tcl_AppendStringsToObj(resultPtr, "invalid tag name \"",
+           tagname, "\"", (char *) NULL);
+    return TCL_ERROR;
+}
diff --git a/libgui/src/tkTableUtil.c b/libgui/src/tkTableUtil.c
new file mode 100644 (file)
index 0000000..29e2f14
--- /dev/null
@@ -0,0 +1,340 @@
+/* 
+ * tkTableUtil.c --
+ *
+ *     This module contains utility functions for table widgets.
+ *
+ * Copyright (c) 2000 Jeffrey Hobbs
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#include "tkTable.h"
+
+static char *  Cmd_GetName _ANSI_ARGS_((const Cmd_Struct *cmds, int val));
+static int     Cmd_GetValue _ANSI_ARGS_((const Cmd_Struct *cmds,
+                       const char *arg));
+static void    Cmd_GetError _ANSI_ARGS_((Tcl_Interp *interp,
+                       const Cmd_Struct *cmds, const char *arg));
+
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableOptionBdSet --
+ *
+ *     This routine configures the borderwidth value for a tag.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     It may adjust the tag struct values of bd[0..4] and borders.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TableOptionBdSet(clientData, interp, tkwin, value, widgRec, offset)
+    ClientData clientData;             /* Type of struct being set. */
+    Tcl_Interp *interp;                        /* Used for reporting errors. */
+    Tk_Window tkwin;                   /* Window containing table widget. */
+    char *value;                       /* Value of option. */
+    char *widgRec;                     /* Pointer to record for item. */
+    int offset;                                /* Offset into item. */
+{
+    char **borderStr;
+    int *bordersPtr, *bdPtr;
+    int type   = (int) clientData;
+    int result = TCL_OK;
+    int argc;
+    char **argv;
+
+
+    if ((type == BD_TABLE) && (value[0] == '\0')) {
+       /*
+        * NULL strings aren't allowed for the table global -bd
+        */
+       Tcl_AppendResult(interp, "borderwidth value may not be empty",
+               (char *) NULL);
+       return TCL_ERROR;
+    }
+
+    if ((type == BD_TABLE) || (type == BD_TABLE_TAG)) {
+       TableTag *tagPtr = (TableTag *) (widgRec + offset);
+       borderStr       = &(tagPtr->borderStr);
+       bordersPtr      = &(tagPtr->borders);
+       bdPtr           = tagPtr->bd;
+    } else if (type == BD_TABLE_WIN) {
+       TableEmbWindow *tagPtr = (TableEmbWindow *) widgRec;
+       borderStr       = &(tagPtr->borderStr);
+       bordersPtr      = &(tagPtr->borders);
+       bdPtr           = tagPtr->bd;
+    } else {
+       panic("invalid type given to TableOptionBdSet\n");
+       return TCL_ERROR; /* lint */
+    }
+
+    result = Tcl_SplitList(interp, value, &argc, &argv);
+    if (result == TCL_OK) {
+       int i, bd[4];
+
+       if (((type == BD_TABLE) && (argc == 0)) || (argc == 3) || (argc > 4)) {
+           Tcl_AppendResult(interp,
+                   "1, 2 or 4 values must be specified for borderwidth",
+                   (char *) NULL);
+           result = TCL_ERROR;
+       } else {
+           /*
+            * We use the shadow bd array first, in case we have an error
+            * parsing arguments half way through.
+            */
+           for (i = 0; i < argc; i++) {
+               if (Tk_GetPixels(interp, tkwin, argv[i], &(bd[i])) != TCL_OK) {
+                   result = TCL_ERROR;
+                   break;
+               }
+           }
+           /*
+            * If everything is OK, store the parsed and given values for
+            * easy retrieval.
+            */
+           if (result == TCL_OK) {
+               for (i = 0; i < argc; i++) {
+                   bdPtr[i] = MAX(0, bd[i]);
+               }
+               if (*borderStr) {
+                   ckfree(*borderStr);
+               }
+               if (value) {
+                   *borderStr  = (char *) ckalloc(strlen(value) + 1);
+                   strcpy(*borderStr, value);
+               } else {
+                   *borderStr  = NULL;
+               }
+               *bordersPtr     = argc;
+           }
+       }
+       ckfree ((char *) argv);
+    }
+
+    return result;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableOptionBdGet --
+ *
+ * Results:
+ *     Value of the -bd option.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+TableOptionBdGet(clientData, tkwin, widgRec, offset, freeProcPtr)
+    ClientData clientData;             /* Type of struct being set. */
+    Tk_Window tkwin;                   /* Window containing canvas widget. */
+    char *widgRec;                     /* Pointer to record for item. */
+    int offset;                                /* Offset into item. */
+    Tcl_FreeProc **freeProcPtr;                /* Pointer to variable to fill in with
+                                        * information about how to reclaim
+                                        * storage for return string. */
+{
+    register int type  = (int) clientData;
+
+    if (type == BD_TABLE) {
+       return ((TableTag *) (widgRec + offset))->borderStr;
+    } else if (type == BD_TABLE_TAG) {
+       return ((TableTag *) widgRec)->borderStr;
+    } else if (type == BD_TABLE_WIN) {
+       return ((TableEmbWindow *) widgRec)->borderStr;
+    } else {
+       panic("invalid type given to TableOptionBdSet\n");
+       return NULL; /* lint */
+    }
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * TableTagConfigureBd --
+ *     This routine configures the border values based on a tag.
+ *     The previous value of the bd string (oldValue) is assumed to
+ *     be a valid value for this tag.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     It may adjust the value used by -bd.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TableTagConfigureBd(Table *tablePtr, TableTag *tagPtr,
+       char *oldValue, int nullOK)
+{
+    int i, argc, result = TCL_OK;
+    char **argv;
+
+    /*
+     * First check to see if the value really changed.
+     */
+    if (strcmp(tagPtr->borderStr ? tagPtr->borderStr : "",
+           oldValue ? oldValue : "") == 0) {
+       return TCL_OK;
+    }
+
+    tagPtr->borders = 0;
+    if (!nullOK && ((tagPtr->borderStr == NULL)
+           || (*(tagPtr->borderStr) == '\0'))) {
+       /*
+        * NULL strings aren't allowed for this tag
+        */
+       result = TCL_ERROR;
+    } else if (tagPtr->borderStr) {
+       result = Tcl_SplitList(tablePtr->interp, tagPtr->borderStr,
+               &argc, &argv);
+       if (result == TCL_OK) {
+           if ((!nullOK && (argc == 0)) || (argc == 3) || (argc > 4)) {
+               Tcl_SetResult(tablePtr->interp,
+                       "1, 2 or 4 values must be specified to -borderwidth",
+                       TCL_STATIC);
+               result = TCL_ERROR;
+           } else {
+               for (i = 0; i < argc; i++) {
+                   if (Tk_GetPixels(tablePtr->interp, tablePtr->tkwin,
+                           argv[i], &(tagPtr->bd[i])) != TCL_OK) {
+                       result = TCL_ERROR;
+                       break;
+                   }
+                   tagPtr->bd[i] = MAX(0, tagPtr->bd[i]);
+               }
+               tagPtr->borders = argc;
+           }
+           ckfree ((char *) argv);
+       }
+    }
+
+    if (result != TCL_OK) {
+       if (tagPtr->borderStr) {
+           ckfree ((char *) tagPtr->borderStr);
+       }
+       if (oldValue != NULL) {
+           size_t length = strlen(oldValue) + 1;
+           /*
+            * We are making the assumption that oldValue is correct.
+            * We have to reparse in case the bad new value had a couple
+            * of correct args before failing on a bad pixel value.
+            */
+           Tcl_SplitList(tablePtr->interp, oldValue, &argc, &argv);
+           for (i = 0; i < argc; i++) {
+               Tk_GetPixels(tablePtr->interp, tablePtr->tkwin,
+                       argv[i], &(tagPtr->bd[i]));
+           }
+           ckfree ((char *) argv);
+           tagPtr->borders     = argc;
+           tagPtr->borderStr   = (char *) ckalloc(length);
+           memcpy(tagPtr->borderStr, oldValue, length);
+       } else {
+           tagPtr->borders     = 0;
+           tagPtr->borderStr   = (char *) NULL;
+       }
+    }
+
+    return result;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Cmd_OptionSet --
+ *
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Cmd_OptionSet(ClientData clientData, Tcl_Interp *interp,
+             Tk_Window unused, char *value, char *widgRec, int offset)
+{
+  Cmd_Struct *p = (Cmd_Struct *)clientData;
+  int mode = Cmd_GetValue(p,value);
+  if (!mode) {
+    Cmd_GetError(interp,p,value);
+    return TCL_ERROR;
+  }
+  *((int*)(widgRec+offset)) = mode;
+  return TCL_OK;
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Cmd_OptionGet --
+ *
+ *
+ * Results:
+ *     Value of the option.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+Cmd_OptionGet(ClientData clientData, Tk_Window unused,
+             char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)
+{
+  Cmd_Struct *p = (Cmd_Struct *)clientData;
+  int mode = *((int*)(widgRec+offset));
+  return Cmd_GetName(p,mode);
+}
+
+/*
+ * simple Cmd_Struct lookup functions
+ */
+
+char *
+Cmd_GetName(const Cmd_Struct *cmds, int val)
+{
+  for(;cmds->name && cmds->name[0];cmds++) {
+    if (cmds->value==val) return cmds->name;
+  }
+  return NULL;
+}
+
+int
+Cmd_GetValue(const Cmd_Struct *cmds, const char *arg)
+{
+  unsigned int len = strlen(arg);
+  for(;cmds->name && cmds->name[0];cmds++) {
+    if (!strncmp(cmds->name, arg, len)) return cmds->value;
+  }
+  return 0;
+}
+
+void
+Cmd_GetError(Tcl_Interp *interp, const Cmd_Struct *cmds, const char *arg)
+{
+  int i;
+  Tcl_AppendResult(interp, "bad option \"", arg, "\" must be ", (char *) 0);
+  for(i=0;cmds->name && cmds->name[0];cmds++,i++) {
+    Tcl_AppendResult(interp, (i?", ":""), cmds->name, (char *) 0);
+  }
+}
index b5f337c..531e258 100644 (file)
@@ -4,11 +4,12 @@
  *     This module implements embedded windows for table widgets.
  *     Much of this code is adapted from tkGrid.c and tkTextWind.c.
  *
- * Copyright (c) 1998 Jeffrey Hobbs
+ * Copyright (c) 1998-2000 Jeffrey Hobbs
  *
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  *
+ * RCS: @(#) $Id$
  */
 
 #include "tkTable.h"
@@ -29,7 +30,7 @@ static void   EmbWinCleanup _ANSI_ARGS_((Table *tablePtr,
                                           TableEmbWindow *ewPtr));
 static int     EmbWinConfigure _ANSI_ARGS_((Table *tablePtr,
                                             TableEmbWindow *ewPtr,
-                                            int argc, char **argv));
+                                            int objc, Tcl_Obj *CONST objv[]));
 static void    EmbWinStructureProc _ANSI_ARGS_((ClientData clientData,
                                                 XEvent *eventPtr));
 static void    EmbWinUnmapNow _ANSI_ARGS_((Tk_Window ewTkwin,
@@ -42,18 +43,11 @@ static Tk_GeomMgr tableGeomType = {
 };
 
 /* windows subcommands */
-#define WIN_CGET       1       /* get config item of embedded window */
-#define WIN_CONFIGURE  2       /* configure an embedded window */
-#define WIN_DELETE     3       /* delete an embedded window */
-#define WIN_MOVE       4       /* moves a window index */
-#define        WIN_NAMES       5       /* print the embedded window names */
-static Cmd_Struct win_cmds[] = {
-  {"configure",        WIN_CONFIGURE},
-  {"cget",     WIN_CGET},
-  {"delete",   WIN_DELETE},
-  {"move",     WIN_MOVE},
-  {"names",    WIN_NAMES},
-  {"", 0}
+static char *winCmdNames[] = {
+    "cget", "configure", "delete", "move", "names", (char *) NULL
+};
+enum winCommand {
+    WIN_CGET, WIN_CONFIGURE, WIN_DELETE, WIN_MOVE, WIN_NAMES
 };
 
 /* Flag values for "sticky"ness  The 16 combinations subsume the packer's
@@ -75,32 +69,37 @@ static Cmd_Struct win_cmds[] = {
  * Done like this to make the command line parsing easy
  */
 
-static Tk_CustomOption stickyOption = {StickyParseProc, StickyPrintProc,
-                                      (ClientData) NULL};
+static Tk_CustomOption stickyOption    = { StickyParseProc, StickyPrintProc,
+                                           (ClientData) NULL };
+static Tk_CustomOption tagBdOpt                = { TableOptionBdSet, TableOptionBdGet,
+                                           (ClientData) BD_TABLE_WIN };
 
 static Tk_ConfigSpec winConfigSpecs[] = {
   {TK_CONFIG_BORDER, "-background", "background", "Background", NULL,
    Tk_Offset(TableEmbWindow, bg),
    TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
-  {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
-   (char *) NULL, 0, 0 },
-  {TK_CONFIG_STRING, "-create", (char *) NULL, (char *) NULL, (char *) NULL,
+  {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
+  {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
+  {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth", "",
+   0 /* no offset */,
+   TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK, &tagBdOpt },
+  {TK_CONFIG_STRING, "-create", (char *)NULL, (char *)NULL, (char *)NULL,
    Tk_Offset(TableEmbWindow, create),
    TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
-  {TK_CONFIG_PIXELS, "-padx", (char *) NULL, (char *) NULL, (char *) NULL,
+  {TK_CONFIG_PIXELS, "-padx", (char *)NULL, (char *)NULL, (char *)NULL,
    Tk_Offset(TableEmbWindow, padX), TK_CONFIG_DONT_SET_DEFAULT },
-  {TK_CONFIG_PIXELS, "-pady", (char *) NULL, (char *) NULL, (char *) NULL,
+  {TK_CONFIG_PIXELS, "-pady", (char *)NULL, (char *)NULL, (char *)NULL,
    Tk_Offset(TableEmbWindow, padY), TK_CONFIG_DONT_SET_DEFAULT },
-  {TK_CONFIG_CUSTOM, "-sticky", (char *) NULL, (char *) NULL, (char *) NULL,
+  {TK_CONFIG_CUSTOM, "-sticky", (char *)NULL, (char *)NULL, (char *)NULL,
    Tk_Offset(TableEmbWindow, sticky), TK_CONFIG_DONT_SET_DEFAULT,
    &stickyOption},
   {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", NULL,
    Tk_Offset(TableEmbWindow, relief), 0 },
-  {TK_CONFIG_WINDOW, "-window", (char *) NULL, (char *) NULL, (char *) NULL,
+  {TK_CONFIG_WINDOW, "-window", (char *)NULL, (char *)NULL, (char *)NULL,
    Tk_Offset(TableEmbWindow, tkwin),
    TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
-  {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
-   (char *) NULL, 0, 0 }
+  {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
+   (char *)NULL, 0, 0 }
 };
 
 /*
@@ -129,18 +128,18 @@ StickyPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)
                                         * information about how to reclaim
                                         * storage for return string. */
 {
-  int flags = ((TableEmbWindow *) widgRec)->sticky;
-  int count = 0;
-  char *result = (char *) ckalloc(5*sizeof(char));
-
-  if (flags&STICK_NORTH) result[count++] = 'n';
-  if (flags&STICK_EAST)  result[count++] = 'e';
-  if (flags&STICK_SOUTH) result[count++] = 's';
-  if (flags&STICK_WEST)  result[count++] = 'w';
-
-  *freeProcPtr = TCL_DYNAMIC;
-  result[count] = '\0';
-  return result;
+    int flags = ((TableEmbWindow *) widgRec)->sticky;
+    int count = 0;
+    char *result = (char *) ckalloc(5*sizeof(char));
+
+    if (flags&STICK_NORTH) result[count++] = 'n';
+    if (flags&STICK_EAST)  result[count++] = 'e';
+    if (flags&STICK_SOUTH) result[count++] = 's';
+    if (flags&STICK_WEST)  result[count++] = 'w';
+
+    *freeProcPtr = TCL_DYNAMIC;
+    result[count] = '\0';
+    return result;
 }
 
 /*
@@ -169,26 +168,27 @@ StickyParseProc(clientData, interp, tkwin, value, widgRec, offset)
                                         * structure. */
     int offset;                                /* Offset into item (ignored). */
 {
-  register TableEmbWindow *ewPtr = (TableEmbWindow *) widgRec;
-  int sticky = 0;
-  char c;
-
-  while ((c = *value++) != '\0') {
-    switch (c) {
-    case 'n': case 'N': sticky |= STICK_NORTH; break;
-    case 'e': case 'E': sticky |= STICK_EAST;  break;
-    case 's': case 'S': sticky |= STICK_SOUTH; break;
-    case 'w': case 'W': sticky |= STICK_WEST;  break;
-    case ' ': case ',': case '\t': case '\r': case '\n': break;
-    default:
-      Tcl_AppendResult(interp, "bad sticky value \"", --value,
-                      "\": must contain n, s, e or w",
-                      (char *) NULL);
-      return TCL_ERROR;
+    register TableEmbWindow *ewPtr = (TableEmbWindow *) widgRec;
+    int sticky = 0;
+    char c;
+
+    while ((c = *value++) != '\0') {
+       switch (c) {
+       case 'n': case 'N': sticky |= STICK_NORTH; break;
+       case 'e': case 'E': sticky |= STICK_EAST;  break;
+       case 's': case 'S': sticky |= STICK_SOUTH; break;
+       case 'w': case 'W': sticky |= STICK_WEST;  break;
+       case ' ': case ',': case '\t': case '\r': case '\n': break;
+       default:
+           Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+                                  "bad sticky value \"", --value,
+                                  "\": must contain n, s, e or w",
+                                  (char *) NULL);
+           return TCL_ERROR;
+       }
     }
-  }
-  ewPtr->sticky = sticky;
-  return TCL_OK;
+    ewPtr->sticky = sticky;
+    return TCL_OK;
 }              
 
 /*
@@ -198,18 +198,18 @@ StickyParseProc(clientData, interp, tkwin, value, widgRec, offset)
 static TableEmbWindow *
 TableNewEmbWindow(Table *tablePtr)
 {
-  TableEmbWindow *ewPtr = (TableEmbWindow *) ckalloc(sizeof(TableEmbWindow));
-  ewPtr->tablePtr      = tablePtr;
-  ewPtr->tkwin         = NULL;
-  ewPtr->hPtr          = NULL;
-  ewPtr->bg            = NULL;
-  ewPtr->create                = NULL;
-  ewPtr->relief                = -1;
-  ewPtr->sticky                = 0;
-  ewPtr->padX          = 0;
-  ewPtr->padY          = 0;
-  ewPtr->displayed     = 0;
-  return ewPtr;
+    TableEmbWindow *ewPtr = (TableEmbWindow *) ckalloc(sizeof(TableEmbWindow));
+    memset((VOID *) ewPtr, 0, sizeof(TableEmbWindow));
+
+    /*
+     * Set the values that aren't 0/NULL by default
+     */
+    ewPtr->tablePtr    = tablePtr;
+    ewPtr->relief      = -1;
+    ewPtr->padX                = -1;
+    ewPtr->padY                = -1;
+
+    return ewPtr;
 }
 
 /* 
@@ -229,8 +229,7 @@ TableNewEmbWindow(Table *tablePtr)
 static void
 EmbWinCleanup(Table *tablePtr, TableEmbWindow *ewPtr)
 {
-  /* free the options in the widget */
-  Tk_FreeOptions(winConfigSpecs, (char *) ewPtr, tablePtr->display, 0);
+    Tk_FreeOptions(winConfigSpecs, (char *) ewPtr, tablePtr->display, 0);
 }
 
 /*
@@ -253,63 +252,75 @@ void
 EmbWinDisplay(Table *tablePtr, Drawable window, TableEmbWindow *ewPtr,
              TableTag *tagPtr, int x, int y, int width, int height)
 {
-  Tk_Window tkwin = tablePtr->tkwin;
-  Tk_Window ewTkwin = ewPtr->tkwin;
-  int diffx=0; /* Cavity width - slave width. */
-  int diffy=0; /* Cavity hight - slave height. */
-  int sticky = ewPtr->sticky;
-
-
-  if (ewPtr->bg)
-    tagPtr->bg = ewPtr->bg;
-  if (ewPtr->relief != -1)
-    tagPtr->relief = ewPtr->relief;
-
-  x += ewPtr->padX/2;
-  width -= ewPtr->padX;
-  y += ewPtr->padY/2;
-  height -= ewPtr->padY;
-
-  if (width > Tk_ReqWidth(ewPtr->tkwin)) {
-    diffx = width - Tk_ReqWidth(ewPtr->tkwin);
-    width = Tk_ReqWidth(ewPtr->tkwin);
-  }
-  if (height > Tk_ReqHeight(ewPtr->tkwin)) {
-    diffy = height - Tk_ReqHeight(ewPtr->tkwin);
-    height = Tk_ReqHeight(ewPtr->tkwin);
-  }
-  if (sticky&STICK_EAST && sticky&STICK_WEST) {
-    width += diffx;
-  }
-  if (sticky&STICK_NORTH && sticky&STICK_SOUTH) {
-    height += diffy;
-  }
-  if (!(sticky&STICK_WEST)) {
-    x += (sticky&STICK_EAST) ? diffx : diffx/2;
-  }
-  if (!(sticky&STICK_NORTH)) {
-    y += (sticky&STICK_SOUTH) ? diffy : diffy/2;
-  }
-
-  /* If we fall below a specific minimum width/height requirement,
-   * we just unmap the window */
-  if (width < 4 || height < 4) {
-    if (ewPtr->displayed) {
-      EmbWinUnmapNow(ewTkwin, tkwin);
+    Tk_Window tkwin = tablePtr->tkwin;
+    Tk_Window ewTkwin = ewPtr->tkwin;
+    int diffx=0;       /* Cavity width - slave width. */
+    int diffy=0;       /* Cavity hight - slave height. */
+    int sticky = ewPtr->sticky;
+    int padx, pady;
+
+    if (ewPtr->bg)             tagPtr->bg      = ewPtr->bg;
+    if (ewPtr->relief != -1)   tagPtr->relief  = ewPtr->relief;
+    if (ewPtr->borders) {
+       tagPtr->borderStr       = ewPtr->borderStr;
+       tagPtr->borders         = ewPtr->borders;
+       tagPtr->bd[0]           = ewPtr->bd[0];
+       tagPtr->bd[1]           = ewPtr->bd[1];
+       tagPtr->bd[2]           = ewPtr->bd[2];
+       tagPtr->bd[3]           = ewPtr->bd[3];
+    }
+
+    padx = (ewPtr->padX < 0) ? tablePtr->padX : ewPtr->padX;
+    pady = (ewPtr->padY < 0) ? tablePtr->padY : ewPtr->padY;
+
+    x          += padx;
+    width      -= padx*2;
+    y          += pady;
+    height     -= pady*2;
+
+    if (width > Tk_ReqWidth(ewPtr->tkwin)) {
+       diffx = width - Tk_ReqWidth(ewPtr->tkwin);
+       width = Tk_ReqWidth(ewPtr->tkwin);
+    }
+    if (height > Tk_ReqHeight(ewPtr->tkwin)) {
+       diffy = height - Tk_ReqHeight(ewPtr->tkwin);
+       height = Tk_ReqHeight(ewPtr->tkwin);
+    }
+    if (sticky&STICK_EAST && sticky&STICK_WEST) {
+       width += diffx;
+    }
+    if (sticky&STICK_NORTH && sticky&STICK_SOUTH) {
+       height += diffy;
+    }
+    if (!(sticky&STICK_WEST)) {
+       x += (sticky&STICK_EAST) ? diffx : diffx/2;
+    }
+    if (!(sticky&STICK_NORTH)) {
+       y += (sticky&STICK_SOUTH) ? diffy : diffy/2;
+    }
+
+    /*
+     * If we fall below a specific minimum width/height requirement,
+     * we just unmap the window
+     */
+    if (width < 4 || height < 4) {
+       if (ewPtr->displayed) {
+           EmbWinUnmapNow(ewTkwin, tkwin);
+       }
+       return;
     }
-    return;
-  }
 
-  if (tkwin == Tk_Parent(ewTkwin)) {
-    if ((x != Tk_X(ewTkwin)) || (y != Tk_Y(ewTkwin))
-       || (width != Tk_Width(ewTkwin)) || (height != Tk_Height(ewTkwin))) {
-      Tk_MoveResizeWindow(ewTkwin, x, y, width, height);
+    if (tkwin == Tk_Parent(ewTkwin)) {
+       if ((x != Tk_X(ewTkwin)) || (y != Tk_Y(ewTkwin))
+           || (width != Tk_Width(ewTkwin))
+           || (height != Tk_Height(ewTkwin))) {
+           Tk_MoveResizeWindow(ewTkwin, x, y, width, height);
+       }
+       Tk_MapWindow(ewTkwin);
+    } else {
+       Tk_MaintainGeometry(ewTkwin, tkwin, x, y, width, height);
     }
-    Tk_MapWindow(ewTkwin);
-  } else {
-    Tk_MaintainGeometry(ewTkwin, tkwin, x, y, width, height);
-  }
-  ewPtr->displayed = 1;
+    ewPtr->displayed = 1;
 }
 
 /*
@@ -331,11 +342,10 @@ EmbWinDisplay(Table *tablePtr, Drawable window, TableEmbWindow *ewPtr,
 static void
 EmbWinUnmapNow(Tk_Window ewTkwin, Tk_Window tkwin)
 {
-  if (tkwin != Tk_Parent(ewTkwin)) {
-    Tk_UnmaintainGeometry(ewTkwin, tkwin);
-  } else {
+    if (tkwin != Tk_Parent(ewTkwin)) {
+       Tk_UnmaintainGeometry(ewTkwin, tkwin);
+    }
     Tk_UnmapWindow(ewTkwin);
-  }
 }
 
 /*
@@ -357,30 +367,34 @@ EmbWinUnmapNow(Tk_Window ewTkwin, Tk_Window tkwin)
 void
 EmbWinUnmap(Table *tablePtr, int rlo, int rhi, int clo, int chi)
 {
-  register TableEmbWindow *ewPtr;
-  Tcl_HashEntry *entryPtr;
-  int row, col;
-  char buf[INDEX_BUFSIZE];
-
-  /* we need to deal with things user coords */
-  rlo += tablePtr->rowOffset;
-  rhi += tablePtr->rowOffset;
-  clo += tablePtr->colOffset;
-  chi += tablePtr->colOffset;
-  for (row = rlo; row <= rhi; row++) {
-    for (col = clo; col <= chi; col++) {
-      TableMakeArrayIndex(row, col, buf);
-      if ((entryPtr = Tcl_FindHashEntry(tablePtr->winTable, buf)) != NULL) {
-       ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
-       if (ewPtr->displayed) {
-         ewPtr->displayed = 0;
-         if (ewPtr->tkwin != NULL && tablePtr->tkwin != NULL) {
-           EmbWinUnmapNow(ewPtr->tkwin, tablePtr->tkwin);
-         }
+    register TableEmbWindow *ewPtr;
+    Tcl_HashEntry *entryPtr;
+    int row, col, trow, tcol;
+    char buf[INDEX_BUFSIZE];
+
+    /*
+     * Transform numbers from real to user user coords
+     */
+    rlo += tablePtr->rowOffset;
+    rhi += tablePtr->rowOffset;
+    clo += tablePtr->colOffset;
+    chi += tablePtr->colOffset;
+    for (row = rlo; row <= rhi; row++) {
+       for (col = clo; col <= chi; col++) {
+           TableTrueCell(tablePtr, row, col, &trow, &tcol);
+           TableMakeArrayIndex(trow, tcol, buf);
+           entryPtr = Tcl_FindHashEntry(tablePtr->winTable, buf);
+           if (entryPtr != NULL) {
+               ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
+               if (ewPtr->displayed) {
+                   ewPtr->displayed = 0;
+                   if (ewPtr->tkwin != NULL && tablePtr->tkwin != NULL) {
+                       EmbWinUnmapNow(ewPtr->tkwin, tablePtr->tkwin);
+                   }
+               }
+           }
        }
-      }
     }
-  }
 }
 
 /*
@@ -407,38 +421,53 @@ EmbWinRequestProc(clientData, tkwin)
     Tk_Window tkwin;           /* Other Tk-related information
                                 * about the window. */
 {
-  register TableEmbWindow *ewPtr = (TableEmbWindow *) clientData;
-
-  /* resize depends on the sticky */
-  if (ewPtr->displayed && ewPtr->hPtr != NULL) {
-    Table *tablePtr = ewPtr->tablePtr;
-    int row, col, x, y, width, height;
+    register TableEmbWindow *ewPtr = (TableEmbWindow *) clientData;
 
-    TableParseArrayIndex(&row, &col,
-                        Tcl_GetHashKey(tablePtr->winTable, ewPtr->hPtr));
-    if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
-                    col-tablePtr->colOffset, &x, &y, &width, &height, 0)) {
-      TableInvalidate(tablePtr, x, y, width, height, 0);
+    /*
+     * Resize depends on the sticky
+     */
+    if (ewPtr->displayed && ewPtr->hPtr != NULL) {
+       Table *tablePtr = ewPtr->tablePtr;
+       int row, col, x, y, width, height;
+
+       TableParseArrayIndex(&row, &col,
+                            Tcl_GetHashKey(tablePtr->winTable, ewPtr->hPtr));
+       if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
+                            col-tablePtr->colOffset, &x, &y, &width, &height,
+                            0)) {
+           TableInvalidate(tablePtr, x, y, width, height, 0);
+       }
     }
-  }
 }
 
 static void
 EmbWinRemove(TableEmbWindow *ewPtr)
 {
-  Table *tablePtr = ewPtr->tablePtr;
-
-  ewPtr->tkwin = NULL;
-  ewPtr->displayed = 0;
-  if (tablePtr->tkwin != NULL) {
-    int row, col, x, y, width, height;
-
-    TableParseArrayIndex(&row, &col,
-                        Tcl_GetHashKey(tablePtr->winTable, ewPtr->hPtr));
-    if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
-                        col-tablePtr->colOffset, &x, &y, &width, &height, 0))
-      TableInvalidate(tablePtr, x, y, width, height, 1);
-  }
+    Table *tablePtr = ewPtr->tablePtr;
+
+    if (ewPtr->tkwin != NULL) {
+       Tk_DeleteEventHandler(ewPtr->tkwin, StructureNotifyMask,
+                             EmbWinStructureProc, (ClientData) ewPtr);
+       ewPtr->tkwin = NULL;
+    }
+    ewPtr->displayed = 0;
+    if (tablePtr->tkwin != NULL) {
+       int row, col, x, y, width, height;
+
+       TableParseArrayIndex(&row, &col,
+                            Tcl_GetHashKey(tablePtr->winTable, ewPtr->hPtr));
+       /* this will cause windows removed from the table to actually
+        * cause the associated embdedded window hash data to be removed */
+       Tcl_DeleteHashEntry(ewPtr->hPtr);
+       if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
+                            col-tablePtr->colOffset, &x, &y, &width, &height,
+                            0))
+           TableInvalidate(tablePtr, x, y, width, height, 1);
+    }
+    /* this will cause windows removed from the table to actually
+     * cause the associated embdedded window hash data to be removed */
+    EmbWinCleanup(tablePtr, ewPtr);
+    ckfree((char *) ewPtr);
 }
 
 /*
@@ -465,8 +494,6 @@ EmbWinLostSlaveProc(clientData, tkwin)
 {
     register TableEmbWindow *ewPtr = (TableEmbWindow *) clientData;
 
-    Tk_DeleteEventHandler(ewPtr->tkwin, StructureNotifyMask,
-                         EmbWinStructureProc, (ClientData) ewPtr);
 #if 0
     Tcl_CancelIdleCall(EmbWinDelayedUnmap, (ClientData) ewPtr);
 #endif
@@ -497,13 +524,13 @@ EmbWinStructureProc(clientData, eventPtr)
     ClientData clientData;     /* Pointer to record describing window item. */
     XEvent *eventPtr;          /* Describes what just happened. */
 {
-  register TableEmbWindow *ewPtr = (TableEmbWindow *) clientData;
+    register TableEmbWindow *ewPtr = (TableEmbWindow *) clientData;
 
-  if (eventPtr->type != DestroyNotify) {
-    return;
-  }
+    if (eventPtr->type != DestroyNotify) {
+       return;
+    }
 
-  EmbWinRemove(ewPtr);
+    EmbWinRemove(ewPtr);
 }
 
 /*
@@ -525,39 +552,38 @@ EmbWinStructureProc(clientData, eventPtr)
 void
 EmbWinDelete(register Table *tablePtr, TableEmbWindow *ewPtr)
 {
-  Tcl_HashEntry *entryPtr;
-
-  if (ewPtr->tkwin != NULL) {
-    int row, col, x, y, width, height;
-    entryPtr = ewPtr->hPtr;
-
-    /*
-     * Delete the event handler for the window before destroying
-     * the window, so that EmbWinStructureProc doesn't get called
-     * (we'll already do everything that it would have done, and
-     * it will just get confused).
-     */
-
-    Tk_DeleteEventHandler(ewPtr->tkwin, StructureNotifyMask,
-                         EmbWinStructureProc, (ClientData) ewPtr);
-    Tk_DestroyWindow(ewPtr->tkwin);
+    Tcl_HashEntry *entryPtr = ewPtr->hPtr;
 
+    if (ewPtr->tkwin != NULL) {
+       Tk_Window tkwin = ewPtr->tkwin;
+       /*
+        * Delete the event handler for the window before destroying
+        * the window, so that EmbWinStructureProc doesn't get called
+        * (we'll already do everything that it would have done, and
+        * it will just get confused).
+        */
+
+       ewPtr->tkwin = NULL;
+       Tk_DeleteEventHandler(tkwin, StructureNotifyMask,
+                             EmbWinStructureProc, (ClientData) ewPtr);
+       Tk_DestroyWindow(tkwin);
+    }
     if (tablePtr->tkwin != NULL && entryPtr != NULL) {
-      TableParseArrayIndex(&row, &col,
-                          Tcl_GetHashKey(tablePtr->winTable, entryPtr));
-      Tcl_DeleteHashEntry(entryPtr);
-
-      if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
-                          col-tablePtr->colOffset,
-                          &x, &y, &width, &height, 0))
-       TableInvalidate(tablePtr, x, y, width, height, 0);
+       int row, col, x, y, width, height;
+       TableParseArrayIndex(&row, &col,
+                            Tcl_GetHashKey(tablePtr->winTable, entryPtr));
+       Tcl_DeleteHashEntry(entryPtr);
+
+       if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
+                            col-tablePtr->colOffset,
+                            &x, &y, &width, &height, 0))
+           TableInvalidate(tablePtr, x, y, width, height, 0);
     }
-  }
 #if 0
-  Tcl_CancelIdleCall(EmbWinDelayedUnmap, (ClientData) ewPtr);
+    Tcl_CancelIdleCall(EmbWinDelayedUnmap, (ClientData) ewPtr);
 #endif
-  EmbWinCleanup(tablePtr, ewPtr);
-  ckfree((char *) ewPtr);
+    EmbWinCleanup(tablePtr, ewPtr);
+    ckfree((char *) ewPtr);
 }
 
 /*
@@ -565,7 +591,7 @@ EmbWinDelete(register Table *tablePtr, TableEmbWindow *ewPtr)
  *
  * EmbWinConfigure --
  *     This procedure is called to handle configuration options
- *     for an embedded window, using an argc/argv list.
+ *     for an embedded window.
  *
  * Results:
  *     The return value is a standard Tcl result.  If TCL_ERROR is
@@ -579,278 +605,351 @@ EmbWinDelete(register Table *tablePtr, TableEmbWindow *ewPtr)
  *--------------------------------------------------------------
  */
 static int
-EmbWinConfigure(tablePtr, ewPtr, argc, argv)
+EmbWinConfigure(tablePtr, ewPtr, objc, objv)
      Table *tablePtr;          /* Information about table widget that
                                 * contains embedded window. */
      TableEmbWindow *ewPtr;    /* Embedded window to be configured. */
-     int argc;                 /* Number of strings in argv. */
-     char **argv;              /* Array of strings describing configuration
-                                * options. */
+     int objc;                 /* Number of objs in objv. */
+     Tcl_Obj *CONST objv[];    /* Obj type options. */
 {
-  Tk_Window oldWindow;
-
-  oldWindow = ewPtr->tkwin;
-  if (Tk_ConfigureWidget(tablePtr->interp, tablePtr->tkwin, winConfigSpecs,
-                        argc, argv, (char *) ewPtr, TK_CONFIG_ARGV_ONLY)
-      != TCL_OK) {
-    return TCL_ERROR;
-  }
-  if (oldWindow != ewPtr->tkwin) {
-    ewPtr->displayed = 0;
-    if (oldWindow != NULL) {
-      Tk_DeleteEventHandler(oldWindow, StructureNotifyMask,
-                           EmbWinStructureProc, (ClientData) ewPtr);
-      Tk_ManageGeometry(oldWindow, (Tk_GeomMgr *) NULL,
-                       (ClientData) NULL);
-      EmbWinUnmapNow(oldWindow, tablePtr->tkwin);
+    Tcl_Interp *interp = tablePtr->interp;
+    Tk_Window oldWindow;
+    int i, result;
+    char **argv;
+
+    oldWindow = ewPtr->tkwin;
+
+    /* Stringify */
+    argv = (char **) ckalloc((objc + 1) * sizeof(char *));
+    for (i = 0; i < objc; i++)
+       argv[i] = Tcl_GetString(objv[i]);
+    argv[i] = NULL;
+    result = Tk_ConfigureWidget(interp, tablePtr->tkwin,
+                               winConfigSpecs, objc, argv, (char *) ewPtr,
+                               TK_CONFIG_ARGV_ONLY);
+    ckfree((char *) argv);
+    if (result != TCL_OK) {
+       return TCL_ERROR;
     }
-    if (ewPtr->tkwin != NULL) {
-      Tk_Window ancestor, parent;
-
-      /*
-       * Make sure that the table is either the parent of the
-       * embedded window or a descendant of that parent.  Also,
-       * don't allow a top-level window to be managed inside
-       * a table.
-       */
-
-      parent = Tk_Parent(ewPtr->tkwin);
-      for (ancestor = tablePtr->tkwin; ;
-          ancestor = Tk_Parent(ancestor)) {
-       if (ancestor == parent) {
-         break;
+
+    if (oldWindow != ewPtr->tkwin) {
+       ewPtr->displayed = 0;
+       if (oldWindow != NULL) {
+           Tk_DeleteEventHandler(oldWindow, StructureNotifyMask,
+                                 EmbWinStructureProc, (ClientData) ewPtr);
+           Tk_ManageGeometry(oldWindow, (Tk_GeomMgr *) NULL,
+                             (ClientData) NULL);
+           EmbWinUnmapNow(oldWindow, tablePtr->tkwin);
        }
-       if (Tk_IsTopLevel(ancestor)) {
-       badMaster:
-         Tcl_AppendResult(tablePtr->interp, "can't embed ",
-                          Tk_PathName(ewPtr->tkwin), " in ",
-                          Tk_PathName(tablePtr->tkwin), (char *) NULL);
-         ewPtr->tkwin = NULL;
-         return TCL_ERROR;
+       if (ewPtr->tkwin != NULL) {
+           Tk_Window ancestor, parent;
+
+           /*
+            * Make sure that the table is either the parent of the
+            * embedded window or a descendant of that parent.  Also,
+            * don't allow a top-level window to be managed inside
+            * a table.
+            */
+
+           parent = Tk_Parent(ewPtr->tkwin);
+           for (ancestor = tablePtr->tkwin; ;
+                ancestor = Tk_Parent(ancestor)) {
+               if (ancestor == parent) {
+                   break;
+               }
+               if (Tk_IsTopLevel(ancestor)) {
+               badMaster:
+                   Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+                                          "can't embed ",
+                                          Tk_PathName(ewPtr->tkwin), " in ",
+                                          Tk_PathName(tablePtr->tkwin),
+                                          (char *)NULL);
+                   ewPtr->tkwin = NULL;
+                   return TCL_ERROR;
+               }
+           }
+           if (Tk_IsTopLevel(ewPtr->tkwin) ||
+               (ewPtr->tkwin == tablePtr->tkwin)) {
+               goto badMaster;
+           }
+
+           /*
+            * Take over geometry management for the window, plus create
+            * an event handler to find out when it is deleted.
+            */
+
+           Tk_ManageGeometry(ewPtr->tkwin, &tableGeomType, (ClientData)ewPtr);
+           Tk_CreateEventHandler(ewPtr->tkwin, StructureNotifyMask,
+                                 EmbWinStructureProc, (ClientData) ewPtr);
        }
-      }
-      if (Tk_IsTopLevel(ewPtr->tkwin) || (ewPtr->tkwin == tablePtr->tkwin)) {
-       goto badMaster;
-      }
-
-      /*
-       * Take over geometry management for the window, plus create
-       * an event handler to find out when it is deleted.
-       */
-
-      Tk_ManageGeometry(ewPtr->tkwin, &tableGeomType, (ClientData) ewPtr);
-      Tk_CreateEventHandler(ewPtr->tkwin, StructureNotifyMask,
-                           EmbWinStructureProc, (ClientData) ewPtr);
     }
-  }
-  return TCL_OK;
+    return TCL_OK;
 }
 
 /*
  *--------------------------------------------------------------
  *
- * TableWindowCmd --
- *     This procedure is invoked to process the window method
- *     that corresponds to a widget managed by this module.
- *     See the user documentation for details on what it does.
+ * Table_WinMove --
+ *     This procedure is invoked by ... whenever
+ *     an embedded window is being moved.
  *
  * Results:
  *     A standard Tcl result.
  *
  * Side effects:
- *     See the user documentation.
+ *     If an embedded window is in the dest cell, it is deleted.
  *
  *--------------------------------------------------------------
  */
 int
-TableWindowCmd(Table * tablePtr, register Tcl_Interp *interp,
-              int argc, char *argv[])
+Table_WinMove(register Table *tablePtr, char *CONST srcPtr,
+          char *CONST destPtr, int flags)
 {
-  int result = TCL_OK, retval;
-  int row, col, x, y, width, height, i, new;
-  TableEmbWindow *ewPtr;
-  Tcl_HashEntry *entryPtr;
-  Tcl_HashSearch search;
-  char buf[INDEX_BUFSIZE], *keybuf;
-
-  /* parse the next argument */
-  retval = Cmd_Parse(interp, win_cmds, argv[2]);
-  switch (retval) {
-    /* failed to parse the argument, error */
-  case 0:
-    return TCL_ERROR;
-
-  case WIN_CGET:
-    if (argc != 5) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"",
-                       argv[0], " window cget index option\"", (char *) NULL);
-      return TCL_ERROR;
-    }
-    if ((entryPtr=Tcl_FindHashEntry(tablePtr->winTable, argv[3])) == NULL) {
-      Tcl_AppendResult(interp, "no window at index \"", argv[3],
-                      "\"", (char *) NULL);
-      return TCL_ERROR;
-    } else {
-      ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
-      result = Tk_ConfigureValue(interp, tablePtr->tkwin, winConfigSpecs,
-                                (char *) ewPtr, argv[4], 0);
-    }
-    return result;     /* CGET */
-
-  case WIN_CONFIGURE:
-    if (argc < 4) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"",
-                      argv[0], " window configure index ?arg arg  ...?\"",
-                      (char *) NULL);
-      return TCL_ERROR;
-    }
-    if (TableGetIndex(tablePtr, argv[3], &row, &col) == TCL_ERROR) {
-      return TCL_ERROR;
-    }
-    TableMakeArrayIndex(row, col, buf);
-    entryPtr = Tcl_CreateHashEntry(tablePtr->winTable, buf, &new);
-    if (new) {
-      /* create the structure */
-      ewPtr = TableNewEmbWindow(tablePtr);
-
-      /* insert it into the table */
-      Tcl_SetHashValue(entryPtr, (ClientData) ewPtr);
-      ewPtr->hPtr = entryPtr;
-
-      /* configure the window structure */
-      result = EmbWinConfigure(tablePtr, ewPtr, argc-4, argv+4);
-      if (result == TCL_ERROR) {
-       /* release the structure */
-       EmbWinCleanup(tablePtr, ewPtr);
-       ckfree((char *) ewPtr);
-
-       /* and free the hash table entry */
-       Tcl_DeleteHashEntry(entryPtr);
-       return TCL_ERROR;
-      }
-
-      /* if a window was specified, make sure it exists */
-    } else {
-      /* pointer wasn't null, do a reconfig if we have enough arguments */
-      /* get the window pointer from the table */
-      ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
-
-      /* 5 args means that there are values to replace */
-      if (argc > 5) {
-       /* and do a reconfigure */
-       result = EmbWinConfigure(tablePtr, ewPtr, argc-4, argv+4);
-       if (result == TCL_ERROR)
-         return TCL_ERROR;
-      }
-    }
+    int srow, scol, row, col, new;
+    Tcl_HashEntry *entryPtr;
+    TableEmbWindow *ewPtr;
 
-    /* 
-     * If there were less than 6 args, we need
-     * to do a printout of the config, even for new windows
-     */
-    if (argc < 6) {
-      result = Tk_ConfigureInfo(interp, tablePtr->tkwin, winConfigSpecs,
-                               (char *) ewPtr, (argc == 5)?argv[4]:0, 0);
-    } else {
-      /* Otherwise we reconfigured so invalidate the table for a redraw */
-      if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
-                          col-tablePtr->colOffset,
-                          &x, &y, &width, &height, 0)) {
-       TableInvalidate(tablePtr, x, y, width, height, 1);
-      }
-    }
-    return result;     /* CONFIGURE */
-
-  case WIN_DELETE:
-    if (argc < 4) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " window delete index ?index ...?\"", (char *) NULL);
-      return TCL_ERROR;
-    }
-    for (i = 3; i < argc; i++) {
-      if ((entryPtr = Tcl_FindHashEntry(tablePtr->winTable, argv[i]))!=NULL) {
-       /* get the window pointer */
-       ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
-
-       EmbWinDelete(tablePtr, ewPtr);
-      }
-    }
-    /* clear up anything that might have been placed in the result string */
-    Tcl_SetResult(interp, "", TCL_STATIC);
-    return result;
-
-  case WIN_MOVE:
-    if (argc != 5) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " window move oldIndex newIndex\"", (char *) NULL);
-      return TCL_ERROR;
-    }
-    if (TableGetIndex(tablePtr, argv[3], &x, &y) == TCL_ERROR ||
-       TableGetIndex(tablePtr, argv[4], &row, &col) == TCL_ERROR) {
-      return TCL_ERROR;
+    if (TableGetIndex(tablePtr, srcPtr, &srow, &scol) != TCL_OK ||
+       TableGetIndex(tablePtr, destPtr, &row, &col) != TCL_OK) {
+       return TCL_ERROR;
     }
-    TableMakeArrayIndex(x, y, buf);
-    if ((entryPtr = Tcl_FindHashEntry(tablePtr->winTable, buf)) == NULL) {
-      Tcl_AppendResult(interp, "no window at index \"", argv[3],
-                      "\"", (char *) NULL);
-      return TCL_ERROR;
+    entryPtr = Tcl_FindHashEntry(tablePtr->winTable, srcPtr);
+    if (entryPtr == NULL) {
+       if (flags & INV_NO_ERR_MSG) {
+           return TCL_OK;
+       } else {
+           Tcl_AppendStringsToObj(Tcl_GetObjResult(tablePtr->interp),
+                   "no window at index \"", srcPtr, "\"", (char *) NULL);
+           return TCL_ERROR;
+       }
     }
     /* avoid moving it to the same location */
-    if (x == row && y == col) {
-      return TCL_OK;
+    if (srow == row && scol == col) {
+       return TCL_OK;
     }
     /* get the window pointer */
     ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
     /* and free the old hash table entry */
     Tcl_DeleteHashEntry(entryPtr);
 
-    TableMakeArrayIndex(row, col, buf);
-    entryPtr = Tcl_CreateHashEntry(tablePtr->winTable, buf, &new);
+    entryPtr = Tcl_CreateHashEntry(tablePtr->winTable, destPtr, &new);
     if (!new) {
-      /* window already there - just delete it */
-      TableEmbWindow *ewPtrDel;
-
-      ewPtrDel = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
-      /* This prevents the deletion of it's own entry, since we need it */
-      ewPtrDel->hPtr = NULL;
-      EmbWinDelete(tablePtr, ewPtrDel);
+       /* window already there - just delete it */
+       TableEmbWindow *ewPtrDel;
+       ewPtrDel = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
+       /* This prevents the deletion of it's own entry, since we need it */
+       ewPtrDel->hPtr = NULL;
+       EmbWinDelete(tablePtr, ewPtrDel);
     }
     /* set the new entry's value */
     Tcl_SetHashValue(entryPtr, (ClientData) ewPtr);
     ewPtr->hPtr = entryPtr;
 
-    /* Invalidate old cell */
-    if (TableCellVCoords(tablePtr, x-tablePtr->rowOffset,
-                        y-tablePtr->colOffset,
-                        &x, &y, &width, &height, 0)) {
-      TableInvalidate(tablePtr, x, y, width, height, 0);
+    if (flags & INV_FORCE) {
+       int x, y, w, h;
+       /* Invalidate old cell */
+       if (TableCellVCoords(tablePtr, srow-tablePtr->rowOffset,
+               scol-tablePtr->colOffset, &x, &y, &w, &h, 0)) {
+           TableInvalidate(tablePtr, x, y, w, h, 0);
+       }
+       /* Invalidate new cell */
+       if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
+               col-tablePtr->colOffset, &x, &y, &w, &h, 0)) {
+           TableInvalidate(tablePtr, x, y, w, h, 0);
+       }
+    }
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_WinDelete --
+ *     This procedure is invoked by ... whenever
+ *     an embedded window is being delete.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     Window info will be deleted.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_WinDelete(register Table *tablePtr, char *CONST idxPtr)
+{
+    Tcl_HashEntry *entryPtr;
+
+    entryPtr = Tcl_FindHashEntry(tablePtr->winTable, idxPtr);
+    if (entryPtr != NULL) {
+       /* get the window pointer & clean up data associated with it */
+       EmbWinDelete(tablePtr, (TableEmbWindow *) Tcl_GetHashValue(entryPtr));
+    }
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Table_WindowCmd --
+ *     This procedure is invoked to process the window method
+ *     that corresponds to a widget managed by this module.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+int
+Table_WindowCmd(ClientData clientData, register Tcl_Interp *interp,
+               int objc, Tcl_Obj *CONST objv[])
+{
+    register Table *tablePtr = (Table *)clientData;
+    int result = TCL_OK, cmdIndex, row, col, x, y, width, height, i, new;
+    TableEmbWindow *ewPtr;
+    Tcl_HashEntry *entryPtr;
+    Tcl_HashSearch search;
+    char buf[INDEX_BUFSIZE], *keybuf, *winname;
+
+    if (objc < 3) {
+       Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?");
+       return TCL_ERROR;
     }
-    /* Invalidate new cell */
-    if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
-                        col-tablePtr->colOffset,
-                        &x, &y, &width, &height, 0)) {
-      TableInvalidate(tablePtr, x, y, width, height, 0);
+
+    /* parse the next argument */
+    if (Tcl_GetIndexFromObj(interp, objv[2], winCmdNames,
+                           "option", 0, &cmdIndex) != TCL_OK) {
+       return TCL_ERROR;
     }
-    break;
-
-  case WIN_NAMES:
-    /* just print out the image names */
-    if (argc != 3 && argc != 4) {
-      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
-                      " window names ?pattern?\"", (char *) NULL);
-      return TCL_ERROR;
+    switch ((enum winCommand) cmdIndex) {
+    case WIN_CGET:
+       if (objc != 5) {
+           Tcl_WrongNumArgs(interp, 3, objv, "index option");
+           return TCL_ERROR;
+       }
+       entryPtr = Tcl_FindHashEntry(tablePtr->winTable,
+                                    Tcl_GetString(objv[3]));
+       if (entryPtr == NULL) {
+           Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+                                  "no window at index \"",
+                                  Tcl_GetString(objv[3]), "\"", (char *)NULL);
+           return TCL_ERROR;
+       } else {
+           ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
+           result = Tk_ConfigureValue(interp, tablePtr->tkwin, winConfigSpecs,
+                                      (char *) ewPtr,
+                                      Tcl_GetString(objv[4]), 0);
+       }
+       return result;  /* CGET */
+
+    case WIN_CONFIGURE:
+       if (objc < 4) {
+           Tcl_WrongNumArgs(interp, 3, objv, "index ?arg arg  ...?");
+           return TCL_ERROR;
+       }
+       if (TableGetIndexObj(tablePtr, objv[3], &row, &col) == TCL_ERROR) {
+           return TCL_ERROR;
+       }
+       TableMakeArrayIndex(row, col, buf);
+       entryPtr = Tcl_CreateHashEntry(tablePtr->winTable, buf, &new);
+
+       if (new) {
+           /* create the structure */
+           ewPtr = TableNewEmbWindow(tablePtr);
+
+           /* insert it into the table */
+           Tcl_SetHashValue(entryPtr, (ClientData) ewPtr);
+           ewPtr->hPtr = entryPtr;
+
+           /* configure the window structure */
+           result = EmbWinConfigure(tablePtr, ewPtr, objc-4, objv+4);
+           if (result == TCL_ERROR) {
+               /* release the structure */
+               EmbWinCleanup(tablePtr, ewPtr);
+               ckfree((char *) ewPtr);
+
+               /* and free the hash table entry */
+               Tcl_DeleteHashEntry(entryPtr);
+           }
+       } else {
+           /* window exists, do a reconfig if we have enough args */
+           /* get the window pointer from the table */
+           ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
+
+           /* 5 args means that there are values to replace */
+           if (objc > 5) {
+               /* and do a reconfigure */
+               result = EmbWinConfigure(tablePtr, ewPtr, objc-4, objv+4);
+           }
+       }
+       if (result == TCL_ERROR) {
+           return TCL_ERROR;
+       }
+
+       /* 
+        * If there were less than 6 args, we need
+        * to do a printout of the config, even for new windows
+        */
+       if (objc < 6) {
+           result = Tk_ConfigureInfo(interp, tablePtr->tkwin, winConfigSpecs,
+                                     (char *) ewPtr, (objc == 5)?
+                                     Tcl_GetString(objv[4]) : NULL, 0);
+       } else {
+           /* Otherwise we reconfigured so invalidate
+            * the table for a redraw */
+           if (TableCellVCoords(tablePtr, row-tablePtr->rowOffset,
+                                col-tablePtr->colOffset,
+                                &x, &y, &width, &height, 0)) {
+               TableInvalidate(tablePtr, x, y, width, height, 1);
+           }
+       }
+       return result;  /* CONFIGURE */
+
+    case WIN_DELETE:
+       if (objc < 4) {
+           Tcl_WrongNumArgs(interp, 3, objv, "index ?index ...?");
+           return TCL_ERROR;
+       }
+       for (i = 3; i < objc; i++) {
+           Table_WinDelete(tablePtr, Tcl_GetString(objv[i]));
+       }
+       break;
+
+    case WIN_MOVE:
+       if (objc != 5) {
+           Tcl_WrongNumArgs(interp, 3, objv, "srcIndex destIndex");
+           return TCL_ERROR;
+       }
+       result = Table_WinMove(tablePtr, Tcl_GetString(objv[3]),
+                              Tcl_GetString(objv[4]), INV_FORCE);
+       break;
+
+    case WIN_NAMES: {
+       Tcl_Obj *objPtr = Tcl_NewObj();
+
+       /* just print out the window names */
+       if (objc < 3 || objc > 4) {
+           Tcl_WrongNumArgs(interp, 3, objv, "?pattern?");
+           return TCL_ERROR;
+       }
+       winname = (objc == 4) ? Tcl_GetString(objv[3]) : NULL;
+       entryPtr = Tcl_FirstHashEntry(tablePtr->winTable, &search);
+       while (entryPtr != NULL) {
+           keybuf = Tcl_GetHashKey(tablePtr->winTable, entryPtr);
+           if (objc == 3 || Tcl_StringMatch(keybuf, winname)) {
+               Tcl_ListObjAppendElement(NULL, objPtr,
+                                        Tcl_NewStringObj(keybuf, -1));
+           }
+           entryPtr = Tcl_NextHashEntry(&search);
+       }
+       Tcl_SetObjResult(interp, TableCellSortObj(interp, objPtr));
+       break;
     }
-    entryPtr = Tcl_FirstHashEntry(tablePtr->winTable, &search);
-    while (entryPtr != NULL) {
-      keybuf = Tcl_GetHashKey(tablePtr->winTable, entryPtr);
-      if (argc == 3 || Tcl_StringMatch(keybuf, argv[3]))
-       Tcl_AppendElement(interp, keybuf);
-      entryPtr = Tcl_NextHashEntry(&search);
     }
-    Tcl_SetResult(interp,
-                 TableCellSort(tablePtr, Tcl_GetStringResult(interp)),
-                 TCL_DYNAMIC);
-    break;
-  }
-  return TCL_OK;
+    return TCL_OK;
 }
index 1f03531..8a61f04 100644 (file)
@@ -1 +1,8 @@
-TBL_VERSION    = 2.1
+#if 0
+TBL_MAJOR_VERSION = 2
+TBL_MINOR_VERSION = 7
+TBL_VERSION     = $(TBL_MAJOR_VERSION).$(TBL_MINOR_VERSION)
+#endif
+#define TBL_MAJOR_VERSION 2
+#define TBL_MINOR_VERSION 7
+#define TBL_VERSION "2.7"