From 76f5a349879d6b114a30d818610b44e85b5a2d28 Mon Sep 17 00:00:00 2001 From: kseitz Date: Mon, 13 Aug 2001 17:53:52 +0000 Subject: [PATCH] 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. --- libgui/ChangeLog | 16 + libgui/configure | 22 +- libgui/configure.in | 4 + libgui/doc/tkTable.html | 1907 ++++++++++ libgui/doc/tkTable.n | 399 ++- libgui/library/Makefile.in | 14 +- libgui/src/Makefile.am | 31 +- libgui/src/Makefile.in | 65 +- libgui/src/tkTable.c | 7603 +++++++++++++++++----------------------- libgui/src/tkTable.h | 675 ++-- libgui/src/tkTable.tcl | 380 +- libgui/src/tkTable.tcl.h | 366 ++ libgui/src/tkTableCell.c | 1520 ++++++-- libgui/src/tkTableCellSort.c | 400 +++ libgui/src/tkTableCmds.c | 1293 +++++++ libgui/src/tkTableEdit.c | 683 ++++ libgui/src/tkTableInitScript.h | 90 + libgui/src/tkTablePs.c | 1299 +++++++ libgui/src/tkTableTag.c | 1584 ++++++--- libgui/src/tkTableUtil.c | 340 ++ libgui/src/tkTableWin.c | 983 +++--- libgui/src/tkTable_version.in | 9 +- 22 files changed, 13531 insertions(+), 6152 deletions(-) create mode 100644 libgui/doc/tkTable.html create mode 100644 libgui/src/tkTable.tcl.h create mode 100644 libgui/src/tkTableCellSort.c create mode 100644 libgui/src/tkTableCmds.c create mode 100644 libgui/src/tkTableEdit.c create mode 100644 libgui/src/tkTableInitScript.h create mode 100644 libgui/src/tkTablePs.c create mode 100644 libgui/src/tkTableUtil.c diff --git a/libgui/ChangeLog b/libgui/ChangeLog index fcb00a2dbe..ca6d716adc 100644 --- a/libgui/ChangeLog +++ b/libgui/ChangeLog @@ -1,3 +1,19 @@ +2001-08-12 Keith Seitz + + 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 * Makefile.in: Regen. diff --git a/libgui/configure b/libgui/configure index 8a93629b6c..2b9ddae4f9 100755 --- a/libgui/configure +++ b/libgui/configure @@ -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 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 diff --git a/libgui/configure.in b/libgui/configure.in index ba4b9b4807..afe79d22ff 100644 --- a/libgui/configure.in +++ b/libgui/configure.in @@ -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 index 0000000000..e7c65978d7 --- /dev/null +++ b/libgui/doc/tkTable.html @@ -0,0 +1,1907 @@ + + + + + +man page(1) + + +Table of Contents

+_________________________________________________________________ + +

Name

+ +

+table - Create and manipulate tables + +

Synopsis

+ +

+table pathName ?options? + +

Standard Options

+ + +
+ +
-anchor
+
-background -cursor +
+ +
-exportselection
+
-font -foreground +
+ +
-highlightbackground
+
-highlightcolor -highlightthickness +
+ +
-insertbackground
+
-insertborderwidth-insertofftime +-insertontime -insertwidth -invertselected +
+ +
-relief
+
-takefocus -xscrollcommand +-yscrollcommand +
+
+

+See the options manual entry for details on the standard +options. + +

Widget-specific Options

+ +

+Command-Line Name:-autoclear
+ +Database Name: autoClear
+ +Database Class: AutoClear +

+A boolean value which specifies whether the first +keypress in a cell will delete whatever text was +previously there. Defaults to 0. +

+Command-Line Name:-bordercursor
+ +Database Name: borderCursor
+ +Database Class: 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 crosshair. +

+Command-Line Name:-borderwidth or -bd
+ +Database Name: borderWidth
+ +Database Class: 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 relief 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 -drawmode for the table. Each value +in the list must have one of the forms acceptable +to Tk_GetPixels. +

+Command-Line Name:-browsecommand or -browsecmd +Database Name: browseCommand
+ +Database Class: BrowseCommand +

+Specifies a command which will be evaluated anytime +the active cell changes. It uses the %-substition +model described in COMMAND SUBSTITUTION below. +

+Command-Line Name:-cache
+ +Database Name: cache
+ +Database Class: Cache +

+A boolean value that specifies whether an internal +cache of the table contents should be kept. This +greatly enhances speed performance when used with +-command but uses extra memory. Can maintain state +when both -command and -variable are empty. The +cache is automatically flushed whenever the value +of -cache or -variable changes, otherwise you have +to explicitly call clear on it. Defaults to off. +

+Command-Line Name:-colorigin
+ +Database Name: colOrigin
+ +Database Class: 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. +

+Command-Line Name:-cols
+ +Database Name: cols
+ +Database Class: Cols +

+Number of cols in the table. Defaults to 10. +

+Command-Line Name:-colseparator
+ +Database Name: colSeparator
+ +Database Class: Separator +

+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. +

+Command-Line Name:-colstretchmode
+ +Database Name: colStretchMode
+ +Database Class: StretchMode +

+Specifies one of the following stretch modes for +columns to fill extra allocated window space: +none 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. +

+unset Only columns that do not have a specific +width set will be stretched. + +

+ +
all
+
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. +
+
+

+last The last column will be stretched to fill +the window space allocated to the table. +

+fill (only valid for -rowstretch 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. +

+Command-Line Name:-coltagcommand
+ +Database Name: colTagCommand
+ +Database Class: TagCommand +

+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 tag col 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. +

+Command-Line Name:-colwidth
+ +Database Name: colWidth
+ +Database Class: ColWidth +

+Default column width, interpreted as characters in +the default font when the number is positive, or +pixels if it is negative. Defaults to 10. +

+Command-Line Name:-command
+ +Database Name: command
+ +Database Class: Command +

+Specified a command to use as a procedural +interface to cell values. If -usecommand is true, +this command will be used instead of any reference +to the -variable 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. +

+Command-Line Name:-drawmode
+ +Database Name: drawMode
+ +Database Class: DrawMode +

+Sets the table drawing mode to one of the following +options: +

+slow 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. +

+compatible
+ +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. +

+fast 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 -borderwidth 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. +

+single The table is drawn to the screen as in fast +mode, but only single pixel lines are drawn +(not square borders). +

+Command-Line Name:-flashmode
+ +Database Name: flashMode
+ +Database Class: FlashMode +

+A boolean value which specifies whether cells +should flash when their value changes. The table +tag flash will be applied to these cells for the +duration specified by -flashtime. Defaults to 0. +

+Command-Line Name:-flashtime
+ +Database Name: flashTime
+ +Database Class: FlashTime +

+The amount of time, in 1/4 second increments, for +which a cell should flash when its value has +changed. -flashmode must be on. Defaults to 2. +

+Command-Line Name:-height
+ +Database Name: height
+ +Database Class: Height +

+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 -maxheight. +

+Command-Line Name:-invertselected
+ +Database Name: invertSelected
+ +Database Class: InvertSelected +

+Specifies whether the foreground and background of +an item should simply have their values swapped +instead of merging the sel tag options when the +cell is selected. Defaults to 0 (merge sel tag). +

+Command-Line Name:-ipadx
+ +Database Name: ipadX
+ +Database Class: 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 -padx for an alternate padding style. +

+Command-Line Name:-ipady
+ +Database Name: ipadY
+ +Database Class: 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 -pady for an alternate padding style. +

+Command-Line Name:-maxheight
+ +Database Name: maxHeight
+ +Database Class: MaxHeight +

+The max height in pixels that the window will +request. Defaults to 600. +

+Command-Line Name:-maxwidth
+ +Database Name: maxWidth
+ +Database Class: MaxWidth +

+The max width in pixels that the window will +request. Defaults to 800. +

+Command-Line Name:-multiline
+ +Database Name: multiline
+ +Database Class: Multiline +

+Specifies the default setting for the multiline tag +option. Defaults to 1. +

+Command-Line Name:-padx
+ +Database Name: padX
+ +Database Class: 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 width 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 -ipadx for an +alternate padding style. +

+Command-Line Name:-pady
+ +Database Name: padY
+ +Database Class: 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 height 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 -ipadx for an +alternate padding style. +

+Command-Line Name:-resizeborders
+ +Database Name: resizeBorders
+ +Database Class: ResizeBorders +

+Specifies what kind of interactive border resizing +to allow, must be one of row, col, both (default) +or none. +

+Command-Line Name:-rowheight
+ +Database Name: rowHeight
+ +Database Class: RowHeight +

+Default row height, interpreted as lines in the +default font when the number is positive, or pixels +if it is negative. Defaults to 1. +

+Command-Line Name:-roworigin
+ +Database Name: rowOrigin
+ +Database Class: Origin +

+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. +

+Command-Line Name:-rows
+ +Database Name: rows
+ +Database Class: Rows +

+Number of rows in the table. Defaults to 10. +

+Command-Line Name:-rowseparator
+ +Database Name: rowSeparator
+ +Database Class: Separator +

+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. +

+Command-Line Name:-rowstretchmode
+ +Database Name: rowStretchMode
+ +Database Class: StretchMode +

+Specifies the stretch modes for rows to fill extra +allocated window space. See -colstretchmode for +valid options. +

+Command-Line Name:-rowtagcommand
+ +Database Name: rowTagCommand
+ +Database Class: TagCommand +

+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 -coltagcommand, except +that it applies to row tags. +

+Command-Line Name:-selectioncommand or -selcmd +Database Name: selectionCommand
+ +Database Class: 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. If an error occurs, a Tcl background +error is generated and nothing is returned. +

+Command-Line Name:-selectmode
+ +Database Name: selectMode
+ +Database Class: 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 +either single, browse, multiple, or extended; the +default value is browse. These styles are like +those for the Tk listbox, except expanded for 2 +dimensions. +

+Command-Line Name:-selecttitle
+ +Database Name: selectTitles
+ +Database Class: SelectTitles +

+Specifies whether title cells should be allowed in +the selection. Defaults to 0 (disallowed). +

+Command-Line Name:-selecttype
+ +Database Name: selectType
+ +Database Class: SelectType +

+Specifies one of several types of selection for the +table. The value of the option may be one of row, +col, cell, or both (meaning row && col); the +default value is cell. These types define whether +an entire row/col is affected when a cell's selection +is changed (set or clear). +

+Command-Line Name:-sparsearray
+ +Database Name: sparseArray
+ +Database Class: 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. +

+Command-Line Name:-state
+ +Database Name: state
+ +Database Class: State + +

+ +
Specifies one of two states for the entry:
+
normal +or disabled. 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 normal. +
+
+

+Command-Line Name:-titlecols
+ +Database Name: titleCols
+ +Database Class: TitleCols +

+Number of columns to use as a title area. Defaults +to 0. +

+Command-Line Name:-titlerows
+ +Database Name: titleRows
+ +Database Class: TitleRows +

+Number of rows to use as a title area. Defaults to +0. +

+Command-Line Name:-usecommand
+ +Database Name: useCommand
+ +Database Class: UseCommand +

+A boolean value which specifies whether to use the +command option. This value sets itself to zero if +command is used and returns an error. Defaults to +1 (will use command if specified). +

+Command-Line Name:-validate
+ +Database Name: validate
+ +Database Class: Validate +

+A boolean specifying whether validation should +occur for the active buffer. Defaults to 0. +

+Command-Line Name:-validatecommand or -vcmd +Database Name: validateCommand
+ +Database Class: ValidateCommand +

+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. +

+Command-Line Name:-variable
+ +Database Name: variable
+ +Database Class: Variable +

+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 row,col for +cells and the special key active 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 -sparsearray options +is set to 0. +

+Command-Line Name:-width
+ +Database Name: width
+ +Database Class: 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 -maxwidth. +

+Command-Line Name:-wrap
+ +Database Name: wrap
+ +Database Class: Wrap +

+Specifies the default wrap value for tags. +Defaults to 0.
+ +_________________________________________________________________ + +

Description

+ +

+The table 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 tags, which can be used to change +the style of a row, column or cell (see TAGS for details). +A cell flash 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 «EMBEDDED WINDOWS» respectively. +

+One or more cells may be selected as described below. If +a table is exporting its selection (see -exportselection +option), then it will observe the standard X11 protocols +for handling the selection. See THE SELECTION for +details. +

+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 -xscrollcommand +and -yscrollcommand options. They also support +scanning, as described below. +

+In order to obtain good performance, the table widget supports +multiple drawing modes, two of which are fully Tk +compatible. + +

Initialization

+ +

+When the table command is loaded into an interpreter, a +built-in Tcl command, tkTableInit, is evaluated. This +will search for the appropriate table binding init file to +load. The directories searched are those in $tcl_pkgPath, +both with Tktable(version) appended and without, +$tk_library and [pwd] (the current directory). You can +also define an $env(TK_TABLE_LIBRARY) to head this search +list. By default, the file searched for is called +tkTable.tcl, but this can be overridden by setting +$env(TK_TABLE_LIBRARY_FILE). +

+This entire init script can be overridden by providing +your own tkTableInit procedure before the library is +loaded. Otherwise, the aforementioned +env(TK_TABLE_LIBRARY) variable will be set with the directory +in which $env(TK_TABLE_LIBRARY_FILE) was found. + +

Indices

+ +

+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: +

+number,number
+ +Specifies the cell as a numerical index of +row,col which corresponds to the index of the +associated Tcl array, where -roworigin,-colorigin +corresponds to the first cell in the +table (0,0 by default). + +

+ +
active
+
Indicates the cell that has the location cursor. +It is specified with the activate widget +command. +
+ +
anchor
+
Indicates the anchor point for the selection, +which is set with the selection anchor widget +command. +
+
+

+bottomright Indicates the bottom-rightmost cell visible in +the table. + +

+ +
end
+
Indicates the bottom right cell of the table. +
+ +
origin
+
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. +
+ +
topleft
+
Indicates the top-leftmost editable cell visible +in the table (this excludes title cells). +
+ +
@x,y
+
Indicates the cell that covers the point in +the table window specified by x and y (in +pixel coordinates). If no cell covers that +point, then the closest cell to that point is +used. +
+
+

+In the widget command descriptions below, arguments named +index, first, and last always contain text indices in one +of the above forms. + +

Tags

+ +

+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: + +

+ +
active
+
This tag is given to the active cell +
+ +
flash
+
If flash mode is on, this tag is given to +any recently edited cells. +
+ +
sel
+
This tag is given to any selected cells. +
+ +
title
+
This tag is given to any cells in the +title rows and columns. This tag has +-state disabled by default. +
+
+

+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 ``pathName tag configure'' 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: + +

+ +
-anchor anchor
+
+Anchor for item in the cell space. +
+ +
-background or -bg color
+
+Background color of the cell. +
+ +
-borderwidth or -bd pixelList
+
+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). +
+ +
-font fontName
+
+Font for text in the cell. +
+ +
-foreground or -fg color
+
+Foreground color of the cell. +
+ +
-justify justify
+
+How to justify multi-line text in a cell. +It must be one of left, right, or center. +
+ +
-image imageName
+
+An image to display in the cell instead of +text. +
+ +
-multiline boolean
+
+Whether to display text with newlines on +multiple lines. +
+ +
-relief relief
+
+The relief for the cell. +
+ +
-showtext boolean
+
+Whether to show the text over an image. +
+ +
-state state
+
+The state of the cell, to allow for certain +cells to be disabled. This prevents the +cell from being edited by the insert or +delete methods, but a direct set will not be +prevented. +
+ +
-wrap boolean
+
+Whether characters should wrap in a cell +that is not wide enough. +
+
+

+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 ``pathName tag lower'' and ``pathName tag raise'' +widget commands. +

+If a cell has several tags associated with it that define +the same display options (eg - a title cell with specific +row and cell 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. +

+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 -showtext. + +

Embedded Windows

+ +

+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. +

+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 window names). +

+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: + +

+ +
-create script
+
+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. +
+ +
-background or -bg color
+
+Background color of the cell. If not specified, +it uses the table's default background. +
+ +
-borderwidth or -bd pixelList
+
+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). +
+ +
-padx pixels
+
+As defined in the Tk options man page. +
+ +
-pady pixels
+
+As defined in the Tk options man page. +
+ +
-relief relief
+
+The relief to use for the cell in which the +window lies. If not specified, it uses the +table's default relief. +
+ +
-sticky sticky
+
+Stickiness of the window inside the cell, as +defined by the grid command. +
+ +
-window pathName
+
+Specifies the name of a window to display in +the annotation. It must exist before being +specified here. +
+
+ +

the Selection

+ +

+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 -rowseparator and -colseparator options. For +example, default Excel format would be to set -rowseparator +to «\n» and -colseparator 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 tk_tablePasteHandler. This might be +necessary if -selectioncommand is set. + +

Row/Col Spanning

+ +

+Individual cells can span multiple rows and/or columns. +This is done via the spans 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. +

+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. +

+Cells hidden 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 get or set. +

+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. + +

Command Substitution

+ +

+The various option based commands that the table supports +all support the familiar Tk %-substitution model (see bind +for more details). The following %-sequences are recognized +and substituted by the table widget: +

+%c For SelectionCommand, it is the maximum number of +columns in any row in the selection. Otherwise it is +the column of the triggered cell. + +

+ +
%C
+
A convenience substitution for %r,%c. +
+ +
%i
+
For SelectionCommand, it is the total number of cells +in the selection. For Command, it is 0 for a read +(get) and 1 for a write (set). Otherwise it is the +current cursor position in the cell. +
+ +
%r
+
For SelectionCommand, it is the number of rows in the +selection. Otherwise it is the row of the triggered +cell. +
+
+

+%s For ValidateCommand, it is the current value of the +cell being validated. For SelectionCommand, it is +the default value of the selection. For BrowseCommand, +it is the index of the last active cell. For +Command, it is empty for reads (get) and the current +value of the cell for writes (set). + +

+ +
%S
+
For ValidateCommand, it is the potential new value of +the cell being validated. For BrowseCommand, it is +the index of the new active cell. +
+
+

+%W The pathname to the window for which the command was +generated. + +

Widget Command

+ +

+The table command creates a new Tcl command whose name is +pathName. This command may be used to invoke various +operations on the widget. It has the following general +form:
+ +pathName option ?arg arg ...?
+ +Option and the args determine the exact behavior of the +command. +

+The following commands are possible for table widgets: +

+pathName activate index
+ +Sets the active cell to the one indicated by index. +

+pathName bbox first ?last?
+ +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. +

+pathName border option args
+ +This command is a voodoo hack to implement border +sizing for tables. This is normally called through +bindings, with the following as valid options: +

+pathName border mark x y ?row|col? +Records x and y and the row and/or column +border under that point in the table window, +if any; used in conjunction with later border +dragto commands. Typically this command +is associated with a mouse button press in +the widget. If row or col is not specified, +it returns a tuple of both border indices +(an empty item means no border). Otherwise, +just the specified item is returned. +

+pathName border dragto x y
+ +This command computes the difference between +its x and y arguments and the x and y arguments +to the last border mark 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. +

+pathName cget option
+ +Returns the current value of the configuration +option given by option. Option may have any of the +values accepted by the table command. +

+pathName clear option ?first? ?last?
+ +This command is a convenience routine to clear certain +state information managed by the table. first +and last represent valid table indices. If neither +are specified, then the command operates on the +whole table. The following options are recognized: +

+pathName clear cache ?first? ?last? +Clears the specified section of the cache, +if the table has been keeping one. +

+pathName clear sizes ?first? ?last? +Clears the specified row and column areas of +specific height/width dimensions. When just +one index is specified, for example 2,0, +that is interpreted as row 2 and column 0. +

+pathName clear tags ?first? ?last? +Clears the specified area of tags (all row, +column and cell tags). +

+pathName clear all ?first? ?last? +Performs all of the above clear functions on +the specified area. +

+pathName configure ?option? ?value option value ...? +Query or modify the configuration options of the +widget. If no option is specified, returns a list +describing all of the available options for path_Name +(see Tk_ConfigureInfo for information on the +format of this list). If option is specified with +no value, 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 option is specified). If one or +more option-value 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. Option may have any of +the values accepted by the table command. +

+pathName curselection ?value?
+ +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. +

+pathName curvalue ?value?
+ +If no value is given, the value of the cell being +edited (indexed by active) is returned, else it is +set to the given value. +

+pathName delete option arg ?arg?
+ +This command is used to delete various things in a +table. It has several forms, depending on the +option: +pathName delete active index ?index? +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. index can be +a number, insert or end. +

+pathName delete cols ?switches? index ?count? +Deletes count cols starting at (and including) +col index. The index will be constrained +to the limits of the tables. If +count is negative, it deletes cols to the +left. Otherwise it deletes cols to the +right. count defaults to 1 (meaning just +the column specified). At the moment, spans +are not adjusted with this action. Optional +switches are: + +

+ +
-holddimensions
+
+Causes the table cols to be unaffected +by the deletion (empty cols +may appear). By default the dimensions +are adjusted by count. +
+ +
-holdselection
+
+Causes the selection to be maintained +on the absolute cells values. +Otherwise, the selection will be +cleared.. +
+ +
-holdtags
+
+Causes the tags specified by the tag +method to not move along with the +data. Also prevents specific widths +set by the width method from being +adjusted. By default, these tags +are properly adjusted. +
+ +
-holdwindows
+
+Causes the embedded windows created +with the window method to not move +along with the data. By default, +these windows are properly adjusted. +
+ +
-keeptitles
+
+Prevents title area cells from being +changed. Otherwise they are treated +just like regular cells and will +move as specified. +
+ +
--
+
Signifies the end of the switches. +
+
+

+pathName delete rows ?switches? index ?count? +Deletes count rows starting at (and +including) row index. If count 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. +

+pathName get first ?last?
+ +Returns the value of the cells specified by the +table indices first and (optionally) last in a +list. +

+pathName height ?row? ?value row value ...? +If no row is specified, returns a list describing +all rows for which a height has been set. If row +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 +row-value pairs are specified, then it sets each +row to be that height in lines (positive number) or +pixels (negative number). If value is default, +then the row uses the default height, specified by +-rowheight. +

+pathName hidden ?index? ?index ...?
+ +When called without args, it returns all the hidden +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. +

+pathName icursor ?arg?
+ +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 insert or end 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. +

+pathName index index ?row|col?
+ +Returns the integer cell coordinate that corresponds +to index in the form row,col. If row or col +is specified, then only the row or column index is +returned. +

+pathName insert option arg arg
+ +This command is used to into various things into a +table. It has several forms, depending on the +option: +

+pathName insert active index value +The value is a text string which is inserted +at the index postion of the active cell. +The cursor is then positioned after the new +text. index can be a number, insert or end. +

+pathName insert cols ?switches? index ?count? +Inserts count cols starting at col index. +If count 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. +

+pathName insert rows ?switches? index ?count? +Inserts count rows starting at row index. +If count 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. +

+pathName reread
+ +Rereads the old contents of the cell back into the +editing buffer. Useful for a key binding when +<Escape> is pressed to abort the edit (a default +binding). +

+pathName scan option args
+ +This command is used to implement scanning on +tables. It has two forms, depending on option: +

+pathName scan mark x y
+ +Records x and y and the current view in the +table window; used in conjunction with +later scan dragto commands. Typically this +command is associated with a mouse button +press in the widget. It returns an empty +string. +

+pathName scan dragto x y.
+ +This command computes the difference between +its x and y arguments and the x and y arguments +to the last scan mark 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. +

+pathName see index
+ +Adjust the view in the table so that the cell given +by index 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. +

+pathName selection option arg
+ +This command is used to adjust the selection within +a table. It has several forms, depending on +option: +

+pathName selection anchor index
+ +Sets the selection anchor to the cell given +by index. The selection anchor is the end +of the selection that is fixed while dragging +out a selection with the mouse. The +index anchor may be used to refer to the +anchor cell. +

+pathName selection clear first ?last? +If any of the cells between first and last +(inclusive) are selected, they are deselected. +The selection state is not changed +for cells outside this range. first may be +specified as all to remove the selection +from all cells. +

+pathName selection includes index +Returns 1 if the cell indicated by index is +currently selected, 0 if it isn't. +

+pathName selection set first ?last? +Selects all of the cells in the range +between first and last, inclusive, without +affecting the selection state of cells outside +that range. +

+pathName set ?row|col? index ?value? ?index value ...? +Sets the specified index to the associated value. +Table validation will not be triggered via this +method. If row or col 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 row is specified) or +rows (for col). For example, set row 2,3 {2,3 2,4 +2,5} will set 3 cells, from 2,3 to 2,5. The setting +of cells is silently bounded by the known +table dimensions. +

+pathName spans ?index? ?rows,cols index rows,cols ...? +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 index, the span +for that index only is returned, if any. Otherwise +an even number of index rows,cols pairs are used to +set spans. A span starts at the index 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. +

+pathName tag option ?arg arg ...?
+ +This command is used to manipulate tags. The exact +behavior of the command depends on the option argument +that follows the tag argument. cget, cell, +and row|col complain about unknown tag names. The +following forms of the command are currently supported: +

+pathName tag cell tagName ?index ...? +With no arguments, prints out the list of +cells that use the tag. Otherwise it sets +the specified cells to use the named tag, +replacing any tag that may have been set +using this method before. If tagName is {}, +the cells are reset to the default tag. +Tags added during -*tagcommand evaluation do +not register here. If tagName does not +exist, it will be created with the default +options. +

+pathName tag cget tagName option +This command returns the current value of +the option named option associated with the +tag given by tagName. Option may have any +of the values accepted by the tag configure +widget command. +

+pathName tag col tagName ?col ...? +With no arguments, prints out the list of +cols that use the tag. Otherwise it sets +the specified columns to use the named tag, +replacing any tag that may have been set +using this method before. If tagName is {}, +the cols are reset to the default tag. Tags +added during -coltagcommand evaluation do +not register here. If tagName does not +exist, it will be created with the default +options. +

+pathName tag configure tagName ?option? ?value? +?option value ...?
+ +This command is similar to the configure +widget command except that it modifies +options associated with the tag given by +tagName instead of modifying options for the +overall table widget. If no option is specified, +the command returns a list describing +all of the available options for tagName +(see Tk_ConfigureInfo for information on the +format of this list). If option is specified +with no value, 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 option +is specified). If one or more option-value +pairs are specified, then the command modifies +the given option(s) to have the given +value(s) in tagName; in this case the command +returns an empty string. See TAGS +above for details on the options available +for tags. +

+pathName tag delete tagName
+ +Deletes a tag. No error if the tag does not +exist. +

+pathName tag exists tagName
+ +Returns 1 if the named tag exists, 0 otherwise. +

+pathName tag includes tagName index +Returns 1 if the specified index has the +named tag, 0 otherwise. +

+pathName tag lower tagName ?belowThis? +Lower the priority of the named tag. If +belowThis is not specified, then the tag's +priority is lowered to the bottom, otherwise +it is lowered to one below belowThis. +

+pathName tag names ?pattern?
+ +If no pattern is specified, shows the names +of all defined tags. Otherwise the pattern +is used as a glob pattern to show only tags +matching that pattern. Tag names are +returned in priority order (highest priority +tag first). +

+pathName tag raise tagName ?aboveThis? +Raise the priority of the named tag. If +aboveThis is not specified, then the tag's +priority is raised to the top, otherwise it +is raised to one above aboveThis. +

+pathName tag row tagName ?row ...? +With no arguments, prints out the list of +rows that use the tag. Otherwise it sets +the specified columns to use the named tag, +replacing any tag that may have been set +using this method before. If tagName is {}, +the rows are reset to use the default tag. +Tags added during -rowtagcommand evaluation +do not register here. If tagName does not +exist, it will be created with the default +options. +

+pathName validate index
+ +Explicitly validates the specified index based on +the current -validatecommand and returns 0 or 1 +based on whether the cell was validated. +

+pathName width ?col? ?value col value ...? +If no col is specified, returns a list describing +all cols for which a width has been set. If col 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 col-value pairs +are specified, then it sets each col to be that +width in characters (positive number) or pixels +(negative number). If value is default, then the +col uses the default width, specified by -colwidth. +

+pathName window option ?arg arg ...?
+ +This command is used to manipulate embedded windows. +The exact behavior of the command depends on +the option argument that follows the window argument. +The following forms of the command are currently +supported: +

+pathName window cget index option +This command returns the current value of +the option named option associated with the +window given by index. Option may have any +of the values accepted by the window configure +widget command. +

+pathName window configure index ?option? ?value? +?option value ...?
+ +This command is similar to the configure +widget command except that it modifies +options associated with the embedded window +given by index instead of modifying options +for the overall table widget. If no option +is specified, the command returns a list +describing all of the available options for +index (see Tk_ConfigureInfo for information +on the format of this list). If option is +specified with no value, 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 option is specified). If one or more +option-value pairs are specified, then the +command modifies the given option(s) to have +the given value(s) in index; in this case +the command returns an empty string. See +EMBEDDED WINDOWS above for details on the +options available for windows. +

+pathName window delete index ?index ...? +Deletes an embedded window from the table. +The associated window will also be deleted. +

+pathName window move indexFrom indexTo +Moves an embedded window from one cell to +another. If a window already exists in the +target cell, it will be deleted. +

+pathName window names ?pattern?
+ +If no pattern is specified, shows the cells +of all embedded windows. Otherwise the pat_tern +is used as a glob pattern to show only +cells matching that pattern. +

+pathName xview args
+ +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: +

+pathName xview
+ +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 +-xscrollcommand option. +

+pathName xview index
+ +Adjusts the view in the window so that the +column given by index is displayed at the +left edge of the window. +

+pathName xview moveto fraction
+ +Adjusts the view in the window so that frac_tion +of the total width of the table text is +off-screen to the left. fraction must be a +fraction between 0 and 1. +

+pathName xview scroll number what +This command shifts the view in the window +left or right according to number and what. +Number must be an integer. What must be +either units or pages or an abbreviation of +one of these. If what is units, the view +adjusts left or right by number character +units (the width of the 0 character) on the +display; if it is pages then the view +adjusts by number screenfuls. If number is +negative then characters farther to the left +become visible; if it is positive then +characters farther to the right become visible. +

+pathName yview ?args?
+ +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: +

+pathName yview
+ +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 -yscrollcommand option. +

+pathName yview index
+ +Adjusts the view in the window so that the +row given by index is displayed at the top +of the window. +

+pathName yview moveto fraction
+ +Adjusts the view in the window so that the +element given by fraction appears at the top +of the window. Fraction 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. +

+pathName yview scroll number what +This command adjusts the view in the window +up or down according to number and what. +Number must be an integer. What must be +either units or pages. If what is units, +the view adjusts up or down by number lines; +if it is pages then the view adjusts by num_ber +screenfuls. If number is negative then +earlier elements become visible; if it is +positive then later elements become visible. + +

Default Bindings

+ +

+The initialization creates class bindings that give the +following default behaviour: + +

+ +
[1]
+
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. +
+ +
[2]
+
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. +
+ +
[3]
+
Moving the mouse while Button-2 is pressed causes +scanning to occur without any selection. +
+ +
[4]
+
Home moves the table to have the origin in view. +
+ +
[5]
+
End moves the table to have the end cell in view. +
+ +
[6]
+
Control-Home moves the table to the origin and +activates that cell. +
+ +
[7]
+
Control-End moves the table to the end and activates +that cell. +
+ +
[8]
+
Shift-Control-Home extends the selection to the +origin. +
+ +
[9]
+
Shift-Control-End extends the selection to the end. +
+ +
[10]
+
The left, right, up and down arrows move the active +cell. +
+
+

+[11] Shift-<arrow> extends the selection in that direction. +

+[12] Control-leftarrow and Control-rightarrow move the +insertion cursor within the cell. + +

+ +
[13]
+
Control-slash selects all the cells. +
+
+

+[14] Control-backslash clears selection from all the +cells. +

+[15] Backspace deletes the character before the insertion +cursor in the active cell. +

+[16] Delete deletes the character after the insertion +cursor in the active cell. +

+[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. +

+[18] Control-a moves the insertion cursor to the beginning +of the active cell. +

+[19] Control-e moves the insertion cursor to the end of +the active cell. +

+[20] Control-minus and Control-equals decrease and +increase the width of the column with the active +cell in it. +

+[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 -resizeborders. +

+Some bindings may have slightly different behavior dependent +on the -selectionmode of the widget. +

+If the widget is disabled using the -state 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. +

+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<version>/tkTable.tcl". + +

Performance Issues

+ +

+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. +

+The display cell with the insert cursor is redrawn each +time the cursor blinks, which causes a steady stream of +graphics traffic. Set the -insertofftime option to 0 +avoid this. The use of a -command with the table without +a cache can cause significant slow-down, as the command is +called once for each request of a cell value. + +

Examples

+ +

+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] + +

Keywords

+ +

+table, widget, extension +

+ +


+Table of Contents

+

+ diff --git a/libgui/doc/tkTable.n b/libgui/doc/tkTable.n index 4fa4be2567..75f9480fba 100644 --- a/libgui/doc/tkTable.n +++ b/libgui/doc/tkTable.n @@ -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", @@ -57,27 +59,25 @@ '\" .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 \{\ @@ -88,10 +88,10 @@ '\" # 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 @@ -131,7 +131,7 @@ '\" # ^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/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 diff --git a/libgui/library/Makefile.in b/libgui/library/Makefile.in index 7121230ca1..d977d32bbc 100644 --- a/libgui/library/Makefile.in +++ b/libgui/library/Makefile.in @@ -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 diff --git a/libgui/src/Makefile.am b/libgui/src/Makefile.am index 4e92d9111e..0be1fe355d 100644 --- a/libgui/src/Makefile.am +++ b/libgui/src/Makefile.am @@ -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 diff --git a/libgui/src/Makefile.in b/libgui/src/Makefile.in index 32bf4ced35..d8026a185e 100644 --- a/libgui/src/Makefile.in +++ b/libgui/src/Makefile.in @@ -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. diff --git a/libgui/src/tkTable.c b/libgui/src/tkTable.c index b6f0d7a343..c26b4b420a 100644 --- a/libgui/src/tkTable.c +++ b/libgui/src/tkTable.c @@ -14,195 +14,104 @@ * 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 */ -#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 + */ + +/* + *--------------------------------------------------------------------------- * - * 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, " ?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)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 ??\"", (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 ?\"", (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; itopRow; + 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| ??\"", - (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; irowOffset; - 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 diff --git a/libgui/src/tkTable.h b/libgui/src/tkTable.h index 360b1de0c1..e4c32d3245 100644 --- a/libgui/src/tkTable.h +++ b/libgui/src/tkTable.h @@ -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_ @@ -17,73 +18,88 @@ #include #include #include -#include +#ifdef MAC_TCL +# include +#else +# include +#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 # 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 - -#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 */ /* @@ -107,6 +123,7 @@ * 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 */ @@ -123,15 +140,31 @@ #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 @@ -141,157 +174,213 @@ #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_ */ diff --git a/libgui/src/tkTable.tcl b/libgui/src/tkTable.tcl index f10f035195..40833110fd 100644 --- a/libgui/src/tkTable.tcl +++ b/libgui/src/tkTable.tcl @@ -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. # @@ -23,17 +32,18 @@ # 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 -## 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 { %W border dragto %x %y } @@ -42,65 +52,72 @@ bind Table { %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 { - 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 { # empty } bind Table { if {[winfo exists %W]} { - tkCancelRepeat + ::tk::table::CancelRepeat %W activate @%x,%y } } -bind Table {tkTableBeginExtend %W [%W index @%x,%y]} -bind Table {tkTableBeginToggle %W [%W index @%x,%y]} -bind Table {tkCancelRepeat} +bind Table {::tk::table::BeginExtend %W [%W index @%x,%y]} +bind Table {::tk::table::BeginToggle %W [%W index @%x,%y]} +bind Table {::tk::table::CancelRepeat} bind Table { - 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 { - 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 { - 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 -} else { - tkTableClipboardKeysyms Control-c Control-x Control-v -} - -bind Table { - # 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 { +bind Table <> { catch {%W activate active} } -bind Table {tkTableExtendSelect %W -1 0} -bind Table {tkTableExtendSelect %W 1 0} -bind Table {tkTableExtendSelect %W 0 -1} -bind Table {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 . +event add <> + +bind Table {::tk::table::ExtendSelect %W -1 0} +bind Table {::tk::table::ExtendSelect %W 1 0} +bind Table {::tk::table::ExtendSelect %W 0 -1} +bind Table {::tk::table::ExtendSelect %W 0 1} bind Table {%W yview scroll -1 pages; %W activate @0,0} bind Table {%W yview scroll 1 pages; %W activate @0,0} bind Table {%W xview scroll -1 pages} @@ -119,44 +136,128 @@ bind Table { %W selection set active %W see active } -bind Table {tkTableDataExtend %W origin} -bind Table {tkTableDataExtend %W end} -bind Table {::tk::table::BeginSelect %W [%W index active]} +bind Table {::tk::table::BeginExtend %W [%W index active]} +bind Table {::tk::table::SelectAll %W} bind Table { if {[string match browse [%W cget -selectmode]]} {%W selection clear all} } -bind Table {tkTableMoveCell %W -1 0} -bind Table {tkTableMoveCell %W 1 0} -bind Table {tkTableMoveCell %W 0 -1} -bind Table {tkTableMoveCell %W 0 1} -bind Table { - if {[string compare {} %A]} { %W insert active insert %A } -} -bind Table { - set tkPriv(junk) [%W icursor] - if {[string compare {} $tkPriv(junk)] && $tkPriv(junk)} { - %W delete active [expr {$tkPriv(junk)-1}] - } -} +bind Table {::tk::table::MoveCell %W -1 0} +bind Table {::tk::table::MoveCell %W 1 0} +bind Table {::tk::table::MoveCell %W 0 -1} +bind Table {::tk::table::MoveCell %W 0 1} +bind Table {::tk::table::Insert %W %A} +bind Table {::tk::table::BackSpace %W} bind Table {%W delete active insert} bind Table {%W reread} -#bind Table {tkTableMoveCell %W 1 0} -bind Table { - %W insert active insert "\n" -} +#bind Table {::tk::table::MoveCell %W 1 0} +bind Table {::tk::table::Insert %W "\n"} bind Table {%W icursor [expr {[%W icursor]-1}]} bind Table {%W icursor [expr {[%W icursor]+1}]} bind Table {%W icursor end} bind Table {%W icursor 0} bind Table {%W delete active insert end} -bind Table {tkTableChangeWidth %W active 1} -bind Table {tkTableChangeWidth %W active -1} +bind Table {::tk::table::ChangeWidth %W active 1} +bind Table {::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 +# class binding will also fire and insert the character, +# which is wrong. Ditto for Tab. + +bind Table {# nothing} +bind Table {# nothing} +bind Table {# nothing} +bind Table {# nothing} +if {[string match "macintosh" $tcl_platform(platform)]} { + bind Table {# 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 {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 index 0000000000..614106e98b --- /dev/null +++ b/libgui/src/tkTable.tcl.h @@ -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 { %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 {\n" +" array set tkPriv {x %x y %y}\n" +" tkTableMotion %W [%W index @%x,%y]\n" +"}\n" +"bind Table {\n" +" # empty\n" +"}\n" +"bind Table {\n" +" if {[winfo exists %W]} {\n" +" tkCancelRepeat\n" +" %W activate @%x,%y\n" +" }\n" +"}\n" +"bind Table {tkTableBeginExtend %W [%W index @%x,%y]}\n" +"bind Table {tkTableBeginToggle %W [%W index @%x,%y]}\n" +"bind Table {tkCancelRepeat}\n" +"bind Table {\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 {\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 {\n" +" if {!$tkPriv(mouseMoved)} { tk_tablePaste %W [%W index @%x,%y] }\n" +"}\n" +"if {[string comp {} [info command event]]} {\n" +" tkTableClipboardKeysyms \n" +"} else {\n" +" tkTableClipboardKeysyms Control-c Control-x Control-v\n" +"}\n" +"bind Table {\n" +" # empty to allow Tk focus movement\n" +"}\n" +"bind Table {\n" +" catch {%W activate active}\n" +"}\n" +"bind Table {tkTableExtendSelect %W -1 0}\n" +"bind Table {tkTableExtendSelect %W 1 0}\n" +"bind Table {tkTableExtendSelect %W 0 -1}\n" +"bind Table {tkTableExtendSelect %W 0 1}\n" +"bind Table {%W yview scroll -1 pages; %W activate @0,0}\n" +"bind Table {%W yview scroll 1 pages; %W activate @0,0}\n" +"bind Table {%W xview scroll -1 pages}\n" +"bind Table {%W xview scroll 1 pages}\n" +"bind Table {%W see origin}\n" +"bind Table {%W see end}\n" +"bind Table {\n" +" %W selection clear all\n" +" %W activate origin\n" +" %W selection set active\n" +" %W see active\n" +"}\n" +"bind Table {\n" +" %W selection clear all\n" +" %W activate end\n" +" %W selection set active\n" +" %W see active\n" +"}\n" +"bind Table {tkTableDataExtend %W origin}\n" +"bind Table {tkTableDataExtend %W end}\n" +"bind Table