OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [insight/] [libgui/] [src/] [tkTable.c] - Blame information for rev 1782

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * tkTable.c --
3
 *
4
 *      This module implements table widgets for the Tk
5
 *      toolkit.  An table displays a 2D array of strings
6
 *      and allows the strings to be edited.
7
 *
8
 * Based on Tk3 table widget written by Roland King
9
 *
10
 * Updates 1996 by:
11
 * Jeffrey Hobbs        jeff.hobbs@acm.org
12
 * John Ellson          ellson@lucent.com
13
 * Peter Bruecker       peter@bj-ig.de
14
 * Tom Moore            tmoore@spatial.ca
15
 * Sebastian Wangnick   wangnick@orthogon.de
16
 *
17
 * Copyright (c) 1997-1998 Jeffrey Hobbs
18
 *
19
 * See the file "license.terms" for information on usage and redistribution
20
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
21
 *
22
 */
23
 
24
#include "tkTable.h"
25
#ifdef DEBUG
26
#include "../../dprint.h"
27
#endif
28
 
29
INLINE static void      TableFlushCache _ANSI_ARGS_((Table *tablePtr));
30
static int      TableClear _ANSI_ARGS_((register Table *tablePtr, int mode,
31
                                        char *first, char *last));
32
INLINE static void      TableGetGc _ANSI_ARGS_((Display *display, Drawable d,
33
                                        TableTag *tagPtr, GC *tagGc));
34
static void     TableRedrawHighlight _ANSI_ARGS_((Table *tablePtr));
35
static void     TableDisplay _ANSI_ARGS_((ClientData clientdata));
36
static void     TableFlashEvent _ANSI_ARGS_((ClientData clientdata));
37
static void     TableAddFlash _ANSI_ARGS_((Table *tablePtr, int row, int col));
38
static void     TableSetActiveIndex _ANSI_ARGS_((register Table *tablePtr));
39
static void     TableGetActiveBuf _ANSI_ARGS_((register Table *tablePtr));
40
static char *   TableVarProc _ANSI_ARGS_((ClientData clientData,
41
                        Tcl_Interp *interp, char *name, char *index,
42
                        int flags));
43
static void     TableGeometryRequest _ANSI_ARGS_((Table *tablePtr));
44
static void     TableAdjustActive _ANSI_ARGS_((register Table *tablePtr));
45
static void     TableAdjustParams _ANSI_ARGS_((register Table *tablePtr));
46
static void     TableCursorEvent _ANSI_ARGS_((ClientData clientData));
47
static void     TableConfigCursor _ANSI_ARGS_((register Table *tablePtr));
48
static int      TableFetchSelection _ANSI_ARGS_((ClientData clientData,
49
                        int offset, char *buffer, int maxBytes));
50
static void     TableLostSelection _ANSI_ARGS_((ClientData clientData));
51
static Tk_RestrictAction TableRestrictProc _ANSI_ARGS_((ClientData arg,
52
                        XEvent *eventPtr));
53
static int      TableValidateChange _ANSI_ARGS_((Table *tablePtr, int r,
54
                        int c, char *old, char *new, int index));
55
static void     TableDeleteChars _ANSI_ARGS_((register Table *tablePtr,
56
                                              int index, int count));
57
static void     TableInsertChars _ANSI_ARGS_((register Table *tablePtr,
58
                                              int index, char *string));
59
static int      TableWidgetCmd _ANSI_ARGS_((ClientData clientData,
60
                        Tcl_Interp *interp, int argc, char **argv));
61
static void     TableDestroy _ANSI_ARGS_((ClientData clientdata));
62
static void     TableEventProc _ANSI_ARGS_((ClientData clientData,
63
                        XEvent *eventPtr));
64
static int      TableConfigure _ANSI_ARGS_((Tcl_Interp *interp,
65
                        Table *tablePtr, int argc, char **argv,
66
                        int flags, int forceUpdate));
67
static void     TableCmdDeletedProc _ANSI_ARGS_((ClientData clientData));
68
static int      TableCmd _ANSI_ARGS_((ClientData clientData,
69
                        Tcl_Interp *interp, int argc, char **argv));
70
 
71
/*
72
 * The list of command values for all the widget commands
73
 * We could use enum for many of these #defines, but it adds
74
 * just that much more code size...
75
 */
76
#define CMD_ACTIVATE    1       /* activate command a la listbox */
77
#define CMD_BBOX        3       /* bounding box of cell <index> */
78
#define CMD_BORDER      5       /* border movement function */
79
#define CMD_CGET        7       /* basic cget widget command */
80
#define CMD_CLEAR       8       /* clear state command */
81
#define CMD_CONFIGURE   9       /* general configure command */
82
#define CMD_CURSELECTION 11     /* get current selected cell(s) */
83
#define CMD_CURVALUE    13      /* get current selection buffer */
84
#define CMD_DELETE      15      /* delete text in the selection */
85
#define CMD_FLUSH       17      /* flush the table cache */
86
#define CMD_GET         19      /* get mode a la listbox */
87
#define CMD_HEIGHT      21      /* (re)set row heights */
88
#define CMD_ICURSOR     23      /* set the insertion cursor */
89
#define CMD_INDEX       25      /* get an index */
90
#define CMD_INSERT      27      /* insert text at any position */
91
#define CMD_REREAD      31      /* reread the current selection */
92
#define CMD_SCAN        33      /* scan command a la listbox */
93
#define CMD_SEE         35      /* see command a la listbox */
94
#define CMD_SELECTION   37      /* selection command a la listbox */
95
#define CMD_SET         39      /* set command, to set multiple items */
96
#define CMD_TAG         41      /* tag command menu */
97
#define CMD_VALIDATE    43      /* validate contents of active cell */
98
#define CMD_VERSION     45      /* hidden command to return version */
99
#define CMD_WIDTH       47      /* (re)set column widths */
100
#define CMD_WINDOW      49      /* manage embedded windows */
101
#define CMD_XVIEW       51      /* change x view of widget (for scrollbars) */
102
#define CMD_YVIEW       53      /* change y view of widget (for scrollbars) */
103
 
104
/* The list of commands for the command parser */
105
 
106
static Cmd_Struct main_cmds[] = {
107
  {"activate",          CMD_ACTIVATE},
108
  {"bbox",              CMD_BBOX},
109
  {"border",            CMD_BORDER},
110
  {"cget",              CMD_CGET},
111
  {"clear",             CMD_CLEAR},
112
  {"configure",         CMD_CONFIGURE},
113
  {"curselection",      CMD_CURSELECTION},
114
  {"curvalue",          CMD_CURVALUE},
115
  {"delete",            CMD_DELETE},
116
  {"flush",             CMD_FLUSH},
117
  {"get",               CMD_GET},
118
  {"height",            CMD_HEIGHT},
119
  {"icursor",           CMD_ICURSOR},
120
  {"index",             CMD_INDEX},
121
  {"insert",            CMD_INSERT},
122
  {"reread",            CMD_REREAD},
123
  {"scan",              CMD_SCAN},
124
  {"see",               CMD_SEE},
125
  {"selection",         CMD_SELECTION},
126
  {"set",               CMD_SET},
127
  {"tag",               CMD_TAG},
128
  {"validate",          CMD_VALIDATE},
129
  {"version",           CMD_VERSION},
130
  {"window",            CMD_WINDOW},
131
  {"width",             CMD_WIDTH},
132
  {"xview",             CMD_XVIEW},
133
  {"yview",             CMD_YVIEW},
134
  {"", 0}
135
};
136
 
137
/* selection subcommands */
138
#define SEL_ANCHOR      1       /* set selection anchor */
139
#define SEL_CLEAR       2       /* clear list from selection */
140
#define SEL_INCLUDES    3       /* query items inclusion in selection */
141
#define SEL_SET         4       /* include items in selection */
142
 
143
static Cmd_Struct sel_cmds[]= {
144
  {"anchor",     SEL_ANCHOR},
145
  {"clear",      SEL_CLEAR},
146
  {"includes",   SEL_INCLUDES},
147
  {"set",        SEL_SET},
148
  {"",           0 }
149
};
150
 
151
/* -selecttype selection type options */
152
/* These alter how the selection set/clear commands behave */
153
#define SEL_ROW         (1<<0)
154
#define SEL_COL         (1<<1)
155
#define SEL_BOTH        (1<<2)
156
#define SEL_CELL        (1<<3)
157
#define SEL_NONE        (1<<4)
158
 
159
static Cmd_Struct sel_vals[]= {
160
  {"row",        SEL_ROW},
161
  {"col",        SEL_COL},
162
  {"both",       SEL_BOTH},
163
  {"cell",       SEL_CELL},
164
  {"",           0 }
165
};
166
 
167
/* clear subcommands */
168
#define CLEAR_TAGS      (1<<0)
169
#define CLEAR_SIZES     (1<<1)
170
#define CLEAR_CACHE     (1<<2)
171
static Cmd_Struct clear_cmds[] = {
172
  {"tags",      CLEAR_TAGS},
173
  {"sizes",     CLEAR_SIZES},
174
  {"cache",     CLEAR_CACHE},
175
  {"all",       CLEAR_TAGS | CLEAR_SIZES | CLEAR_CACHE},
176
  {"",          0}
177
};
178
 
179
/* -resizeborders options */
180
static Cmd_Struct resize_vals[]= {
181
  {"row",        SEL_ROW},              /* allow rows to be dragged */
182
  {"col",        SEL_COL},              /* allow cols to be dragged */
183
  {"both",       SEL_ROW|SEL_COL},      /* allow either to be dragged */
184
  {"none",       SEL_NONE},             /* allow nothing to be dragged */
185
  {"",           0 }
186
};
187
 
188
/* insert/delete subcommands */
189
#define MOD_ACTIVE      1
190
#define MOD_COLS        2
191
#define MOD_ROWS        3
192
static Cmd_Struct mod_cmds[] = {
193
  {"active",    MOD_ACTIVE},
194
  {"cols",      MOD_COLS},
195
  {"rows",      MOD_ROWS},
196
  {"", 0}
197
};
198
 
199
/* border subcommands */
200
#define BD_MARK         1
201
#define BD_DRAGTO       2
202
static Cmd_Struct bd_cmds[] = {
203
  {"mark",      BD_MARK},
204
  {"dragto",    BD_DRAGTO},
205
  {"", 0}
206
};
207
 
208
/* drawmode values */
209
/* The display redraws with a pixmap using TK function calls */
210
#define DRAW_MODE_SLOW          (1<<0)
211
/* The redisplay is direct to the screen, but TK function calls are still
212
 * used to give correct 3-d border appearance and thus remain compatible
213
 * with other TK apps */
214
#define DRAW_MODE_TK_COMPAT     (1<<1)
215
/* the redisplay goes straight to the screen and the 3d borders are rendered
216
 * with a single pixel wide line only. It cheats and uses the internal
217
 * border structure to do the borders */
218
#define DRAW_MODE_FAST          (1<<2)
219
#define DRAW_MODE_SINGLE        (1<<3)
220
 
221
static Cmd_Struct drawmode_vals[] = {
222
  {"fast",              DRAW_MODE_FAST},
223
  {"compatible",        DRAW_MODE_TK_COMPAT},
224
  {"slow",              DRAW_MODE_SLOW},
225
  {"single",            DRAW_MODE_SINGLE},
226
  {"", 0}
227
};
228
 
229
/* stretchmode values */
230
#define STRETCH_MODE_NONE       (1<<0)  /* No additional pixels will be
231
                                           added to rows or cols */
232
#define STRETCH_MODE_UNSET      (1<<1)  /* All default rows or columns will
233
                                           be stretched to fill the screen */
234
#define STRETCH_MODE_ALL        (1<<2)  /* All rows/columns will be padded
235
                                           to fill the window */
236
#define STRETCH_MODE_LAST       (1<<3)  /* Stretch last elememt to fill
237
                                           window */
238
#define STRETCH_MODE_FILL       (1<<4)  /* More ROWS in Window */
239
 
240
static Cmd_Struct stretch_vals[] = {
241
  {"none",      STRETCH_MODE_NONE},
242
  {"unset",     STRETCH_MODE_UNSET},
243
  {"all",       STRETCH_MODE_ALL},
244
  {"last",      STRETCH_MODE_LAST},
245
  {"fill",      STRETCH_MODE_FILL},
246
  {"", 0}
247
};
248
 
249
static Cmd_Struct state_vals[]= {
250
  {"normal",     STATE_NORMAL},
251
  {"disabled",   STATE_DISABLED},
252
  {"",           0 }
253
};
254
 
255
/* The widget configuration table */
256
static Tk_CustomOption drawOpt = { Cmd_OptionSet, Cmd_OptionGet,
257
                                   (ClientData)(&drawmode_vals) };
258
static Tk_CustomOption resizeTypeOpt = { Cmd_OptionSet, Cmd_OptionGet,
259
                                         (ClientData)(&resize_vals) };
260
static Tk_CustomOption stretchOpt = { Cmd_OptionSet, Cmd_OptionGet,
261
                                      (ClientData)(&stretch_vals) };
262
static Tk_CustomOption selTypeOpt = { Cmd_OptionSet, Cmd_OptionGet,
263
                                      (ClientData)(&sel_vals) };
264
static Tk_CustomOption stateTypeOpt = { Cmd_OptionSet, Cmd_OptionGet,
265
                                        (ClientData)(&state_vals) };
266
 
267
static Tk_ConfigSpec TableConfig[] = {
268
  {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", "center",
269
   Tk_Offset(Table, defaultTag.anchor), 0 },
270
  {TK_CONFIG_BOOLEAN, "-autoclear", "autoClear", "AutoClear", "0",
271
   Tk_Offset(Table, autoClear), 0 },
272
  {TK_CONFIG_BORDER, "-background", "background", "Background", NORMAL_BG,
273
   Tk_Offset(Table, defaultTag.bg), 0 },
274
  {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, (char *) NULL, 0, 0},
275
  {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *) NULL, 0, 0},
276
  {TK_CONFIG_CURSOR, "-bordercursor", "borderCursor", "Cursor", "crosshair",
277
   Tk_Offset(Table, bdcursor), TK_CONFIG_NULL_OK },
278
  {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", "1",
279
   Tk_Offset(Table, borderWidth), 0 },
280
  {TK_CONFIG_STRING, "-browsecommand", "browseCommand", "BrowseCommand", "",
281
   Tk_Offset(Table, browseCmd), TK_CONFIG_NULL_OK},
282
  {TK_CONFIG_SYNONYM, "-browsecmd", "browseCommand", (char *) NULL,
283
   (char *) NULL, 0, TK_CONFIG_NULL_OK},
284
  {TK_CONFIG_BOOLEAN, "-cache", "cache", "Cache", "0",
285
   Tk_Offset(Table, caching), 0},
286
  {TK_CONFIG_INT, "-colorigin", "colOrigin", "Origin", "0",
287
   Tk_Offset(Table, colOffset), 0 },
288
  {TK_CONFIG_INT, "-cols", "cols", "Cols", "10",
289
   Tk_Offset(Table, cols), 0 },
290
  {TK_CONFIG_STRING, "-colseparator", "colSeparator", "Separator", NULL,
291
   Tk_Offset(Table, colSep), TK_CONFIG_NULL_OK },
292
  {TK_CONFIG_CUSTOM, "-colstretchmode", "colStretch", "StretchMode", "none",
293
   Tk_Offset (Table, colStretch), 0 , &stretchOpt },
294
  {TK_CONFIG_STRING, "-coltagcommand", "colTagCommand", "TagCommand", NULL,
295
   Tk_Offset(Table, colTagCmd), TK_CONFIG_NULL_OK },
296
  {TK_CONFIG_INT, "-colwidth", "colWidth", "ColWidth", "10",
297
   Tk_Offset(Table, defColWidth), 0 },
298
  {TK_CONFIG_STRING, "-command", "command", "Command", "",
299
   Tk_Offset(Table, command), TK_CONFIG_NULL_OK},
300
  {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", "xterm",
301
   Tk_Offset(Table, cursor), TK_CONFIG_NULL_OK },
302
  {TK_CONFIG_CUSTOM, "-drawmode", "drawMode", "DrawMode", "compatible",
303
   Tk_Offset(Table, drawMode), 0, &drawOpt },
304
  {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
305
   "ExportSelection", "1", Tk_Offset(Table, exportSelection), 0},
306
  {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL, (char *) NULL, 0, 0},
307
  {TK_CONFIG_BOOLEAN, "-flashmode", "flashMode", "FlashMode", "0",
308
   Tk_Offset(Table, flashMode), 0 },
309
  {TK_CONFIG_INT, "-flashtime", "flashTime", "FlashTime", "2",
310
   Tk_Offset(Table, flashTime), 0 },
311
  {TK_CONFIG_FONT, "-font", "font", "Font",  DEF_TABLE_FONT,
312
   Tk_Offset(Table, defaultTag.tkfont), 0 },
313
  {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground", "black",
314
   Tk_Offset(Table, defaultTag.fg), 0 },
315
  {TK_CONFIG_INT, "-height", "height", "Height", "0",
316
   Tk_Offset(Table, maxReqRows), 0 },
317
  {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
318
   "HighlightBackground", NORMAL_BG, Tk_Offset(Table, highlightBgColorPtr), 0},
319
  {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
320
   HIGHLIGHT, Tk_Offset(Table, highlightColorPtr), 0 },
321
  {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
322
   "HighlightThickness", "2", Tk_Offset(Table, highlightWidth), 0 },
323
  {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
324
   "Black", Tk_Offset(Table, insertBg), 0 },
325
  {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
326
   "0", Tk_Offset(Table, insertBorderWidth), TK_CONFIG_COLOR_ONLY},
327
  {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
328
   "0", Tk_Offset(Table, insertBorderWidth), TK_CONFIG_MONO_ONLY},
329
  {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime", "300",
330
   Tk_Offset(Table, insertOffTime), 0},
331
  {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime", "600",
332
   Tk_Offset(Table, insertOnTime), 0},
333
  {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth", "2",
334
   Tk_Offset(Table, insertWidth), 0},
335
   {TK_CONFIG_BOOLEAN, "-invertselected", "invertSelected", "InvertSelected",
336
    "0", Tk_Offset(Table, invertSelected), 0},
337
  {TK_CONFIG_PIXELS, "-maxheight", "maxHeight", "MaxHeight", "600",
338
   Tk_Offset(Table, maxReqHeight), 0 },
339
  {TK_CONFIG_PIXELS, "-maxwidth", "maxWidth", "MaxWidth", "800",
340
   Tk_Offset(Table, maxReqWidth), 0 },
341
  {TK_CONFIG_BOOLEAN, "-multiline", "multiline", "Multiline", "1",
342
   Tk_Offset(Table, defaultTag.multiline), 0 },
343
  {TK_CONFIG_PIXELS, "-padx", "padX", "Pad", "2", Tk_Offset(Table, padX), 0},
344
  {TK_CONFIG_PIXELS, "-pady", "padY", "Pad", "1", Tk_Offset(Table, padY), 0},
345
  {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", "sunken",
346
   Tk_Offset(Table, defaultTag.relief), 0 },
347
  {TK_CONFIG_CUSTOM, "-resizeborders", "resizeBorders", "ResizeBorders",
348
   "both", Tk_Offset(Table, resize), 0, &resizeTypeOpt },
349
  {TK_CONFIG_PIXELS, "-rowheight", "rowHeight", "RowHeight", "1",
350
   Tk_Offset(Table, defRowHeight), 0 },
351
  {TK_CONFIG_INT, "-roworigin", "rowOrigin", "Origin", "0",
352
   Tk_Offset(Table, rowOffset), 0 },
353
  {TK_CONFIG_INT, "-rows", "rows", "Rows", "10", Tk_Offset(Table, rows), 0 },
354
  {TK_CONFIG_STRING, "-rowseparator", "rowSeparator", "Separator", NULL,
355
   Tk_Offset(Table, rowSep), TK_CONFIG_NULL_OK },
356
  {TK_CONFIG_CUSTOM, "-rowstretchmode", "rowStretch", "StretchMode", "none",
357
   Tk_Offset(Table, rowStretch), 0 , &stretchOpt },
358
  {TK_CONFIG_STRING, "-rowtagcommand", "rowTagCommand", "TagCommand", NULL,
359
   Tk_Offset(Table, rowTagCmd), TK_CONFIG_NULL_OK },
360
  {TK_CONFIG_SYNONYM, "-selcmd", "selectionCommand", (char *) NULL,
361
   (char *) NULL, 0, TK_CONFIG_NULL_OK},
362
  {TK_CONFIG_STRING, "-selectioncommand", "selectionCommand",
363
   "SelectionCommand", NULL, Tk_Offset(Table, selCmd), TK_CONFIG_NULL_OK },
364
  {TK_CONFIG_STRING, "-selectmode", "selectMode", "SelectMode", "browse",
365
   Tk_Offset(Table, selectMode), TK_CONFIG_NULL_OK },
366
  {TK_CONFIG_BOOLEAN, "-selecttitles", "selectTitles", "SelectTitles", "0",
367
   Tk_Offset(Table, selectTitles), 0 },
368
  {TK_CONFIG_CUSTOM, "-selecttype", "selectType", "SelectType", "cell",
369
   Tk_Offset(Table, selectType), 0, &selTypeOpt },
370
  {TK_CONFIG_CUSTOM, "-state", "state", "State", "normal",
371
   Tk_Offset(Table, state), 0, &stateTypeOpt},
372
  {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus", (char *) NULL,
373
   Tk_Offset(Table, takeFocus), TK_CONFIG_NULL_OK },
374
  {TK_CONFIG_INT, "-titlecols", "titleCols", "TitleCols", "0",
375
   Tk_Offset(Table, titleCols), TK_CONFIG_NULL_OK },
376
  {TK_CONFIG_INT, "-titlerows", "titleRows", "TitleRows", "0",
377
   Tk_Offset(Table, titleRows), TK_CONFIG_NULL_OK },
378
  {TK_CONFIG_BOOLEAN, "-usecommand", "useCommand", "UseCommand", "1",
379
   Tk_Offset(Table, useCmd), 0},
380
  {TK_CONFIG_STRING, "-variable", "variable", "Variable", (char *) NULL,
381
   Tk_Offset(Table, arrayVar), TK_CONFIG_NULL_OK },
382
  {TK_CONFIG_BOOLEAN, "-validate", "validate", "Validate", "0",
383
   Tk_Offset(Table, validate), 0 },
384
  {TK_CONFIG_STRING, "-validatecommand", "validateCommand", "ValidateCommand",
385
   "", Tk_Offset(Table, valCmd), TK_CONFIG_NULL_OK},
386
  {TK_CONFIG_SYNONYM, "-vcmd", "validateCommand", (char *) NULL,
387
   (char *) NULL, 0, TK_CONFIG_NULL_OK},
388
  {TK_CONFIG_INT, "-width", "width", "Width", "0",
389
   Tk_Offset(Table, maxReqCols), 0 },
390
  {TK_CONFIG_BOOLEAN, "-wrap", "wrap", "Wrap", "0",
391
   Tk_Offset(Table, defaultTag.wrap), 0 },
392
  {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
393
   NULL, Tk_Offset(Table, xScrollCmd), TK_CONFIG_NULL_OK },
394
  {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
395
   NULL, Tk_Offset(Table, yScrollCmd), TK_CONFIG_NULL_OK },
396
  {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
397
   (char *) NULL, 0, 0 }
398
};
399
 
400
/*
401
 * This specifies the configure options that will cause an update to
402
 * occur, so we should have a quick lookup table for them.
403
 * Keep this in sync with the above values.
404
 */
405
static Cmd_Struct update_config[] = {
406
  {"-anchor",           1},  {"-background",    1},
407
  {"-bg",               1},  {"-bd",            1},
408
  {"-borderwidth",      1},  {"-cache",         1},
409
  {"-command",          1},  {"-colorigin",     1},
410
  {"-cols",             1},  {"-colstretchmode",        1},
411
  {"-coltagcommand",    1},  {"-drawmode",      1},
412
  {"-fg",               1},  {"-font",          1},
413
  {"-foreground",       1},
414
  {"-height",           1},  {"-highlightbackground",   1},
415
  {"-highlightcolor",   1},  {"-highlightthickness",    1},
416
  {"-insertbackground", 1},  {"-insertborderwidth",     1},
417
  {"-insertwidth",      1},  {"-invertselected",        1},
418
  {"-maxheight",        1},  {"-maxwidth",              1},
419
  {"-multiline",        1},
420
  {"-padx",             1},  {"-pady",          1},
421
  {"-relief",           1},  {"-roworigin",     1},
422
  {"-rows",             1},  {"-rowstretchmode",        1},
423
  {"-rowtagcommand",    1},  {"-state",         1},
424
  {"-titlecols",        1},  {"-titlerows",     1},
425
  {"-usecommand",       1},  {"-variable",              1},
426
  {"-width",            1},  {"-wrap",          1},
427
  {"-xscrollcommand",   1},  {"-yscrollcommand",        1},
428
  {"", 0},
429
};
430
 
431
/*
432
 * END HEADER INFORMATION
433
 */
434
 
435
/*
436
 *----------------------------------------------------------------------
437
 *
438
 * TableFlushCache --
439
 *      Flushes the internal cache of the table.
440
 *
441
 * Results:
442
 *      None.
443
 *
444
 * Side effects:
445
 *      None.
446
 *
447
 *----------------------------------------------------------------------
448
 */
449
INLINE static void
450
TableFlushCache(register Table *tablePtr)
451
{
452
  /* Just get rid of it and reinit it */
453
  Tcl_DeleteHashTable(tablePtr->cache);
454
  ckfree((char *) (tablePtr->cache));
455
  tablePtr->cache = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
456
  Tcl_InitHashTable(tablePtr->cache, TCL_STRING_KEYS);
457
}
458
 
459
 
460
 
461
/*
462
 *----------------------------------------------------------------------
463
 *
464
 * TableRefresh --
465
 *      Refreshes an area of the table based on the mode.
466
 *      row,col in real coords (0-based)
467
 *
468
 * Results:
469
 *      Will cause redraw for visible cells
470
 *
471
 * Side effects:
472
 *      None.
473
 *
474
 *----------------------------------------------------------------------
475
 */
476
void
477
TableRefresh(register Table *tablePtr, int row, int col, int mode)
478
{
479
  int x, y, w, h;
480
 
481
  if (mode & CELL) {
482
    if (TableCellVCoords(tablePtr, row, col, &x, &y, &w, &h, 0)) {
483
      TableInvalidate(tablePtr, x, y, w, h, mode);
484
    }
485
  } else if (mode & ROW) {
486
    /* get the position of the leftmost cell in the row */
487
    if ((mode & INV_FILL) && row < tablePtr->topRow) {
488
      /* Invalidate whole table */
489
      TableInvalidateAll(tablePtr, mode);
490
    } else if (TableCellVCoords(tablePtr, row, tablePtr->leftCol,
491
                         &x, &y, &w, &h, 0)) {
492
      /* Invalidate from this row, maybe to end */
493
      TableInvalidate(tablePtr, 0, y, Tk_Width(tablePtr->tkwin),
494
                      (mode&INV_FILL)?Tk_Height(tablePtr->tkwin):h, mode);
495
    }
496
  } else if (mode & COL) {
497
    /* get the position of the topmost cell on the column */
498
    if ((mode & INV_FILL) && col < tablePtr->leftCol) {
499
      /* Invalidate whole table */
500
      TableInvalidateAll(tablePtr, mode);
501
    } else if (TableCellVCoords(tablePtr, tablePtr->topRow, col,
502
                         &x, &y, &w, &h, 0)) {
503
      /* Invalidate from this column, maybe to end */
504
      TableInvalidate(tablePtr, x, 0,
505
                      (mode&INV_FILL)?Tk_Width(tablePtr->tkwin):w,
506
                      Tk_Height(tablePtr->tkwin), mode);
507
    }
508
  }
509
}
510
 
511
/*
512
 *----------------------------------------------------------------------
513
 *
514
 * TableClear --
515
 *      Clears state information about the table.
516
 *
517
 * Results:
518
 *      Cached info can be lost.  Returns valid Tcl result.
519
 *
520
 * Side effects:
521
 *      Can cause redraw.
522
 *
523
 *----------------------------------------------------------------------
524
 */
525
static int
526
TableClear(register Table *tablePtr, int mode, char *first, char *last)
527
{
528
  int redraw = 0;
529
 
530
  if (mode == 0) return TCL_ERROR;
531
 
532
  if (first == NULL) {
533
    if (mode & CLEAR_TAGS) {
534
      Tcl_DeleteHashTable(tablePtr->rowStyles);
535
      Tcl_DeleteHashTable(tablePtr->colStyles);
536
      Tcl_DeleteHashTable(tablePtr->cellStyles);
537
      Tcl_DeleteHashTable(tablePtr->flashCells);
538
      Tcl_DeleteHashTable(tablePtr->selCells);
539
 
540
      /* style hash tables */
541
      Tcl_InitHashTable(tablePtr->rowStyles, TCL_ONE_WORD_KEYS);
542
      Tcl_InitHashTable(tablePtr->colStyles, TCL_ONE_WORD_KEYS);
543
      Tcl_InitHashTable(tablePtr->cellStyles, TCL_STRING_KEYS);
544
 
545
      /* special style hash tables */
546
      Tcl_InitHashTable(tablePtr->flashCells, TCL_STRING_KEYS);
547
      Tcl_InitHashTable(tablePtr->selCells, TCL_STRING_KEYS);
548
    }
549
 
550
    if (mode & CLEAR_SIZES) {
551
      Tcl_DeleteHashTable(tablePtr->colWidths);
552
      Tcl_DeleteHashTable(tablePtr->rowHeights);
553
 
554
      /* style hash tables */
555
      Tcl_InitHashTable(tablePtr->colWidths, TCL_ONE_WORD_KEYS);
556
      Tcl_InitHashTable(tablePtr->rowHeights, TCL_ONE_WORD_KEYS);
557
    }
558
 
559
    if (mode & CLEAR_CACHE) {
560
      TableFlushCache(tablePtr);
561
      /* If we were caching and we have no other data source,
562
       * invalidate all the cells */
563
      if (tablePtr->dataSource == DATA_CACHE) {
564
        TableGetActiveBuf(tablePtr);
565
      }
566
    }
567
    redraw = 1;
568
  } else {
569
    int row, col, r1, r2, c1, c2;
570
    Tcl_HashEntry *entryPtr;
571
    char buf[INDEX_BUFSIZE];
572
 
573
    if (TableGetIndex(tablePtr, first, &row, &col) == TCL_ERROR ||
574
        (last != NULL && TableGetIndex(tablePtr, last, &r2, &c2)==TCL_ERROR)) {
575
      return TCL_ERROR;
576
    }
577
    if (last == NULL) {
578
      r1 = r2 = row;
579
      c1 = c2 = col;
580
    } else {
581
      r1 = MIN(row,r2); r2 = MAX(row,r2);
582
      c1 = MIN(col,c2); c2 = MAX(col,c2);
583
    }
584
    for (row = r1; row <= r2; row++) {
585
      /* Note that *Styles entries are user based (no offset)
586
       * while size entries are 0-based (real) */
587
      if ((mode & CLEAR_TAGS) &&
588
          (entryPtr = Tcl_FindHashEntry(tablePtr->rowStyles, (char *) row))) {
589
        Tcl_DeleteHashEntry(entryPtr);
590
        redraw = 1;
591
      }
592
 
593
      if ((mode & CLEAR_SIZES) &&
594
          (entryPtr = Tcl_FindHashEntry(tablePtr->rowHeights,
595
                                        (char *) row-tablePtr->rowOffset))) {
596
        Tcl_DeleteHashEntry(entryPtr);
597
        redraw = 1;
598
      }
599
 
600
      for (col = c1; col <= c2; col++) {
601
        TableMakeArrayIndex(row, col, buf);
602
 
603
        if (mode & CLEAR_TAGS) {
604
          if ((row == r1) && (entryPtr = Tcl_FindHashEntry(tablePtr->colStyles,
605
                                                           (char *) col))) {
606
            Tcl_DeleteHashEntry(entryPtr);
607
            redraw = 1;
608
          }
609
          if ((entryPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf))) {
610
            Tcl_DeleteHashEntry(entryPtr);
611
            redraw = 1;
612
          }
613
          if ((entryPtr = Tcl_FindHashEntry(tablePtr->flashCells, buf))) {
614
            Tcl_DeleteHashEntry(entryPtr);
615
            redraw = 1;
616
          }
617
          if ((entryPtr = Tcl_FindHashEntry(tablePtr->selCells, buf))) {
618
            Tcl_DeleteHashEntry(entryPtr);
619
            redraw = 1;
620
          }
621
        }
622
 
623
        if ((mode & CLEAR_SIZES) && row == r1 &&
624
            (entryPtr = Tcl_FindHashEntry(tablePtr->colWidths, (char *)
625
                                          col-tablePtr->colOffset))) {
626
          Tcl_DeleteHashEntry(entryPtr);
627
          redraw = 1;
628
        }
629
 
630
        if ((mode & CLEAR_CACHE) &&
631
            (entryPtr = Tcl_FindHashEntry(tablePtr->cache, buf))) {
632
          Tcl_DeleteHashEntry(entryPtr);
633
          /* if the cache is our data source,
634
           * we need to invalidate the cells changed */
635
          if ((tablePtr->dataSource == DATA_CACHE) &&
636
              (row-tablePtr->rowOffset == tablePtr->activeRow &&
637
               col-tablePtr->colOffset == tablePtr->activeCol))
638
            TableGetActiveBuf(tablePtr);
639
          redraw = 1;
640
        }
641
      }
642
    }
643
  }
644
  /* This could be more sensitive about what it updates,
645
   * but that can actually be a lot more costly in some cases */
646
  if (redraw) {
647
    if (mode & CLEAR_SIZES) {
648
      TableAdjustParams(tablePtr);
649
      /* rerequest geometry */
650
      TableGeometryRequest(tablePtr);
651
    }
652
    TableInvalidateAll(tablePtr, 0);
653
  }
654
  return TCL_OK;
655
}
656
 
657
/*
658
 *----------------------------------------------------------------------
659
 *
660
 * TableGetGc --
661
 *      Gets a GC corresponding to the tag structure passed.
662
 *
663
 * Results:
664
 *      Returns usable GC.
665
 *
666
 * Side effects:
667
 *      None
668
 *
669
 *----------------------------------------------------------------------
670
 */
671
INLINE static void
672
TableGetGc(Display *display, Drawable d, TableTag *tagPtr, GC *tagGc)
673
{
674
  XGCValues gcValues;
675
  gcValues.foreground = Tk_3DBorderColor(tagPtr->fg)->pixel;
676
  gcValues.background = Tk_3DBorderColor(tagPtr->bg)->pixel;
677
  gcValues.font = Tk_FontId(tagPtr->tkfont);
678
  if (*tagGc == NULL) {
679
    gcValues.graphics_exposures = False;
680
    *tagGc = XCreateGC(display, d,
681
                       GCForeground|GCBackground|GCFont|GCGraphicsExposures,
682
                       &gcValues);
683
  } else {
684
    XChangeGC(display, *tagGc, GCForeground|GCBackground|GCFont, &gcValues);
685
  }
686
}
687
 
688
#define TableFreeGc     XFreeGC
689
 
690
/*
691
 *----------------------------------------------------------------------
692
 *
693
 * TableRedrawHighlight --
694
 *      Redraws just the highlight for the window
695
 *
696
 * Results:
697
 *      None.
698
 *
699
 * Side effects:
700
 *      None
701
 *
702
 *----------------------------------------------------------------------
703
 */
704
INLINE static void
705
TableRedrawHighlight(Table *tablePtr)
706
{
707
  if ((tablePtr->flags & REDRAW_BORDER) && tablePtr->highlightWidth > 0) {
708
    GC gc = Tk_GCForColor((tablePtr->flags & HAS_FOCUS)
709
                          ?(tablePtr->highlightColorPtr)
710
                          :(tablePtr->highlightBgColorPtr),
711
                          Tk_WindowId(tablePtr->tkwin));
712
    Tk_DrawFocusHighlight(tablePtr->tkwin, gc, tablePtr->highlightWidth,
713
                          Tk_WindowId(tablePtr->tkwin));
714
  }
715
  tablePtr->flags &= ~REDRAW_BORDER;
716
}
717
 
718
/*
719
 *--------------------------------------------------------------
720
 *
721
 * TableUndisplay --
722
 *      This procedure removes the contents of a table window
723
 *      that have been moved offscreen.
724
 *
725
 * Results:
726
 *      Embedded windows can be unmapped.
727
 *
728
 * Side effects:
729
 *      Information disappears from the screen.
730
 *
731
 *--------------------------------------------------------------
732
 */
733
static void
734
TableUndisplay(register Table *tablePtr)
735
{
736
  register int *seen = tablePtr->seen;
737
  int row, col;
738
 
739
  TableGetLastCell(tablePtr, &row, &col);
740
  if (seen[0] != -1) {
741
    if (seen[0] < tablePtr->topRow) {
742
      /* Remove now hidden rows */
743
      EmbWinUnmap(tablePtr, seen[0], tablePtr->topRow-1, 0, seen[3]);
744
    }
745
    if (seen[1] < tablePtr->leftCol) {
746
      /* Remove now hidden cols */
747
      EmbWinUnmap(tablePtr, 0, seen[2], seen[1], tablePtr->leftCol-1);
748
    }
749
    if (seen[2] > row) {
750
      /* Remove now off-screen rows */
751
      EmbWinUnmap(tablePtr, row+1, seen[2], 0, seen[3]);
752
    }
753
    if (seen[3] > col) {
754
      /* Remove now off-screen cols */
755
      EmbWinUnmap(tablePtr, 0, seen[2], col+1, seen[3]);
756
    }
757
  }
758
  seen[0] = tablePtr->topRow;
759
  seen[1] = tablePtr->leftCol;
760
  seen[2] = row;
761
  seen[3] = col;
762
}
763
 
764
/*
765
 *--------------------------------------------------------------
766
 *
767
 * TableDisplay --
768
 *      This procedure redraws the contents of a table window.
769
 *      The conditional code in this function is due to these factors:
770
 *              o Lack of XSetClipRectangles on Windows
771
 *
772
 * Results:
773
 *      None.
774
 *
775
 * Side effects:
776
 *      Information appears on the screen.
777
 *
778
 *--------------------------------------------------------------
779
 */
780
static void
781
TableDisplay(ClientData clientdata)
782
{
783
  register Table *tablePtr = (Table *) clientdata;
784
  Tk_Window tkwin = tablePtr->tkwin;
785
  Display *display = tablePtr->display;
786
  Drawable window;
787
#ifdef _WIN32
788
  Drawable clipWind;
789
#else
790
  XRectangle clipRect;
791
#endif
792
  int rowFrom, rowTo, colFrom, colTo,
793
    invalidX, invalidY, invalidWidth, invalidHeight,
794
    x, y, width, height, itemX, itemY, itemW, itemH,
795
    row, col, urow, ucol, cx, cy, cw, ch, bd,
796
    numBytes, new, boundW, boundH, maxW, maxH,
797
    originX, originY, activeCell, clipRectSet, shouldInvert;
798
  GC tagGc = NULL, topGc, bottomGc;
799
  char *string = NULL;
800
  char buf[INDEX_BUFSIZE];
801
  TableTag *tagPtr = NULL, *titlePtr, *selPtr, *activePtr, *flashPtr,
802
    *rowPtr, *colPtr;
803
  Tcl_HashEntry *entryPtr;
804
  static XPoint rect[3] = { {0, 0}, {0, 0}, {0, 0} };
805
  Tcl_HashTable *colTagsCache = NULL;
806
  Tk_TextLayout textLayout = NULL;
807
  TableEmbWindow *ewPtr;
808
 
809
  if ((tablePtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
810
    return;
811
  }
812
  tablePtr->flags &= ~REDRAW_PENDING;
813
 
814
  bd = tablePtr->borderWidth;
815
  boundW = Tk_Width(tkwin)-tablePtr->highlightWidth;
816
  boundH = Tk_Height(tkwin)-tablePtr->highlightWidth;
817
 
818
  /* Constrain drawable to not include highlight borders */
819
  invalidX = MAX(tablePtr->highlightWidth, tablePtr->invalidX);
820
  invalidY = MAX(tablePtr->highlightWidth, tablePtr->invalidY);
821
  invalidWidth = MIN(tablePtr->invalidWidth, MAX(1, boundW-invalidX));
822
  invalidHeight = MIN(tablePtr->invalidHeight, MAX(1, boundH-invalidY));
823
 
824
  /*
825
   * if we are using the slow drawing mode with a pixmap
826
   * create the pixmap and adjust x && y for offset in pixmap
827
   */
828
  if (tablePtr->drawMode == DRAW_MODE_SLOW) {
829
    window = Tk_GetPixmap(display, Tk_WindowId(tkwin),
830
                          invalidWidth, invalidHeight, Tk_Depth(tkwin));
831
  } else {
832
    window = Tk_WindowId(tkwin);
833
  }
834
#ifdef _WIN32
835
  clipWind = Tk_GetPixmap(display, window,
836
                          invalidWidth, invalidHeight, Tk_Depth(tkwin));
837
#endif
838
 
839
  /* set up the permanent tag styles */
840
  entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, "title");
841
  titlePtr = (TableTag *) Tcl_GetHashValue(entryPtr);
842
  entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, "sel");
843
  selPtr   = (TableTag *) Tcl_GetHashValue(entryPtr);
844
  entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, "active");
845
  activePtr= (TableTag *) Tcl_GetHashValue(entryPtr);
846
  entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, "flash");
847
  flashPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
848
 
849
  /* find out the cells represented by the invalid region */
850
  TableWhatCell(tablePtr, invalidX, invalidY, &rowFrom, &colFrom);
851
  TableWhatCell(tablePtr, invalidX+invalidWidth-1,
852
                invalidY+invalidHeight-1, &rowTo, &colTo);
853
 
854
#ifdef DEBUG
855
  tcl_dprintf(tablePtr->interp, "display %d,%d => %d,%d",
856
              rowFrom+tablePtr->rowOffset, colFrom+tablePtr->colOffset,
857
              rowTo+tablePtr->rowOffset, colTo+tablePtr->colOffset);
858
#endif
859
 
860
  /*
861
   * Initialize colTagsCache hash table to cache column tag names.
862
   */
863
  colTagsCache = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
864
  Tcl_InitHashTable(colTagsCache, TCL_ONE_WORD_KEYS);
865
 
866
  /* Cycle through the cells and display them */
867
  for (row = rowFrom; row <= rowTo; row++) {
868
    /*
869
     * are we in the 'dead zone' between the
870
     * title rows and the first displayed row
871
     */
872
    if (row < tablePtr->topRow && row >= tablePtr->titleRows) {
873
      row = tablePtr->topRow;
874
    }
875
 
876
    /* Cache the row in user terms */
877
    urow = row+tablePtr->rowOffset;
878
 
879
    /* Get the row tag once for all iterations of col */
880
    rowPtr = FindRowColTag(tablePtr, urow, ROW);
881
 
882
    for (col = colFrom; col <= colTo; col++) {
883
      activeCell = 0;
884
      /*
885
       * are we in the 'dead zone' between the
886
       * title cols and the first displayed col
887
       */
888
      if (col < tablePtr->leftCol && col >= tablePtr->titleCols) {
889
        col = tablePtr->leftCol;
890
      }
891
 
892
      /* Cache the col in user terms */
893
      ucol = col+tablePtr->colOffset;
894
 
895
      /* put the use cell ref into a buffer for the hash lookups */
896
      TableMakeArrayIndex(urow, ucol, buf);
897
 
898
      /* get the coordinates for the cell */
899
      TableCellCoords(tablePtr, row, col, &x, &y, &width, &height);
900
 
901
      /* Constrain drawn size to the visual boundaries */
902
      if (width > boundW-x) {
903
        width = boundW-x;
904
      }
905
      if (height > boundH-y) {
906
        height = boundH-y;
907
      }
908
 
909
      /* Create the tag here */
910
      tagPtr = TableNewTag();
911
      /* First, merge in the default tag */
912
      TableMergeTag(tagPtr, &(tablePtr->defaultTag));
913
 
914
      if ((entryPtr = Tcl_FindHashEntry(tablePtr->winTable, buf)) != NULL) {
915
        ewPtr = (TableEmbWindow *) Tcl_GetHashValue(entryPtr);
916
 
917
        if (ewPtr->tkwin != NULL) {
918
          /* Display embedded window instead of text */
919
 
920
          /* if active, make it disabled to avoid unnecessary editing */
921
          if ((tablePtr->flags & HAS_ACTIVE)
922
              && row == tablePtr->activeRow && col == tablePtr->activeCol) {
923
            tablePtr->flags |= ACTIVE_DISABLED;
924
          }
925
 
926
          EmbWinDisplay(tablePtr, window, ewPtr, tagPtr,
927
                        x, y, width, height);
928
 
929
          if (tablePtr->drawMode == DRAW_MODE_SLOW) {
930
            /* Correctly adjust x && y with the offset */
931
            x -= invalidX;
932
            y -= invalidY;
933
          }
934
          Tk_Fill3DRectangle(tkwin, window, tagPtr->bg,
935
                             x, y, width, height, bd, TK_RELIEF_FLAT);
936
 
937
          goto ImageUsed;
938
        }
939
      }
940
 
941
      if (tablePtr->drawMode == DRAW_MODE_SLOW) {
942
        /* Correctly adjust x && y with the offset */
943
        x -= invalidX;
944
        y -= invalidY;
945
      }
946
 
947
      shouldInvert = 0;
948
      /*
949
       * get the combined tag structure for the cell
950
       * first clear out a new tag structure that we will build in
951
       * then add tags as we realize they belong.
952
       * Tags with the highest priority are added first
953
       */
954
 
955
      /*
956
       * Merge colPtr if it exists
957
       * let's see if we have the value cached already
958
       * if not, run the findColTag routine and cache the value
959
       */
960
      entryPtr = Tcl_CreateHashEntry(colTagsCache, (char *)ucol, &new);
961
      if (new) {
962
        colPtr = FindRowColTag(tablePtr, ucol, COL);
963
        Tcl_SetHashValue(entryPtr, colPtr);
964
      } else {
965
        colPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
966
      }
967
      if (colPtr != (TableTag *) NULL)
968
        TableMergeTag(tagPtr, colPtr);
969
      /* Merge rowPtr if it exists */
970
      if (rowPtr != (TableTag *) NULL)
971
        TableMergeTag(tagPtr, rowPtr);
972
      /* Am I in the titles */
973
      if (row < tablePtr->topRow || col < tablePtr->leftCol)
974
        TableMergeTag(tagPtr, titlePtr);
975
      /* Does this have a cell tag */
976
      if ((entryPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf)) != NULL)
977
        TableMergeTag(tagPtr, (TableTag *) Tcl_GetHashValue(entryPtr));
978
      /* is this cell selected? */
979
      if (Tcl_FindHashEntry(tablePtr->selCells, buf) != NULL) {
980
        if (tablePtr->invertSelected && !activeCell) {
981
          shouldInvert = 1;
982
        } else {
983
          TableMergeTag(tagPtr, selPtr);
984
        }
985
      }
986
      /* is this cell active? */
987
      if ((tablePtr->flags & HAS_ACTIVE) && tablePtr->state == STATE_NORMAL
988
          && row == tablePtr->activeRow && col == tablePtr->activeCol) {
989
        if (tagPtr->state == STATE_DISABLED) {
990
          tablePtr->flags |= ACTIVE_DISABLED;
991
        } else {
992
          TableMergeTag(tagPtr, activePtr);
993
          activeCell = 1;
994
          tablePtr->flags &= ~ACTIVE_DISABLED;
995
        }
996
      }
997
      /* if flash mode is on, is this cell flashing */
998
      if (tablePtr->flashMode &&
999
          Tcl_FindHashEntry(tablePtr->flashCells, buf) != NULL)
1000
        TableMergeTag(tagPtr, flashPtr);
1001
 
1002
      if (shouldInvert) TableInvertTag(tagPtr);
1003
 
1004
      /*
1005
       * first fill in a blank rectangle. This is left as a Tk call instead
1006
       * of a direct X call for Tk compatibilty. The TK_RELIEF_FLAT ensures
1007
       * that only XFillRectangle is called anyway so the speed is the same
1008
       */
1009
      Tk_Fill3DRectangle(tkwin, window, tagPtr->bg,
1010
                         x, y, width, height, bd, TK_RELIEF_FLAT);
1011
 
1012
      /*
1013
       * If an image is in the tag, draw it
1014
       */
1015
      if (tagPtr->image != NULL) {
1016
        Tk_SizeOfImage(tagPtr->image, &itemW, &itemH);
1017
        /* Handle anchoring of image in cell space */
1018
        switch (tagPtr->anchor) {
1019
        case TK_ANCHOR_NW:
1020
        case TK_ANCHOR_W:
1021
        case TK_ANCHOR_SW:      /* western position */
1022
          originX = itemX = 0;
1023
          break;
1024
        case TK_ANCHOR_N:
1025
        case TK_ANCHOR_S:
1026
        case TK_ANCHOR_CENTER:  /* centered position */
1027
          itemX = MAX(0, (itemW-width)/2-bd);
1028
          originX = MAX(0, (width-itemW)/2);
1029
          break;
1030
        default:        /* eastern position */
1031
          itemX = MAX(0, itemW-width-2*bd);
1032
          originX = MAX(0, width-itemW);
1033
        }
1034
        switch (tagPtr->anchor) {
1035
        case TK_ANCHOR_N:
1036
        case TK_ANCHOR_NE:
1037
        case TK_ANCHOR_NW:      /* northern position */
1038
          originY = itemY = 0;
1039
          break;
1040
        case TK_ANCHOR_W:
1041
        case TK_ANCHOR_E:
1042
        case TK_ANCHOR_CENTER:  /* centered position */
1043
          itemY = MAX(0, (itemH-height)/2-bd);
1044
          originY = MAX(0, (height-itemH)/2);
1045
          break;
1046
        default:        /* southern position */
1047
          itemY = MAX(0, itemH-height-2*bd);
1048
          originY = MAX(0, height-itemH);
1049
        }
1050
        Tk_RedrawImage(tagPtr->image, itemX, itemY,
1051
                       MIN(itemW, width-originX-2*bd),
1052
                       MIN(itemH, height-originY-2*bd), window,
1053
                       x+originX+bd, y+originY+bd);
1054
        /* Jump to avoid display of the text value */
1055
        if (tagPtr->showtext == 0)
1056
          goto ImageUsed;
1057
      }
1058
 
1059
      /* get the GC for this particular blend of tags
1060
       * this creates the GC if it never existed, otherwise it
1061
       * modifies the one we have */
1062
      TableGetGc(display, window, tagPtr, &tagGc);
1063
 
1064
      /* if this is the active cell, use the buffer */
1065
      if (activeCell) {
1066
        string = tablePtr->activeBuf;
1067
      } else {
1068
        /* Is there a value in the cell? If so, draw it  */
1069
        string = TableGetCellValue(tablePtr, urow, ucol);
1070
      }
1071
 
1072
      numBytes = strlen(string);
1073
      /* If there is a string, show it */
1074
      if (activeCell || numBytes) {
1075
        /* get the dimensions of the string */
1076
        textLayout = Tk_ComputeTextLayout(tagPtr->tkfont, string, numBytes,
1077
                                          (tagPtr->wrap>0) ? width : 0,
1078
                                          tagPtr->justify,
1079
                                          (tagPtr->multiline>0) ? 0 :
1080
                                          TK_IGNORE_NEWLINES,
1081
                                          &itemW, &itemH);
1082
 
1083
        /*
1084
         * Set the origin coordinates of the string to draw using the anchor.
1085
         * origin represents the (x,y) coordinate of the lower left corner of
1086
         * the text box, relative to the internal (inside the border) window
1087
         */
1088
 
1089
        /* set the X origin first */
1090
        switch (tagPtr->anchor) {
1091
        case TK_ANCHOR_NW:
1092
        case TK_ANCHOR_W:
1093
        case TK_ANCHOR_SW:      /* western position */
1094
          originX = tablePtr->padX;
1095
          break;
1096
        case TK_ANCHOR_N:
1097
        case TK_ANCHOR_S:
1098
        case TK_ANCHOR_CENTER:  /* centered position */
1099
          originX = (width-itemW)/2 - bd;
1100
          break;
1101
        default:        /* eastern position */
1102
          originX = width-itemW-2*bd-tablePtr->padX;
1103
        }
1104
 
1105
        /* then set the Y origin */
1106
        switch (tagPtr->anchor) {
1107
        case TK_ANCHOR_N:
1108
        case TK_ANCHOR_NE:
1109
        case TK_ANCHOR_NW:      /* northern position */
1110
          originY = tablePtr->padY;
1111
          break;
1112
        case TK_ANCHOR_W:
1113
        case TK_ANCHOR_E:
1114
        case TK_ANCHOR_CENTER:  /* centered position */
1115
          originY = (height-itemH)/2 - bd;
1116
          break;
1117
        default:        /* southern position */
1118
          originY = height-itemH-2*bd-tablePtr->padY;
1119
        }
1120
 
1121
        /*
1122
         * if this is the selected cell and we are editing
1123
         * ensure that the cursor will be displayed
1124
         */
1125
        if (activeCell) {
1126
#if (TK_MINOR_VERSION > 0)
1127
          int insertByte;
1128
 
1129
          insertByte = Tcl_UtfAtIndex(string, tablePtr->icursor) - string;
1130
          Tk_CharBbox(textLayout, MIN(numBytes, insertByte),
1131
                      &cx, &cy, &cw, &ch);
1132
#else
1133
          Tk_CharBbox(textLayout, MIN(numBytes, tablePtr->icursor),
1134
                      &cx, &cy, &cw, &ch);
1135
#endif
1136
          /* we have to fudge with maxW because of odd width
1137
           * determination for newlines at the end of a line */
1138
          maxW = width-bd-tablePtr->padX-tablePtr->insertWidth
1139
            -(cx+MIN(tablePtr->charWidth, cw));
1140
          maxH = height-bd-tablePtr->padY-(cy+ch);
1141
          if (originX < tablePtr->padX+bd-cx) {
1142
            /* cursor off cell to the left */
1143
            /* use western positioning to cet cursor at left edge
1144
             * with slight variation to show some text */
1145
            originX = tablePtr->padX+bd-cx
1146
              +MIN(cx, width-2*bd-tablePtr->padX-tablePtr->insertWidth);
1147
          } else if (originX > maxW) {
1148
            /* cursor off cell to the right */
1149
            /* use eastern positioning to cet cursor at right edge */
1150
            originX = maxW;
1151
          }
1152
          if (originY < tablePtr->padY+bd-cy) {
1153
            /* cursor before top of cell */
1154
            /* use northern positioning to cet cursor at top edge */
1155
            originY = tablePtr->padY+bd-cy;
1156
          } else if (originY > maxH) {
1157
            /* cursor beyond bottom of cell */
1158
            /* use southern positioning to cet cursor at bottom edge */
1159
            originY = maxH;
1160
          }
1161
          tablePtr->activeLayout = textLayout;
1162
          tablePtr->activeX = originX;
1163
          tablePtr->activeY = originY;
1164
        }
1165
        /*
1166
         * use a clip rectangle only if necessary as it means
1167
         * updating the GC in the server which slows everything down.
1168
         * The bd offsets allow us to fudge a little more since the
1169
         * borders are drawn after drawing the string.
1170
         */
1171
        if ((clipRectSet = ((originX < bd) || (originY < bd)
1172
                            || (originX+itemW > width-bd)
1173
                            || (originY+itemH > height-bd)))) {
1174
#ifdef _WIN32
1175
          /* We always draw in the upper-left corner of the clipWind */
1176
          Tk_Fill3DRectangle(tkwin, clipWind, tagPtr->bg, 0, 0,
1177
                             width, height, bd, TK_RELIEF_FLAT);
1178
          Tk_DrawTextLayout(display, clipWind, tagGc, textLayout,
1179
                            originX+bd, originY+bd, 0, -1);
1180
          XCopyArea(display, clipWind, window, tagGc, 0, 0,
1181
                    width, height, x, y);
1182
#else
1183
          /* set the clipping rectangle */
1184
          clipRect.x = x;
1185
          clipRect.y = y;
1186
          clipRect.width = width;
1187
          clipRect.height = height;
1188
          XSetClipRectangles(display, tagGc, 0, 0, &clipRect, 1, Unsorted);
1189
#endif
1190
        }
1191
 
1192
#ifdef _WIN32   /* no cliprect on windows */
1193
        if (!clipRectSet)
1194
#endif
1195
          Tk_DrawTextLayout(display, window, tagGc, textLayout,
1196
                            x+originX+bd, y+originY+bd, 0, -1);
1197
 
1198
#ifndef _WIN32  /* no cliprect on windows */
1199
        /* reset the clip mask */
1200
        if (clipRectSet) {
1201
          XSetClipMask(display, tagGc, None);
1202
        }
1203
#endif
1204
 
1205
        /* if this is the active cell draw the cursor if it's on.
1206
         * this ignores clip rectangles. */
1207
        if (activeCell && (tablePtr->flags & CURSOR_ON) &&
1208
            (originY+bd+cy < height) &&
1209
            (originX+cx+bd-(tablePtr->insertWidth/2) >= 0)) {
1210
          /* make sure it will fit in the box */
1211
          maxW = MAX(0, originY+bd+cy);
1212
          maxH = MIN(ch, height-maxW);
1213
          Tk_Fill3DRectangle(tkwin, window, tablePtr->insertBg,
1214
                             x+originX+cx+bd-(tablePtr->insertWidth/2),
1215
                             y+maxW, tablePtr->insertWidth,
1216
                             maxH, 0, TK_RELIEF_FLAT);
1217
        }
1218
      }
1219
 
1220
    ImageUsed:
1221
      /* Draw the 3d border on the pixmap correctly offset */
1222
      if (tablePtr->borderWidth) {
1223
        switch (tablePtr->drawMode) {
1224
        case DRAW_MODE_SLOW:
1225
        case DRAW_MODE_TK_COMPAT:
1226
          Tk_Draw3DRectangle(tkwin, window, tagPtr->bg,
1227
                             x, y, width, height, bd, tagPtr->relief);
1228
          break;
1229
        case DRAW_MODE_FAST:
1230
          /*
1231
          ** choose the GCs to get the best approximation
1232
          ** to the desired drawing style
1233
          */
1234
          switch(tagPtr->relief) {
1235
          case TK_RELIEF_FLAT:
1236
            topGc = bottomGc = Tk_3DBorderGC(tkwin, tagPtr->bg, TK_3D_FLAT_GC);
1237
            break;
1238
          case TK_RELIEF_RAISED:
1239
          case TK_RELIEF_RIDGE:
1240
            topGc    = Tk_3DBorderGC(tkwin, tagPtr->bg, TK_3D_LIGHT_GC);
1241
            bottomGc = Tk_3DBorderGC(tkwin, tagPtr->bg, TK_3D_DARK_GC);
1242
            break;
1243
          default: /* TK_RELIEF_SUNKEN TK_RELIEF_GROOVE */
1244
            bottomGc = Tk_3DBorderGC(tkwin, tagPtr->bg, TK_3D_LIGHT_GC);
1245
            topGc    = Tk_3DBorderGC(tkwin, tagPtr->bg, TK_3D_DARK_GC);
1246
            break;
1247
          }
1248
 
1249
          /* draw a line with single pixel width */
1250
          rect[0].x = x + width - 1;
1251
          rect[0].y = y;
1252
          rect[1].y = height - 1;
1253
          rect[2].x = -width + 1;
1254
          XDrawLines(display, window, bottomGc, rect, 3, CoordModePrevious);
1255
          rect[0].x = x;
1256
          rect[0].y = y + height - 1;
1257
          rect[1].y = -height + 1;
1258
          rect[2].x = width - 1;
1259
          XDrawLines(display, window, topGc, rect, 3, CoordModePrevious);
1260
          break;
1261
        case DRAW_MODE_SINGLE:
1262
          topGc = Tk_3DBorderGC(tkwin, tagPtr->bg, TK_3D_DARK_GC);
1263
          /* draw a line with single pixel width */
1264
          rect[0].x = x;
1265
          rect[0].y = y + height - 1;
1266
          rect[1].y = -height + 1;
1267
          rect[2].x = width - 1;
1268
          XDrawLines(display, window, topGc, rect, 3, CoordModePrevious);
1269
          break;
1270
        }
1271
      }
1272
 
1273
      /* delete the tag structure */
1274
      ckfree((char *) (tagPtr));
1275
      if (textLayout && !activeCell) {
1276
        Tk_FreeTextLayout(textLayout);
1277
        textLayout = NULL;
1278
      }
1279
    }
1280
  }
1281
#ifdef _WIN32
1282
  Tk_FreePixmap(display, clipWind);
1283
#endif
1284
 
1285
  /* Take care of removing embedded windows that are no longer in view */
1286
  TableUndisplay(tablePtr);
1287
 
1288
  /* copy over and delete the pixmap if we are in slow mode */
1289
  if (tablePtr->drawMode == DRAW_MODE_SLOW) {
1290
    /* Get a default valued GC */
1291
    TableGetGc(display, window, &(tablePtr->defaultTag), &tagGc);
1292
    XCopyArea(display, window, Tk_WindowId(tkwin), tagGc, 0, 0,
1293
              invalidWidth, invalidHeight, invalidX, invalidY);
1294
    Tk_FreePixmap(display, window);
1295
    window = Tk_WindowId(tkwin);
1296
  }
1297
 
1298
  /*
1299
   * if we have got to the end of the table,
1300
   * clear the area after the last row/col
1301
   */
1302
  TableCellCoords(tablePtr, tablePtr->rows-1, tablePtr->cols-1,
1303
                  &x, &y, &width, &height);
1304
 
1305
  /* This should occur before moving pixmap, but this simplifies things
1306
   *
1307
   * Could use Tk_Fill3DRectangle instead of XFillRectangle
1308
   * for best compatibility, and XClearArea could be used on Unix
1309
   * for best speed, so this is the compromise w/o #ifdef's
1310
   */
1311
  if (x+width < invalidX+invalidWidth) {
1312
    XFillRectangle(display, window,
1313
                   Tk_3DBorderGC(tkwin, tablePtr->defaultTag.bg,
1314
                                 TK_3D_FLAT_GC), x+width, invalidY,
1315
                   invalidX+invalidWidth-x-width, invalidHeight);
1316
  }
1317
 
1318
  if (y+height < invalidY+invalidHeight) {
1319
    XFillRectangle(display, window,
1320
                   Tk_3DBorderGC(tkwin, tablePtr->defaultTag.bg,
1321
                                 TK_3D_FLAT_GC), invalidX, y+height,
1322
                   invalidWidth, invalidY+invalidHeight-y-height);
1323
  }
1324
 
1325
  if (tagGc != NULL) {
1326
    TableFreeGc(display, tagGc);
1327
  }
1328
  TableRedrawHighlight(tablePtr);
1329
  /*
1330
   * Free the hash table used to cache evaluations.
1331
   */
1332
  Tcl_DeleteHashTable(colTagsCache);
1333
  ckfree((char *) (colTagsCache));
1334
}
1335
 
1336
/*
1337
 *----------------------------------------------------------------------
1338
 *
1339
 * TableInvalidate --
1340
 *      Invalidates a rectangle and adds it to the total invalid rectangle
1341
 *      waiting to be redrawn.  If the INV_FORCE flag bit is set,
1342
 *      it does an update instantly else waits until Tk is idle.
1343
 *
1344
 * Results:
1345
 *      Will schedule table (re)display.
1346
 *
1347
 * Side effects:
1348
 *      None
1349
 *
1350
 *----------------------------------------------------------------------
1351
 */
1352
void
1353
TableInvalidate(Table * tablePtr, int x, int y,
1354
                int width, int height, int flags)
1355
{
1356
  register int hl = tablePtr->highlightWidth;
1357
  register Tk_Window tkwin = tablePtr->tkwin;
1358
 
1359
  /* make sure that the window hasn't been destroyed already */
1360
  /* avoid allocating 0 sized pixmaps which would be fatal */
1361
  /* and check if rectangle is even on the screen */
1362
  if ((tkwin == NULL) || (width <= 0) || (height <= 0)
1363
      || (x > Tk_Width(tkwin)) || (y > Tk_Height(tkwin))) return;
1364
 
1365
  /* If not even mapped, wait for the remap to redraw all */
1366
  if (!Tk_IsMapped(tkwin)) {
1367
    tablePtr->flags |= REDRAW_ON_MAP;
1368
    return;
1369
  }
1370
 
1371
  /* if no pending updates then replace the rectangle,
1372
   * otherwise find the bounding rectangle */
1373
  if ((flags & INV_HIGHLIGHT) &&
1374
      (x < hl || y < hl || x+width >= Tk_Width(tkwin)-hl ||
1375
      y+height >= Tk_Height(tkwin)-hl)) {
1376
    tablePtr->flags |= REDRAW_BORDER;
1377
  }
1378
 
1379
  if (tablePtr->flags & REDRAW_PENDING) {
1380
    tablePtr->invalidWidth  = MAX(tablePtr->invalidX+tablePtr->invalidWidth,
1381
                                  x + width);
1382
    tablePtr->invalidHeight = MAX(tablePtr->invalidY+tablePtr->invalidHeight,
1383
                                  y + height);
1384
    if (tablePtr->invalidX > x) tablePtr->invalidX = x;
1385
    if (tablePtr->invalidY > y) tablePtr->invalidY = y;
1386
    tablePtr->invalidWidth  -= tablePtr->invalidX;
1387
    tablePtr->invalidHeight -= tablePtr->invalidY;
1388
    /* are we forcing this update out */
1389
    if (flags & INV_FORCE) {
1390
      Tcl_CancelIdleCall(TableDisplay, (ClientData) tablePtr);
1391
      TableDisplay((ClientData) tablePtr);
1392
    }
1393
  } else {
1394
    tablePtr->invalidX = x;
1395
    tablePtr->invalidY = y;
1396
    tablePtr->invalidWidth = width;
1397
    tablePtr->invalidHeight = height;
1398
    if (flags & INV_FORCE) {
1399
      TableDisplay((ClientData) tablePtr);
1400
    } else {
1401
      tablePtr->flags |= REDRAW_PENDING;
1402
      Tcl_DoWhenIdle(TableDisplay, (ClientData) tablePtr);
1403
    }
1404
  }
1405
}
1406
 
1407
/*
1408
 *----------------------------------------------------------------------
1409
 *
1410
 * TableFlashEvent --
1411
 *      Called when the flash timer goes off.
1412
 *
1413
 * Results:
1414
 *      Decrements all the entries in the hash table and invalidates
1415
 *      any cells that expire, deleting them from the table.  If the
1416
 *      table is now empty, stops the timer, else reenables it.
1417
 *
1418
 * Side effects:
1419
 *      None.
1420
 *
1421
 *----------------------------------------------------------------------
1422
 */
1423
static void
1424
TableFlashEvent(ClientData clientdata)
1425
{
1426
  Table *tablePtr = (Table *) clientdata;
1427
  Tcl_HashEntry *entryPtr;
1428
  Tcl_HashSearch search;
1429
  int entries, count, row, col;
1430
 
1431
  entries = 0;
1432
  for (entryPtr = Tcl_FirstHashEntry(tablePtr->flashCells, &search);
1433
       entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
1434
    count = (int) Tcl_GetHashValue(entryPtr);
1435
    if (--count <= 0) {
1436
      /* get the cell address and invalidate that region only */
1437
      TableParseArrayIndex(&row, &col,
1438
                           Tcl_GetHashKey(tablePtr->flashCells, entryPtr));
1439
 
1440
      /* delete the entry from the table */
1441
      Tcl_DeleteHashEntry(entryPtr);
1442
 
1443
      TableRefresh(tablePtr, row-tablePtr->rowOffset,
1444
                   col-tablePtr->colOffset, CELL|INV_FORCE);
1445
    } else {
1446
      Tcl_SetHashValue(entryPtr, (ClientData) count);
1447
      entries++;
1448
    }
1449
  }
1450
 
1451
  /* do I need to restart the timer */
1452
  if (entries && tablePtr->flashMode)
1453
    tablePtr->flashTimer = Tcl_CreateTimerHandler(250, TableFlashEvent,
1454
                                                  (ClientData) tablePtr);
1455
  else
1456
    tablePtr->flashTimer = 0;
1457
}
1458
 
1459
/*
1460
 *----------------------------------------------------------------------
1461
 *
1462
 * TableAddFlash --
1463
 *      Adds a flash on cell row,col (real coords) with the default timeout
1464
 *      if flashing is enabled and flashtime > 0.
1465
 *
1466
 * Results:
1467
 *      Cell will flash.
1468
 *
1469
 * Side effects:
1470
 *      Will start flash timer if it didn't exist.
1471
 *
1472
 *----------------------------------------------------------------------
1473
 */
1474
static void
1475
TableAddFlash(Table *tablePtr, int row, int col)
1476
{
1477
  char buf[INDEX_BUFSIZE];
1478
  int dummy;
1479
  Tcl_HashEntry *entryPtr;
1480
 
1481
  if (!tablePtr->flashMode || tablePtr->flashTime < 1)
1482
    return;
1483
 
1484
  /* create the array index in user coords */
1485
  TableMakeArrayIndex(row+tablePtr->rowOffset, col+tablePtr->colOffset, buf);
1486
 
1487
  /* add the flash to the hash table */
1488
  entryPtr = Tcl_CreateHashEntry(tablePtr->flashCells, buf, &dummy);
1489
  Tcl_SetHashValue(entryPtr, tablePtr->flashTime);
1490
 
1491
  /* now set the timer if it's not already going and invalidate the area */
1492
  if (tablePtr->flashTimer == NULL)
1493
    tablePtr->flashTimer = Tcl_CreateTimerHandler(250, TableFlashEvent,
1494
                                                  (ClientData) tablePtr);
1495
}
1496
 
1497
/*
1498
 *----------------------------------------------------------------------
1499
 *
1500
 * TableSetActiveIndex --
1501
 *      Sets the "active" index of the associated array to the current
1502
 *      value of the active buffer.
1503
 *
1504
 * Results:
1505
 *      None.
1506
 *
1507
 * Side effects:
1508
 *      Traces on the array can cause side effects.
1509
 *
1510
 *----------------------------------------------------------------------
1511
 */
1512
static void
1513
TableSetActiveIndex(register Table *tablePtr)
1514
{
1515
  if (tablePtr->arrayVar) {
1516
    tablePtr->flags |= SET_ACTIVE;
1517
    Tcl_SetVar2(tablePtr->interp, tablePtr->arrayVar, "active",
1518
                tablePtr->activeBuf, TCL_GLOBAL_ONLY);
1519
    tablePtr->flags &= ~SET_ACTIVE;
1520
  }
1521
}
1522
 
1523
/*
1524
 *----------------------------------------------------------------------
1525
 *
1526
 * TableGetActiveBuf --
1527
 *      Get the current selection into the buffer and mark it as unedited.
1528
 *      Set the position to the end of the string.
1529
 *
1530
 * Results:
1531
 *      None.
1532
 *
1533
 * Side effects:
1534
 *      tablePtr->activeBuf will change.
1535
 *
1536
 *----------------------------------------------------------------------
1537
 */
1538
static void
1539
TableGetActiveBuf(register Table *tablePtr)
1540
{
1541
  char *data = "";
1542
 
1543
  if (tablePtr->flags & HAS_ACTIVE)
1544
    data = TableGetCellValue(tablePtr, tablePtr->activeRow+tablePtr->rowOffset,
1545
                             tablePtr->activeCol+tablePtr->colOffset);
1546
 
1547
  if (strcmp(tablePtr->activeBuf, data) == 0) {
1548
    /* this forced SetActiveIndex is necessary if we change array vars and
1549
     * they happen to have these cells equal, we won't properly set the
1550
     * active index for the new array var unless we do this here */
1551
    TableSetActiveIndex(tablePtr);
1552
    return;
1553
  }
1554
  /* is the buffer long enough */
1555
  tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf, strlen(data)+1);
1556
  strcpy(tablePtr->activeBuf, data);
1557
  TableGetIcursor(tablePtr, "end", (int *)0);
1558
  tablePtr->flags &= ~TEXT_CHANGED;
1559
  TableSetActiveIndex(tablePtr);
1560
}
1561
 
1562
/*
1563
 *----------------------------------------------------------------------
1564
 *
1565
 * TableVarProc --
1566
 *      This is the trace procedure associated with the Tcl array.  No
1567
 *      validation will occur here because this only triggers when the
1568
 *      array value is directly set, and we can't maintain the old value.
1569
 *
1570
 * Results:
1571
 *      Invalidates changed cell.
1572
 *
1573
 * Side effects:
1574
 *      Creates/Updates entry in the cache if we are caching.
1575
 *
1576
 *----------------------------------------------------------------------
1577
 */
1578
static char *
1579
TableVarProc(clientData, interp, name, index, flags)
1580
    ClientData clientData;      /* Information about table. */
1581
    Tcl_Interp *interp;         /* Interpreter containing variable. */
1582
    char *name;                 /* Not used. */
1583
    char *index;                /* Not used. */
1584
    int flags;                  /* Information about what happened. */
1585
{
1586
  Table *tablePtr = (Table *) clientData;
1587
  int dummy, row, col, update = 1;
1588
 
1589
  /* This is redundant, as the name should always == arrayVar */
1590
  name = tablePtr->arrayVar;
1591
 
1592
  /* is this the whole var being destroyed or just one cell being deleted */
1593
  if ((flags & TCL_TRACE_UNSETS) && index == NULL) {
1594
    /* if this isn't the interpreter being destroyed reinstate the trace */
1595
    if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1596
      Tcl_SetVar2(interp, name, TEST_KEY, "", TCL_GLOBAL_ONLY);
1597
      Tcl_UnsetVar2(interp, name, TEST_KEY, TCL_GLOBAL_ONLY);
1598
      Tcl_ResetResult(interp);
1599
 
1600
      /* set a trace on the variable */
1601
      Tcl_TraceVar(interp, name,
1602
                   TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY,
1603
                   (Tcl_VarTraceProc *)TableVarProc, (ClientData) tablePtr);
1604
 
1605
      /* only do the following if arrayVar is our data source */
1606
      if (tablePtr->dataSource & DATA_ARRAY) {
1607
        /* clear the selection buffer */
1608
        TableGetActiveBuf(tablePtr);
1609
        /* flush any cache */
1610
        TableFlushCache(tablePtr);
1611
        /* and invalidate the table */
1612
        TableInvalidateAll(tablePtr, 0);
1613
      }
1614
    }
1615
    return (char *) NULL;
1616
  }
1617
  /* only continue if arrayVar is our data source */
1618
  if (!(tablePtr->dataSource & DATA_ARRAY)) {
1619
    return (char *) NULL;
1620
  }
1621
  /* get the cell address and invalidate that region only.
1622
   * Make sure that it is a valid cell address. */
1623
  if (strcmp("active", index) == 0) {
1624
    if (tablePtr->flags & SET_ACTIVE) {
1625
      /* If we are already setting the active cell, the update
1626
       * will occur in other code */
1627
      update = 0;
1628
    } else {
1629
      /* modified TableGetActiveBuf */
1630
      char *data = "";
1631
 
1632
      row = tablePtr->activeRow;
1633
      col = tablePtr->activeCol;
1634
      if (tablePtr->flags & HAS_ACTIVE)
1635
        data = Tcl_GetVar2(interp, name, index, TCL_GLOBAL_ONLY);
1636
      if (!data) data = "";
1637
 
1638
      if (strcmp(tablePtr->activeBuf, data) == 0) {
1639
        return (char *) NULL;
1640
      }
1641
      tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf,
1642
                                              strlen(data)+1);
1643
      strcpy(tablePtr->activeBuf, data);
1644
      /* set cursor to the last char */
1645
      TableGetIcursor(tablePtr, "end", (int *)0);
1646
      tablePtr->flags |= TEXT_CHANGED;
1647
    }
1648
  } else if (TableParseArrayIndex(&row, &col, index) == 2) {
1649
    char buf[INDEX_BUFSIZE];
1650
    /* Make sure it won't trigger on array(2,3extrastuff) */
1651
    TableMakeArrayIndex(row, col, buf);
1652
    if (strcmp(buf, index)) {
1653
      return (char *) NULL;
1654
    }
1655
    if (tablePtr->caching) {
1656
      Tcl_HashEntry *entryPtr;
1657
      char *val, *data = NULL;
1658
 
1659
      data = Tcl_GetVar2(interp, name, index, TCL_GLOBAL_ONLY);
1660
      if (!data) data = "";
1661
      val = (char *)ckalloc(strlen(data)+1);
1662
      strcpy(val, data);
1663
      entryPtr = Tcl_CreateHashEntry(tablePtr->cache, buf, &dummy);
1664
      Tcl_SetHashValue(entryPtr, val);
1665
    }
1666
    /* convert index to real coords */
1667
    row -= tablePtr->rowOffset;
1668
    col -= tablePtr->colOffset;
1669
    /* did the active cell just update */
1670
    if (row == tablePtr->activeRow && col == tablePtr->activeCol)
1671
      TableGetActiveBuf(tablePtr);
1672
    /* Flash the cell */
1673
    TableAddFlash(tablePtr, row, col);
1674
  } else {
1675
    return (char *) NULL;
1676
  }
1677
 
1678
  if (update) TableRefresh(tablePtr, row, col, CELL);
1679
 
1680
  return (char *) NULL;
1681
}
1682
 
1683
/*
1684
 *----------------------------------------------------------------------
1685
 *
1686
 * TableGeometryRequest --
1687
 *      This procedure is invoked to request a new geometry from Tk.
1688
 *
1689
 * Results:
1690
 *      None.
1691
 *
1692
 * Side effects:
1693
 *      Geometry information is updated and a new requested size is
1694
 *      registered for the widget.  Internal border info is also set.
1695
 *
1696
 *----------------------------------------------------------------------
1697
 */
1698
static void
1699
TableGeometryRequest(tablePtr)
1700
     register Table *tablePtr;
1701
{
1702
  int x, y;
1703
 
1704
  /* Do the geometry request
1705
   * If -width #cols was not specified or it is greater than the real
1706
   * number of cols, use maxWidth as a lower bound, with the other lower
1707
   * bound being the upper bound of the window's user-set width and the
1708
   * value of -maxwidth set by the programmer
1709
   * Vice versa for rows/height
1710
   */
1711
  x = MIN((tablePtr->maxReqCols==0 || tablePtr->maxReqCols > tablePtr->cols) ?
1712
           tablePtr->maxWidth : tablePtr->colStarts[tablePtr->maxReqCols],
1713
          tablePtr->maxReqWidth) + 2*tablePtr->highlightWidth;
1714
  y = MIN((tablePtr->maxReqRows==0 || tablePtr->maxReqRows > tablePtr->rows) ?
1715
           tablePtr->maxHeight : tablePtr->rowStarts[tablePtr->maxReqRows],
1716
          tablePtr->maxReqHeight) + 2*tablePtr->highlightWidth;
1717
  Tk_GeometryRequest(tablePtr->tkwin, x, y);
1718
}
1719
 
1720
/*
1721
 *----------------------------------------------------------------------
1722
 *
1723
 * TableAdjustActive --
1724
 *      This procedure is called by AdjustParams and CMD_ACTIVATE to
1725
 *      move the active cell.
1726
 *
1727
 * Results:
1728
 *      Old and new active cell indices will be invalidated.
1729
 *
1730
 * Side effects:
1731
 *      If the old active cell index was edited, it will be saved.
1732
 *      The active buffer will be updated.
1733
 *
1734
 *----------------------------------------------------------------------
1735
 */
1736
static void
1737
TableAdjustActive(tablePtr)
1738
     register Table *tablePtr;          /* Widget record for table */
1739
{
1740
  if (tablePtr->flags & HAS_ACTIVE) {
1741
    /* make sure the active cell has a reasonable real index */
1742
    tablePtr->activeRow = MAX(0, MIN(tablePtr->activeRow, tablePtr->rows-1));
1743
    tablePtr->activeCol = MAX(0, MIN(tablePtr->activeCol, tablePtr->cols-1));
1744
  }
1745
 
1746
  /*
1747
   * now check the new value of active cell against the original,
1748
   * If it changed, invalidate the area, else leave it alone
1749
   */
1750
  if (tablePtr->oldActRow != tablePtr->activeRow ||
1751
      tablePtr->oldActCol != tablePtr->activeCol) {
1752
    int x, y, width, height;
1753
    /* put the value back in the cell */
1754
    if (tablePtr->oldActRow >= 0 && tablePtr->oldActCol >= 0) {
1755
      /*
1756
       * Set the value of the old active cell to the active buffer
1757
       * SetCellValue will check if the value actually changed
1758
       */
1759
      if (tablePtr->flags & TEXT_CHANGED) {
1760
        /* WARNING an outside trace will be triggered here and if it
1761
         * calls something that causes TableAdjustParams to be called
1762
         * again, we are in data consistency trouble */
1763
        /* HACK - turn TEXT_CHANGED off now to possibly avoid the
1764
         * above data inconsistency problem.  */
1765
        tablePtr->flags &= ~TEXT_CHANGED;
1766
        TableSetCellValue(tablePtr, tablePtr->oldActRow+tablePtr->rowOffset,
1767
                          tablePtr->oldActCol+tablePtr->colOffset,
1768
                          tablePtr->activeBuf);
1769
      }
1770
      /* invalidate the old active cell */
1771
      TableCellCoords(tablePtr, tablePtr->oldActRow, tablePtr->oldActCol,
1772
                       &x, &y, &width, &height);
1773
      TableInvalidate(tablePtr, x, y, width, height, 0);
1774
    }
1775
 
1776
    /* get the new value of the active cell into buffer */
1777
    TableGetActiveBuf(tablePtr);
1778
 
1779
    /* invalidate the new active cell */
1780
    TableCellCoords(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
1781
                    &x, &y, &width, &height);
1782
    TableInvalidate(tablePtr, x, y, width, height, 0);
1783
    /* set the old active row/col for the next time this function is called */
1784
    tablePtr->oldActRow = tablePtr->activeRow;
1785
    tablePtr->oldActCol = tablePtr->activeCol;
1786
  }
1787
}
1788
 
1789
/*
1790
 *----------------------------------------------------------------------
1791
 *
1792
 * TableAdjustParams --
1793
 *      Calculate the row and column starts.  Adjusts the topleft corner
1794
 *      variable to keep it within the screen range, out of the titles
1795
 *      and keep the screen full make sure the selected cell is in the
1796
 *      visible area checks to see if the top left cell has changed at
1797
 *      all and invalidates the table if it has.
1798
 *
1799
 * Results:
1800
 *      None.
1801
 *
1802
 * Side Effects:
1803
 *      Number of rows can change if -rowstretchmode == fill.
1804
 *      topRow && leftCol can change to fit display.
1805
 *      activeRow/Col can change to ensure it is a valid cell.
1806
 *
1807
 *----------------------------------------------------------------------
1808
 */
1809
static void
1810
TableAdjustParams(register Table *tablePtr)
1811
{
1812
  int topRow, leftCol, row, col, total, i, value, x, y, width, height;
1813
  int w, h, bd, hl, recalc = 0;
1814
  int diff, unpreset, lastUnpreset, pad, lastPad, numPixels;
1815
  int defColWidth, defRowHeight;
1816
  Tcl_HashEntry *entryPtr;
1817
 
1818
  /* cache the borderwidth (doubled) for many upcoming calculations */
1819
  bd = 2*tablePtr->borderWidth;
1820
  hl = tablePtr->highlightWidth;
1821
  w = Tk_Width(tablePtr->tkwin)-2*hl;
1822
  h = Tk_Height(tablePtr->tkwin)-2*hl;
1823
 
1824
  /* account for whether defColWidth is in chars (>=0) or pixels (<0) */
1825
  /* bd is added in here for convenience */
1826
  if (tablePtr->defColWidth > 0)
1827
    defColWidth = tablePtr->charWidth * tablePtr->defColWidth + bd;
1828
  else
1829
    defColWidth = -(tablePtr->defColWidth) + bd;
1830
  if (tablePtr->defRowHeight > 0)
1831
    defRowHeight = tablePtr->charHeight * tablePtr->defRowHeight + bd;
1832
  else
1833
    defRowHeight = -(tablePtr->defRowHeight) + bd;
1834
 
1835
  /* Set up the arrays to hold the col pixels and starts */
1836
  if (tablePtr->colPixels) ckfree((char *) tablePtr->colPixels);
1837
  tablePtr->colPixels = (int *) ckalloc(tablePtr->cols * sizeof(int));
1838
  if (tablePtr->colStarts) ckfree((char *) tablePtr->colStarts);
1839
  tablePtr->colStarts = (int *) ckalloc((tablePtr->cols+1) * sizeof(int));
1840
 
1841
  /* get all the preset columns and set their widths */
1842
  lastUnpreset = 0;
1843
  numPixels = 0;
1844
  unpreset = 0;
1845
  for (i = 0; i < tablePtr->cols; i++) {
1846
    if ((entryPtr = Tcl_FindHashEntry(tablePtr->colWidths,
1847
                                      (char *) i)) == NULL) {
1848
      tablePtr->colPixels[i] = -1;
1849
      unpreset++;
1850
      lastUnpreset = i;
1851
    } else {
1852
      value = (int) Tcl_GetHashValue(entryPtr);
1853
      if (value <= 0) {
1854
        tablePtr->colPixels[i] = -value + bd;
1855
      } else {
1856
        tablePtr->colPixels[i] = value * tablePtr->charWidth + bd;
1857
      }
1858
      numPixels += tablePtr->colPixels[i];
1859
    }
1860
  }
1861
 
1862
  /* work out how much to pad each col depending on the mode */
1863
  diff = w-numPixels-(unpreset*defColWidth);
1864
  total = 0;
1865
  /* now do the padding and calculate the column starts */
1866
  /* diff lower than 0 means we can't see the entire set of columns,
1867
   * thus no special stretching will occur & we optimize the calculation */
1868
  if (diff <= 0) {
1869
    for (i = 0; i < tablePtr->cols; i++) {
1870
      if (tablePtr->colPixels[i] == -1)
1871
        tablePtr->colPixels[i] = defColWidth;
1872
      tablePtr->colStarts[i] = total;
1873
      total += tablePtr->colPixels[i];
1874
    }
1875
  } else {
1876
    switch(tablePtr->colStretch) {
1877
    case STRETCH_MODE_NONE:
1878
      pad = 0;
1879
      lastPad = 0;
1880
      break;
1881
    case STRETCH_MODE_UNSET:
1882
      if (unpreset == 0) {
1883
        pad = 0;
1884
        lastPad = 0;
1885
      } else {
1886
        pad = diff / unpreset;
1887
        lastPad = diff - pad * (unpreset - 1);
1888
      }
1889
      break;
1890
    case STRETCH_MODE_LAST:
1891
      pad = 0;
1892
      lastPad = diff;
1893
      lastUnpreset = tablePtr->cols - 1;
1894
      break;
1895
    default:    /* STRETCH_MODE_ALL, but also FILL for cols */
1896
      pad = diff / tablePtr->cols;
1897
      /* force it to be applied to the last column too */
1898
      lastUnpreset = tablePtr->cols - 1;
1899
      lastPad = diff - pad * lastUnpreset;
1900
    }
1901
 
1902
    for (i = 0; i < tablePtr->cols; i++) {
1903
      if (tablePtr->colPixels[i] == -1) {
1904
        tablePtr->colPixels[i] = defColWidth
1905
          + ((i==lastUnpreset)?lastPad:pad);
1906
      } else if (tablePtr->colStretch == STRETCH_MODE_ALL) {
1907
        tablePtr->colPixels[i] += (i==lastUnpreset)?lastPad:pad;
1908
      }
1909
      tablePtr->colStarts[i] = total;
1910
      total += tablePtr->colPixels[i];
1911
    }
1912
  }
1913
  tablePtr->colStarts[i] = tablePtr->maxWidth = total;
1914
 
1915
  /*
1916
   * The 'do' loop is only necessary for rows because of FILL mode
1917
   */
1918
  do {
1919
    /* Set up the arrays to hold the row pixels and starts */
1920
    /* FIX - this can be moved outside 'do' if you check >row size */
1921
    if (tablePtr->rowPixels) ckfree((char *) tablePtr->rowPixels);
1922
    tablePtr->rowPixels = (int *) ckalloc(tablePtr->rows * sizeof(int));
1923
 
1924
    /* get all the preset rows and set their heights */
1925
    lastUnpreset = 0;
1926
    numPixels = 0;
1927
    unpreset = 0;
1928
    for (i = 0; i < tablePtr->rows; i++) {
1929
      if ((entryPtr = Tcl_FindHashEntry(tablePtr->rowHeights,
1930
                                        (char *) i)) == NULL) {
1931
        tablePtr->rowPixels[i] = -1;
1932
        unpreset++;
1933
        lastUnpreset = i;
1934
      } else {
1935
        value = (int) Tcl_GetHashValue(entryPtr);
1936
        if (value <= 0) {
1937
          tablePtr->rowPixels[i] = -value + bd;
1938
        } else {
1939
          tablePtr->rowPixels[i] = value * tablePtr->charHeight + bd;
1940
        }
1941
        numPixels += tablePtr->rowPixels[i];
1942
      }
1943
    }
1944
 
1945
    /* work out how much to pad each row depending on the mode */
1946
    diff = h-numPixels-(unpreset*defRowHeight);
1947
    switch(tablePtr->rowStretch) {
1948
    case STRETCH_MODE_NONE:
1949
      pad = 0;
1950
      lastPad = 0;
1951
      break;
1952
    case STRETCH_MODE_UNSET:
1953
      if (unpreset == 0)  {
1954
        pad = 0;
1955
        lastPad = 0;
1956
      } else {
1957
        pad = MAX(0,diff) / unpreset;
1958
        lastPad = MAX(0,diff) - pad * (unpreset - 1);
1959
      }
1960
      break;
1961
    case STRETCH_MODE_LAST:
1962
      pad = 0;
1963
      lastPad = MAX(0,diff);
1964
      /* force it to be applied to the last column too */
1965
      lastUnpreset = tablePtr->rows - 1;
1966
      break;
1967
    case STRETCH_MODE_FILL:
1968
      pad = 0;
1969
      lastPad = diff;
1970
      if (diff && !recalc) {
1971
        tablePtr->rows += (diff/defRowHeight);
1972
        if (diff < 0 && tablePtr->rows < 0)
1973
          tablePtr->rows = 0;
1974
        lastUnpreset = tablePtr->rows - 1;
1975
        recalc = 1;
1976
        continue;
1977
      } else {
1978
        lastUnpreset = tablePtr->rows - 1;
1979
        recalc = 0;
1980
      }
1981
      break;
1982
    default:    /* STRETCH_MODE_ALL */
1983
      pad = MAX(0,diff) / tablePtr->rows;
1984
      /* force it to be applied to the last column too */
1985
      lastUnpreset = tablePtr->rows - 1;
1986
      lastPad = MAX(0,diff) - pad * lastUnpreset;
1987
    }
1988
  } while (recalc);
1989
 
1990
  if (tablePtr->rowStarts) ckfree((char *) tablePtr->rowStarts);
1991
  tablePtr->rowStarts = (int *) ckalloc((tablePtr->rows+1)*sizeof(int));
1992
  /* now do the padding and calculate the row starts */
1993
  total = 0;
1994
  for (i = 0; i < tablePtr->rows; i++) {
1995
    if (tablePtr->rowPixels[i] == -1) {
1996
      tablePtr->rowPixels[i] = defRowHeight
1997
        + ((i==lastUnpreset)?lastPad:pad);
1998
    } else if (tablePtr->rowStretch == STRETCH_MODE_ALL) {
1999
      tablePtr->rowPixels[i] += (i==lastUnpreset)?lastPad:pad;
2000
    }
2001
    /* calculate the start of each row */
2002
    tablePtr->rowStarts[i] = total;
2003
    total += tablePtr->rowPixels[i];
2004
  }
2005
  tablePtr->rowStarts[i] = tablePtr->maxHeight = total;
2006
 
2007
  /* make sure the top row and col have reasonable real indices */
2008
  tablePtr->topRow = topRow =
2009
    MAX(tablePtr->titleRows, MIN(tablePtr->topRow, tablePtr->rows-1));
2010
  tablePtr->leftCol = leftCol =
2011
    MAX(tablePtr->titleCols, MIN(tablePtr->leftCol, tablePtr->cols-1));
2012
 
2013
  /* If we dont have the info, dont bother to fix up the other parameters */
2014
  if (Tk_WindowId(tablePtr->tkwin) == None) {
2015
    tablePtr->oldTopRow = tablePtr->oldLeftCol = -1;
2016
    return;
2017
  }
2018
 
2019
  w += hl;
2020
  h += hl;
2021
  /*
2022
   * If we use this value of topRow, will we fill the window?
2023
   * if not, decrease it until we will, or until it gets to titleRows
2024
   * make sure we don't cut off the bottom row
2025
   */
2026
  for (; topRow > tablePtr->titleRows; topRow--)
2027
    if ((tablePtr->maxHeight-(tablePtr->rowStarts[topRow-1] -
2028
                              tablePtr->rowStarts[tablePtr->titleRows])) > h)
2029
      break;
2030
  /*
2031
   * If we use this value of topCol, will we fill the window?
2032
   * if not, decrease it until we will, or until it gets to titleCols
2033
   * make sure we don't cut off the left column
2034
   */
2035
  for (; leftCol > tablePtr->titleCols; leftCol--)
2036
    if ((tablePtr->maxWidth-(tablePtr->colStarts[leftCol-1] -
2037
                             tablePtr->colStarts[tablePtr->titleCols])) > w)
2038
      break;
2039
 
2040
  tablePtr->topRow = topRow;
2041
  tablePtr->leftCol = leftCol;
2042
 
2043
  /* Now work out where the bottom right for scrollbar update
2044
   * and testing for one last stretch */
2045
  TableGetLastCell(tablePtr, &row, &col);
2046
  TableCellVCoords(tablePtr, row, col, &x, &y, &width, &height, 0);
2047
 
2048
  /*
2049
   * Do we have scrollbars, if so, calculate and call the TCL functions In
2050
   * order to get the scrollbar to be completely full when the whole screen
2051
   * is shown and there are titles, we have to arrange for the scrollbar
2052
   * range to be 0 -> rows-titleRows etc.  This leads to the position
2053
   * setting methods, toprow and leftcol, being relative to the titles, not
2054
   * absolute row and column numbers.
2055
   */
2056
  if (tablePtr->yScrollCmd != NULL || tablePtr->xScrollCmd != NULL) {
2057
    Tcl_Interp *interp = tablePtr->interp;
2058
    char buf[INDEX_BUFSIZE];
2059
    double first, last;
2060
 
2061
    /*
2062
     * We must hold onto the interpreter because the data referred to at
2063
     * tablePtr might be freed as a result of the call to Tcl_VarEval.
2064
     */
2065
    Tcl_Preserve((ClientData) interp);
2066
 
2067
    /* Do we have a Y-scrollbar and rows to scroll? */
2068
    if (tablePtr->yScrollCmd != NULL) {
2069
      if (row < tablePtr->titleRows) {
2070
        first = 0;
2071
        last  = 1;
2072
      } else {
2073
        diff = tablePtr->rowStarts[tablePtr->titleRows];
2074
        last = (double) (tablePtr->rowStarts[tablePtr->rows]-diff);
2075
        first = (tablePtr->rowStarts[topRow]-diff) / last;
2076
        last  = (height+tablePtr->rowStarts[row]-diff) / last;
2077
      }
2078
      sprintf(buf, " %g %g", first, last);
2079
      if (Tcl_VarEval(interp, tablePtr->yScrollCmd,
2080
                      buf, (char *) NULL) != TCL_OK) {
2081
        Tcl_AddErrorInfo(interp,
2082
                "\n    (vertical scrolling command executed by table)");
2083
        Tcl_BackgroundError(interp);
2084
      }
2085
    }
2086
    /* Do we have a X-scrollbar and cols to scroll? */
2087
    if (tablePtr->xScrollCmd != NULL) {
2088
      if (col < tablePtr->titleCols) {
2089
        first = 0;
2090
        last  = 1;
2091
      } else {
2092
        diff = tablePtr->colStarts[tablePtr->titleCols];
2093
        last = (double) (tablePtr->colStarts[tablePtr->cols]-diff);
2094
        first = (tablePtr->colStarts[leftCol]-diff) / last;
2095
        last  = (width+tablePtr->colStarts[col]-diff) / last;
2096
      }
2097
      sprintf(buf, " %g %g", first, last);
2098
      if (Tcl_VarEval(interp, tablePtr->xScrollCmd,
2099
                      buf, (char *) NULL) != TCL_OK) {
2100
        Tcl_AddErrorInfo(interp,
2101
                "\n    (horizontal scrolling command executed by table)");
2102
        Tcl_BackgroundError(interp);
2103
      }
2104
    }
2105
 
2106
    Tcl_Release((ClientData) interp);
2107
  }
2108
 
2109
  /* Adjust the last row/col to fill empty space if it is visible */
2110
  /* do this after setting the scrollbars to not upset its calculations */
2111
  if (row == tablePtr->rows-1 && tablePtr->rowStretch != STRETCH_MODE_NONE) {
2112
    diff = h-(y+height);
2113
    if (diff > 0) {
2114
      tablePtr->rowPixels[tablePtr->rows-1] += diff;
2115
      tablePtr->rowStarts[tablePtr->rows] += diff;
2116
    }
2117
  }
2118
  if (col == tablePtr->cols-1 && tablePtr->colStretch != STRETCH_MODE_NONE) {
2119
    diff = w-(x+width);
2120
    if (diff > 0) {
2121
      tablePtr->colPixels[tablePtr->cols-1] += diff;
2122
      tablePtr->colStarts[tablePtr->cols] += diff;
2123
    }
2124
  }
2125
 
2126
  TableAdjustActive(tablePtr);
2127
 
2128
  /*
2129
   * now check the new value of topleft cell against the originals,
2130
   * If they changed, invalidate the area, else leave it alone
2131
   */
2132
  if (tablePtr->topRow != tablePtr->oldTopRow ||
2133
      tablePtr->leftCol != tablePtr->oldLeftCol) {
2134
    /* set the old top row/col for the next time this function is called */
2135
    tablePtr->oldTopRow = tablePtr->topRow;
2136
    tablePtr->oldLeftCol = tablePtr->leftCol;
2137
    /* only the upper corner title cells wouldn't change */
2138
    TableInvalidateAll(tablePtr, 0);
2139
  }
2140
}
2141
 
2142
/*
2143
 *----------------------------------------------------------------------
2144
 *
2145
 * TableCursorEvent --
2146
 *      Toggle the cursor status.  Equivalent to EntryBlinkProc.
2147
 *
2148
 * Results:
2149
 *      None.
2150
 *
2151
 * Side effects:
2152
 *      The cursor will be switched off/on.
2153
 *
2154
 *----------------------------------------------------------------------
2155
 */
2156
static void
2157
TableCursorEvent(ClientData clientData)
2158
{
2159
  register Table *tablePtr = (Table *) clientData;
2160
 
2161
  if (!(tablePtr->flags & HAS_FOCUS) || (tablePtr->insertOffTime == 0)) {
2162
    return;
2163
  }
2164
 
2165
  if (tablePtr->cursorTimer != NULL)
2166
    Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
2167
 
2168
  tablePtr->cursorTimer =
2169
    Tcl_CreateTimerHandler((tablePtr->flags & CURSOR_ON) ?
2170
                           tablePtr->insertOffTime : tablePtr->insertOnTime,
2171
                           TableCursorEvent, (ClientData) tablePtr);
2172
  /* Toggle the cursor */
2173
  tablePtr->flags ^= CURSOR_ON;
2174
 
2175
  /* invalidate the cell */
2176
  TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
2177
               CELL|INV_FORCE);
2178
}
2179
 
2180
/*
2181
 *----------------------------------------------------------------------
2182
 *
2183
 * TableConfigCursor --
2184
 *      Configures the timer depending on the state of the table.
2185
 *      Equivalent to EntryFocusProc.
2186
 *
2187
 * Results:
2188
 *      None.
2189
 *
2190
 * Side effects:
2191
 *      The cursor will be switched off/on.
2192
 *
2193
 *----------------------------------------------------------------------
2194
 */
2195
static void
2196
TableConfigCursor(register Table *tablePtr)
2197
{
2198
  /* to get a cursor, we have to have focus and allow edits */
2199
  if ((tablePtr->flags & HAS_FOCUS) && !(tablePtr->flags & ACTIVE_DISABLED) &&
2200
      (tablePtr->state == STATE_NORMAL)) {
2201
    /* turn the cursor on */
2202
    if (!(tablePtr->flags & CURSOR_ON)) {
2203
      tablePtr->flags |= CURSOR_ON;
2204
    }
2205
 
2206
    /* set up the first timer */
2207
    if (tablePtr->insertOffTime != 0) {
2208
      /* make sure nothing existed */
2209
      Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
2210
      tablePtr->cursorTimer = Tcl_CreateTimerHandler(tablePtr->insertOnTime,
2211
                                                     TableCursorEvent,
2212
                                                     (ClientData) tablePtr);
2213
    }
2214
 
2215
  } else {
2216
    /* turn the cursor off */
2217
    if ((tablePtr->flags & CURSOR_ON)) {
2218
      tablePtr->flags &= ~CURSOR_ON;
2219
    }
2220
 
2221
    /* and disable the timer */
2222
    if (tablePtr->cursorTimer != NULL)
2223
      Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
2224
    tablePtr->cursorTimer = NULL;
2225
  }
2226
 
2227
  /* Invalidate the selection window to show or hide the cursor */
2228
  TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
2229
               CELL|INV_FORCE);
2230
}
2231
 
2232
/*
2233
 *----------------------------------------------------------------------
2234
 *
2235
 * TableFetchSelection --
2236
 *      This procedure is called back by Tk when the selection is
2237
 *      requested by someone.  It returns part or all of the selection
2238
 *      in a buffer provided by the caller.
2239
 *
2240
 * Results:
2241
 *      The return value is the number of non-NULL bytes stored
2242
 *      at buffer.  Buffer is filled (or partially filled) with a
2243
 *      NULL-terminated string containing part or all of the selection,
2244
 *      as given by offset and maxBytes.
2245
 *
2246
 * Side effects:
2247
 *      None.
2248
 *
2249
 *----------------------------------------------------------------------
2250
 */
2251
static int
2252
TableFetchSelection(clientData, offset, buffer, maxBytes)
2253
    ClientData clientData;              /* Information about table widget. */
2254
    int offset;                         /* Offset within selection of first
2255
                                         * character to be returned. */
2256
    char *buffer;                       /* Location in which to place
2257
                                         * selection. */
2258
    int maxBytes;                       /* Maximum number of bytes to place
2259
                                         * at buffer, not including terminating
2260
                                         * NULL character. */
2261
{
2262
  register Table *tablePtr = (Table *) clientData;
2263
  Tcl_Interp *interp = tablePtr->interp;
2264
  char *value, *data, *rowsep = tablePtr->rowSep, *colsep = tablePtr->colSep;
2265
  Tcl_DString selection;
2266
  Tcl_HashEntry *entryPtr;
2267
  Tcl_HashSearch search;
2268
  int length, count, lastrow=0, needcs=0, r, c, listArgc, rslen=0, cslen=0;
2269
  int numcols, numrows;
2270
  char **listArgv;
2271
 
2272
  /* if we are not exporting the selection || we have no data source, return */
2273
  if (!tablePtr->exportSelection ||
2274
      (tablePtr->dataSource == DATA_NONE)) {
2275
    return -1;
2276
  }
2277
 
2278
  /* First get a sorted list of the selected elements */
2279
  Tcl_DStringInit(&selection);
2280
  for (entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
2281
       entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
2282
    Tcl_DStringAppendElement(&selection,
2283
                             Tcl_GetHashKey(tablePtr->selCells, entryPtr));
2284
  }
2285
  value = TableCellSort(tablePtr, Tcl_DStringValue(&selection));
2286
  Tcl_DStringFree(&selection);
2287
 
2288
  if (value == NULL ||
2289
      Tcl_SplitList(interp, value, &listArgc, &listArgv) != TCL_OK) {
2290
    return -1;
2291
  }
2292
  ckfree(value);
2293
 
2294
  Tcl_DStringInit(&selection);
2295
  rslen = (rowsep?(strlen(rowsep)):0);
2296
  cslen = (colsep?(strlen(colsep)):0);
2297
  numrows = numcols = 0;
2298
  for (count = 0; count < listArgc; count++) {
2299
    TableParseArrayIndex(&r, &c, listArgv[count]);
2300
    if (count) {
2301
      if (lastrow != r) {
2302
        lastrow = r;
2303
        needcs = 0;
2304
        if (rslen) {
2305
          Tcl_DStringAppend(&selection, rowsep, rslen);
2306
        } else {
2307
          Tcl_DStringEndSublist(&selection);
2308
          Tcl_DStringStartSublist(&selection);
2309
        }
2310
        ++numrows;
2311
      } else {
2312
        if (++needcs > numcols)
2313
          numcols = needcs;
2314
      }
2315
    } else {
2316
      lastrow = r;
2317
      needcs = 0;
2318
      if (!rslen)
2319
        Tcl_DStringStartSublist(&selection);
2320
    }
2321
    data = TableGetCellValue(tablePtr, r, c);
2322
    if (cslen) {
2323
      if (needcs)
2324
        Tcl_DStringAppend(&selection, colsep, cslen);
2325
      Tcl_DStringAppend(&selection, data, -1);
2326
    } else {
2327
      Tcl_DStringAppendElement(&selection, data);
2328
    }
2329
  }
2330
  if (!rslen && count)
2331
    Tcl_DStringEndSublist(&selection);
2332
  ckfree((char *) listArgv);
2333
 
2334
  if (tablePtr->selCmd != NULL) {
2335
    Tcl_DString script;
2336
    Tcl_DStringInit(&script);
2337
    ExpandPercents(tablePtr, tablePtr->selCmd, numrows+1, numcols+1,
2338
                   Tcl_DStringValue(&selection), (char *) NULL,
2339
                   listArgc, &script, CMD_ACTIVATE);
2340
    if (Tcl_GlobalEval(interp, Tcl_DStringValue(&script)) == TCL_ERROR) {
2341
      Tcl_AddErrorInfo(interp,
2342
                       "\n    (error in table selection command)");
2343
      Tcl_BackgroundError(interp);
2344
      Tcl_DStringFree(&script);
2345
      Tcl_DStringFree(&selection);
2346
      return -1;
2347
    } else {
2348
      Tcl_DStringGetResult(interp, &selection);
2349
    }
2350
    Tcl_DStringFree(&script);
2351
  }
2352
 
2353
  length = Tcl_DStringLength(&selection);
2354
 
2355
  if (length == 0)
2356
    return -1;
2357
 
2358
  /* Copy the requested portion of the selection to the buffer. */
2359
  count = length - offset;
2360
  if (count <= 0) {
2361
    count = 0;
2362
  } else {
2363
    if (count > maxBytes) {
2364
      count = maxBytes;
2365
    }
2366
    memcpy((VOID *) buffer, (VOID *) (Tcl_DStringValue(&selection) + offset),
2367
           (size_t) count);
2368
  }
2369
  buffer[count] = '\0';
2370
  Tcl_DStringFree(&selection);
2371
  return count;
2372
}
2373
 
2374
/*
2375
 *----------------------------------------------------------------------
2376
 *
2377
 * TableLostSelection --
2378
 *      This procedure is called back by Tk when the selection is
2379
 *      grabbed away from a table widget.
2380
 *
2381
 * Results:
2382
 *      None.
2383
 *
2384
 * Side effects:
2385
 *      The existing selection is unhighlighted, and the window is
2386
 *      marked as not containing a selection.
2387
 *
2388
 *----------------------------------------------------------------------
2389
 */
2390
static void
2391
TableLostSelection(clientData)
2392
    ClientData clientData;      /* Information about table widget. */
2393
{
2394
  register Table *tablePtr = (Table *) clientData;
2395
 
2396
  if (tablePtr->exportSelection) {
2397
    Tcl_HashEntry *entryPtr;
2398
    Tcl_HashSearch search;
2399
    int row, col;
2400
 
2401
    /* Same as SEL CLEAR ALL */
2402
    for (entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
2403
         entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
2404
      TableParseArrayIndex(&row, &col,
2405
                           Tcl_GetHashKey(tablePtr->selCells,entryPtr));
2406
      Tcl_DeleteHashEntry(entryPtr);
2407
      TableRefresh(tablePtr, row-tablePtr->rowOffset,
2408
                   col-tablePtr->colOffset, CELL);
2409
    }
2410
  }
2411
}
2412
 
2413
/*
2414
 *----------------------------------------------------------------------
2415
 *
2416
 * TableRestrictProc --
2417
 *      A Tk_RestrictProc used by TableValidateChange to eliminate any
2418
 *      extra key input events in the event queue that
2419
 *      have a serial number no less than a given value.
2420
 *
2421
 * Results:
2422
 *      Returns either TK_DISCARD_EVENT or TK_DEFER_EVENT.
2423
 *
2424
 * Side effects:
2425
 *      None.
2426
 *
2427
 *----------------------------------------------------------------------
2428
 */
2429
static Tk_RestrictAction
2430
TableRestrictProc(serial, eventPtr)
2431
    ClientData serial;
2432
    XEvent *eventPtr;
2433
{
2434
  if ((eventPtr->type == KeyRelease || eventPtr->type == KeyPress) &&
2435
      ((eventPtr->xany.serial-(unsigned int)serial) > 0)) {
2436
    return TK_DEFER_EVENT;
2437
  } else {
2438
    return TK_PROCESS_EVENT;
2439
  }
2440
}
2441
 
2442
/*
2443
 *--------------------------------------------------------------
2444
 *
2445
 * TableValidateChange --
2446
 *      This procedure is invoked when any character is added or
2447
 *      removed from the table widget, or a set has triggered validation.
2448
 *
2449
 * Results:
2450
 *      TCL_OK    if the validatecommand accepts the new string,
2451
 *      TCL_BREAK if the validatecommand rejects the new string,
2452
 *      TCL_ERROR if any problems occured with validatecommand.
2453
 *
2454
 * Side effects:
2455
 *      The insertion/deletion may be aborted, and the
2456
 *      validatecommand might turn itself off (if an error
2457
 *      or loop condition arises).
2458
 *
2459
 *--------------------------------------------------------------
2460
 */
2461
static int
2462
TableValidateChange(tablePtr, r, c, old, new, index)
2463
     register Table *tablePtr;  /* Table that needs validation. */
2464
     int r, c;                  /* row,col index of cell in user coords */
2465
     char *old;                 /* current value of cell */
2466
     char *new;                 /* potential new value of cell */
2467
     int index;                 /* index of insert/delete, -1 otherwise */
2468
{
2469
  register Tcl_Interp *interp = tablePtr->interp;
2470
  int code, bool;
2471
  Tk_RestrictProc *restrict;
2472
  ClientData cdata;
2473
  Tcl_DString script;
2474
 
2475
  if (tablePtr->valCmd == NULL || tablePtr->validate == 0) {
2476
    return TCL_OK;
2477
  }
2478
 
2479
  /* Magic code to make this bit of code synchronous in the face of
2480
   * possible new key events */
2481
  XSync(tablePtr->display, False);
2482
  restrict = Tk_RestrictEvents(TableRestrictProc, (ClientData)
2483
                               NextRequest(tablePtr->display), &cdata);
2484
 
2485
  /*
2486
   * If we're already validating, then we're hitting a loop condition
2487
   * Return and set validate to 0 to disallow further validations
2488
   * and prevent current validation from finishing
2489
   */
2490
  if (tablePtr->flags & VALIDATING) {
2491
    tablePtr->validate = 0;
2492
    return TCL_OK;
2493
  }
2494
  tablePtr->flags |= VALIDATING;
2495
 
2496
  /* Now form command string and run through the -validatecommand */
2497
  Tcl_DStringInit(&script);
2498
  ExpandPercents(tablePtr, tablePtr->valCmd, r, c, old, new, index, &script,
2499
                 CMD_VALIDATE);
2500
  code = Tcl_GlobalEval(tablePtr->interp, Tcl_DStringValue(&script));
2501
  Tcl_DStringFree(&script);
2502
 
2503
  if (code != TCL_OK && code != TCL_RETURN) {
2504
    Tcl_AddErrorInfo(interp, "\n\t(in validation command executed by table)");
2505
    Tk_BackgroundError(interp);
2506
    code = TCL_ERROR;
2507
  } else if (Tcl_GetBoolean(interp, Tcl_GetStringResult(interp),
2508
                            &bool) != TCL_OK) {
2509
    Tcl_AddErrorInfo(interp, "\n\tboolean not returned by validation command");
2510
    Tk_BackgroundError(interp);
2511
    code = TCL_ERROR;
2512
  } else {
2513
    if (bool)
2514
      code = TCL_OK;
2515
    else
2516
      code = TCL_BREAK;
2517
  }
2518
  Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
2519
 
2520
  /*
2521
   * If ->validate has become VALIDATE_NONE during the validation,
2522
   * it means that a loop condition almost occured.  Do not allow
2523
   * this validation result to finish.
2524
   */
2525
  if (tablePtr->validate == 0) {
2526
    code = TCL_ERROR;
2527
  }
2528
 
2529
  /* If validate will return ERROR, then disallow further validations */
2530
  if (code == TCL_ERROR) {
2531
    tablePtr->validate = 0;
2532
  }
2533
 
2534
  Tk_RestrictEvents(restrict, cdata, &cdata);
2535
  tablePtr->flags &= ~VALIDATING;
2536
 
2537
  return code;
2538
}
2539
 
2540
/*
2541
 *--------------------------------------------------------------
2542
 *
2543
 * ExpandPercents --
2544
 *      Given a command and an event, produce a new command
2545
 *      by replacing % constructs in the original command
2546
 *      with information from the X event.
2547
 *
2548
 * Results:
2549
 *      The new expanded command is appended to the dynamic string
2550
 *      given by dsPtr.
2551
 *
2552
 * Side effects:
2553
 *      None.
2554
 *
2555
 *--------------------------------------------------------------
2556
 */
2557
void
2558
ExpandPercents(tablePtr, before, r, c, old, new, index, dsPtr, cmdType)
2559
     Table *tablePtr;           /* Table that needs validation. */
2560
     char *before;              /* Command containing percent
2561
                                 * expressions to be replaced. */
2562
     int r, c;                  /* row,col index of cell */
2563
     char *old;                 /* current value of cell */
2564
     char *new;                 /* potential new value of cell */
2565
     int index;                 /* index of insert/delete */
2566
     Tcl_DString *dsPtr;        /* Dynamic string in which to append
2567
                                 * new command. */
2568
     int cmdType;               /* type of command to make %-subs for */
2569
{
2570
  int spaceNeeded, cvtFlags;    /* Used to substitute string as proper Tcl
2571
                                 * list element. */
2572
  int number, length;
2573
  char *string;
2574
  char buf[INDEX_BUFSIZE];
2575
 
2576
  /* This returns the static value of the string as set in the array */
2577
  if (old == NULL && cmdType == CMD_VALIDATE) {
2578
    old = TableGetCellValue(tablePtr, r, c);
2579
  }
2580
 
2581
  while (1) {
2582
    /*
2583
     * Find everything up to the next % character and append it
2584
     * to the result string.
2585
     */
2586
    for (string = before; (*string != 0) && (*string != '%'); string++) {
2587
      /* Empty loop body. */
2588
    }
2589
    if (string != before) {
2590
      Tcl_DStringAppend(dsPtr, before, string-before);
2591
      before = string;
2592
    }
2593
    if (*before == 0) break;
2594
 
2595
    /* There's a percent sequence here.  Process it. */
2596
    number = 0;
2597
    string = "??";
2598
    /* cmdType independent substitutions */
2599
    switch (before[1]) {
2600
    case 'c':
2601
      number = c;
2602
      goto doNumber;
2603
    case 'C': /* index of cell */
2604
      TableMakeArrayIndex(r, c, buf);
2605
      string = buf;
2606
      goto doString;
2607
    case 'r':
2608
      number = r;
2609
      goto doNumber;
2610
    case 'i': /* index of cursor OR |number| of cells selected */
2611
      number = index;
2612
      goto doNumber;
2613
    case 's': /* Current cell value */
2614
      string = old;
2615
      goto doString;
2616
    case 'S': /* Potential new value of cell */
2617
      string = (new?new:old);
2618
      goto doString;
2619
    case 'W': /* widget name */
2620
      string = Tk_PathName(tablePtr->tkwin);
2621
      goto doString;
2622
    default:
2623
      buf[0] = before[1];
2624
      buf[1] = '\0';
2625
      string = buf;
2626
      goto doString;
2627
    }
2628
 
2629
  doNumber:
2630
    sprintf(buf, "%d", number);
2631
    string = buf;
2632
 
2633
  doString:
2634
    spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
2635
    length = Tcl_DStringLength(dsPtr);
2636
    Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
2637
    spaceNeeded = Tcl_ConvertElement(string, Tcl_DStringValue(dsPtr) + length,
2638
                                     cvtFlags | TCL_DONT_USE_BRACES);
2639
    Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
2640
    before += 2;
2641
  }
2642
  Tcl_DStringAppend(dsPtr, "", 1);
2643
}
2644
 
2645
/*
2646
 *----------------------------------------------------------------------
2647
 *
2648
 * TableDeleteChars --
2649
 *      Remove one or more characters from an table widget.
2650
 *
2651
 * Results:
2652
 *      None.
2653
 *
2654
 * Side effects:
2655
 *      Memory gets freed, the table gets modified and (eventually)
2656
 *      redisplayed.
2657
 *
2658
 *----------------------------------------------------------------------
2659
 */
2660
static void
2661
TableDeleteChars(tablePtr, index, count)
2662
    register Table *tablePtr;   /* Table widget to modify. */
2663
    int index;                  /* Index of first character to delete. */
2664
    int count;                  /* How many characters to delete. */
2665
{
2666
  int x, y, width, height;
2667
#if (TK_MINOR_VERSION > 0)
2668
  int byteIndex, byteCount, newByteCount, numBytes, numChars;
2669
  char *new, *string;
2670
 
2671
  string = tablePtr->activeBuf;
2672
  numBytes = strlen(string);
2673
  numChars = Tcl_NumUtfChars(string, numBytes);
2674
  if ((index + count) > numChars) {
2675
    count = numChars - index;
2676
  }
2677
  if (count <= 0) {
2678
    return;
2679
  }
2680
 
2681
  byteIndex = Tcl_UtfAtIndex(string, index) - string;
2682
  byteCount = Tcl_UtfAtIndex(string + byteIndex, count) - (string + byteIndex);
2683
 
2684
  newByteCount = numBytes + 1 - byteCount;
2685
  new = (char *) ckalloc((unsigned) newByteCount);
2686
  memcpy(new, string, (size_t) byteIndex);
2687
  strcpy(new + byteIndex, string + byteIndex + byteCount);
2688
#else
2689
  int oldlen;
2690
  char *new;
2691
 
2692
  /* this gets the length of the string, as well as ensuring that
2693
   * the cursor isn't beyond the end char */
2694
  TableGetIcursor(tablePtr, "end", &oldlen);
2695
 
2696
  if ((index+count) > oldlen)
2697
    count = oldlen-index;
2698
  if (count <= 0)
2699
    return;
2700
 
2701
  new = (char *) ckalloc((unsigned)(oldlen-count+1));
2702
  strncpy(new, tablePtr->activeBuf, (size_t) index);
2703
  strcpy(new+index, tablePtr->activeBuf+index+count);
2704
  /* make sure this string is null terminated */
2705
  new[oldlen-count] = '\0';
2706
#endif
2707
  /* This prevents deletes on BREAK or validation error. */
2708
  if (tablePtr->validate &&
2709
      TableValidateChange(tablePtr, tablePtr->activeRow+tablePtr->rowOffset,
2710
                          tablePtr->activeCol+tablePtr->colOffset,
2711
                          tablePtr->activeBuf, new, index) != TCL_OK) {
2712
    ckfree(new);
2713
    return;
2714
  }
2715
 
2716
  ckfree(tablePtr->activeBuf);
2717
  tablePtr->activeBuf = new;
2718
 
2719
  /* mark the text as changed */
2720
  tablePtr->flags |= TEXT_CHANGED;
2721
 
2722
  if (tablePtr->icursor >= index) {
2723
    if (tablePtr->icursor >= (index+count)) {
2724
      tablePtr->icursor -= count;
2725
    } else {
2726
      tablePtr->icursor = index;
2727
    }
2728
  }
2729
 
2730
  TableSetActiveIndex(tablePtr);
2731
 
2732
  TableCellCoords(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
2733
                   &x, &y, &width, &height);
2734
  TableInvalidate(tablePtr, x, y, width, height, INV_FORCE);
2735
}
2736
 
2737
/*
2738
 *----------------------------------------------------------------------
2739
 *
2740
 * TableInsertChars --
2741
 *      Add new characters to the active cell of a table widget.
2742
 *
2743
 * Results:
2744
 *      None.
2745
 *
2746
 * Side effects:
2747
 *      New information gets added to tablePtr; it will be redisplayed
2748
 *      soon, but not necessarily immediately.
2749
 *
2750
 *----------------------------------------------------------------------
2751
 */
2752
static void
2753
TableInsertChars(tablePtr, index, value)
2754
    register Table *tablePtr;   /* Table that is to get the new elements. */
2755
    int index;                  /* Add the new elements before this element. */
2756
    char *value;                /* New characters to add (NULL-terminated
2757
                                 * string). */
2758
{
2759
#if (TK_MINOR_VERSION > 0)
2760
  int x, y, width, height, oldlen;
2761
  int byteIndex, byteCount;
2762
  char *new, *string;
2763
 
2764
  string = tablePtr->activeBuf;
2765
  byteIndex = Tcl_UtfAtIndex(string, index) - string;
2766
  byteCount = strlen(value);
2767
  if (byteCount == 0) {
2768
    return;
2769
  }
2770
 
2771
  /* Is this an autoclear and this is the first update */
2772
  /* Note that this clears without validating */
2773
  if (tablePtr->autoClear && !(tablePtr->flags & TEXT_CHANGED)) {
2774
    /* set the buffer to be empty */
2775
    tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf, 1);
2776
    tablePtr->activeBuf[0] = '\0';
2777
    /* the insert position now has to be 0 */
2778
    index = 0;
2779
  }
2780
 
2781
  oldlen = strlen(string);
2782
  new = (char *) ckalloc((unsigned)(oldlen + byteCount + 1));
2783
  memcpy(new, string, (size_t) byteIndex);
2784
  strcpy(new + byteIndex, value);
2785
  strcpy(new + byteIndex + byteCount, string + byteIndex);
2786
 
2787
  /* validate potential new active buffer */
2788
  /* This prevents inserts on either BREAK or validation error. */
2789
  if (tablePtr->validate &&
2790
      TableValidateChange(tablePtr, tablePtr->activeRow+tablePtr->rowOffset,
2791
                          tablePtr->activeCol+tablePtr->colOffset,
2792
                          tablePtr->activeBuf, new, byteIndex) != TCL_OK) {
2793
    ckfree(new);
2794
    return;
2795
  }
2796
 
2797
  /*
2798
   * The following construction is used because inserting improperly
2799
   * formed UTF-8 sequences between other improperly formed UTF-8
2800
   * sequences could result in actually forming valid UTF-8 sequences;
2801
   * the number of characters added may not be Tcl_NumUtfChars(string, -1),
2802
   * because of context.  The actual number of characters added is how
2803
   * many characters were are in the string now minus the number that
2804
   * used to be there.
2805
   */
2806
 
2807
  if (tablePtr->icursor >= index) {
2808
    tablePtr->icursor += Tcl_NumUtfChars(new, oldlen+byteCount)
2809
      - Tcl_NumUtfChars(tablePtr->activeBuf, oldlen);
2810
  }
2811
 
2812
  ckfree(string);
2813
  tablePtr->activeBuf = new;
2814
 
2815
#else
2816
  int x, y, width, height, oldlen, newlen;
2817
  char *new;
2818
 
2819
  newlen = strlen(value);
2820
  if (newlen == 0) return;
2821
 
2822
  /* Is this an autoclear and this is the first update */
2823
  /* Note that this clears without validating */
2824
  if (tablePtr->autoClear && !(tablePtr->flags & TEXT_CHANGED)) {
2825
    /* set the buffer to be empty */
2826
    tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf, 1);
2827
    tablePtr->activeBuf[0] = '\0';
2828
    /* the insert position now has to be 0 */
2829
    index = 0;
2830
  }
2831
  oldlen = strlen(tablePtr->activeBuf);
2832
  /* get the buffer to at least the right length */
2833
  new = (char *) ckalloc((unsigned)(oldlen+newlen+1));
2834
  strncpy(new, tablePtr->activeBuf, (size_t) index);
2835
  strcpy(new+index, value);
2836
  strcpy(new+index+newlen, (tablePtr->activeBuf)+index);
2837
  /* make sure this string is null terminated */
2838
  new[oldlen+newlen] = '\0';
2839
 
2840
  /* validate potential new active buffer */
2841
  /* This prevents inserts on either BREAK or validation error. */
2842
  if (tablePtr->validate &&
2843
      TableValidateChange(tablePtr, tablePtr->activeRow+tablePtr->rowOffset,
2844
                          tablePtr->activeCol+tablePtr->colOffset,
2845
                          tablePtr->activeBuf, new, index) != TCL_OK) {
2846
    ckfree(new);
2847
    return;
2848
  }
2849
  ckfree(tablePtr->activeBuf);
2850
  tablePtr->activeBuf = new;
2851
 
2852
  if (tablePtr->icursor >= index) {
2853
    tablePtr->icursor += newlen;
2854
  }
2855
#endif
2856
 
2857
  /* mark the text as changed */
2858
  tablePtr->flags |= TEXT_CHANGED;
2859
 
2860
  TableSetActiveIndex(tablePtr);
2861
 
2862
  TableCellCoords(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
2863
                   &x, &y, &width, &height);
2864
  TableInvalidate(tablePtr, x, y, width, height, INV_FORCE);
2865
}
2866
 
2867
/*
2868
 *----------------------------------------------------------------------
2869
 *
2870
 * TableModifyRCaux --
2871
 *      Helper function that does the core work of moving rows/cols
2872
 *      and associated tags.
2873
 *
2874
 * Results:
2875
 *      None.
2876
 *
2877
 * Side effects:
2878
 *      Moves cell data and possibly tag data
2879
 *
2880
 *----------------------------------------------------------------------
2881
 */
2882
static void
2883
TableModifyRCaux(tablePtr, type, which, movetag, tagTblPtr, dimTblPtr,
2884
                 offset, from, to, lo, hi, check)
2885
    Table *tablePtr;    /* Information about text widget. */
2886
    int type;           /* insert (CMD_INSERT) | delete (CMD_DELETE) */
2887
    int which;          /* rows (MOD_ROWS) or cols (MOD_COLS) */
2888
    int movetag;        /* whether tags are supposed to be moved */
2889
    Tcl_HashTable *tagTblPtr, *dimTblPtr; /* Pointers to the row/col tags
2890
                                           * and width/height tags */
2891
    int offset;         /* appropriate offset */
2892
    int from, to;       /* the from and to row/col */
2893
    int lo, hi;         /* the lo and hi col/row */
2894
    int check;          /* the boundary check for shifting items */
2895
{
2896
  int j, dummy;
2897
  char buf[INDEX_BUFSIZE], buf1[INDEX_BUFSIZE];
2898
  Tcl_HashEntry *entryPtr, *newPtr;
2899
 
2900
  /* move row/col style && width/height here */
2901
  if (movetag) {
2902
    if ((entryPtr=Tcl_FindHashEntry(tagTblPtr, (char *)from)) != NULL) {
2903
      Tcl_DeleteHashEntry(entryPtr);
2904
    }
2905
    if ((entryPtr=Tcl_FindHashEntry(dimTblPtr, (char *)from-offset)) != NULL) {
2906
      Tcl_DeleteHashEntry(entryPtr);
2907
    }
2908
    if (!check) {
2909
      if ((entryPtr=Tcl_FindHashEntry(tagTblPtr, (char *)to)) != NULL) {
2910
        newPtr = Tcl_CreateHashEntry(tagTblPtr, (char *)from, &dummy);
2911
        Tcl_SetHashValue(newPtr, Tcl_GetHashValue(entryPtr));
2912
        Tcl_DeleteHashEntry(entryPtr);
2913
      }
2914
      if ((entryPtr=Tcl_FindHashEntry(dimTblPtr, (char *)to-offset)) != NULL) {
2915
        newPtr = Tcl_CreateHashEntry(dimTblPtr, (char *)from-offset, &dummy);
2916
        Tcl_SetHashValue(newPtr, Tcl_GetHashValue(entryPtr));
2917
        Tcl_DeleteHashEntry(entryPtr);
2918
      }
2919
    }
2920
  }
2921
  for (j = lo; j <= hi; j++) {
2922
    if (which == MOD_COLS) {
2923
      TableMakeArrayIndex(j, from, buf);
2924
      TableMakeArrayIndex(j, to, buf1);
2925
      TableSetCellValue(tablePtr, j, from, check ? "" :
2926
                        TableGetCellValue(tablePtr, j, to));
2927
    } else {
2928
      TableMakeArrayIndex(from, j, buf);
2929
      TableMakeArrayIndex(to, j, buf1);
2930
      TableSetCellValue(tablePtr, from, j, check ? "" :
2931
                        TableGetCellValue(tablePtr, to, j));
2932
    }
2933
    /* move cell style here */
2934
    if (movetag) {
2935
      if ((entryPtr=Tcl_FindHashEntry(tablePtr->cellStyles,buf)) != NULL) {
2936
        Tcl_DeleteHashEntry(entryPtr);
2937
      }
2938
      if (!check &&
2939
          (entryPtr=Tcl_FindHashEntry(tablePtr->cellStyles,buf1))!=NULL) {
2940
        newPtr = Tcl_CreateHashEntry(tablePtr->cellStyles, buf, &dummy);
2941
        Tcl_SetHashValue(newPtr, Tcl_GetHashValue(entryPtr));
2942
        Tcl_DeleteHashEntry(entryPtr);
2943
      }
2944
    }
2945
  }
2946
}
2947
 
2948
/*
2949
 *----------------------------------------------------------------------
2950
 *
2951
 * TableModifyRC --
2952
 *      Modify rows/cols of the table (insert or delete)
2953
 *
2954
 * Results:
2955
 *      None.
2956
 *
2957
 * Side effects:
2958
 *      Modifies associated row/col data
2959
 *
2960
 *----------------------------------------------------------------------
2961
 */
2962
static int
2963
TableModifyRC(tablePtr, interp, type, which, argc, argv)
2964
    Table *tablePtr;            /* Information about text widget. */
2965
    Tcl_Interp *interp;         /* Current interpreter. */
2966
    int type;                   /* insert (CMD_INSERT) | delete (CMD_DELETE) */
2967
    int which;                  /* rows (MOD_ROWS) or cols (MOD_COLS) */
2968
    int argc;                   /* Number of arguments. */
2969
    char **argv;                /* Argument strings. */
2970
{
2971
  int i, first, lo, hi, idx, c, argsLeft, x, y, offset;
2972
  int maxrow, maxcol, maxkey, minkey, movetitle, movetag, movedim;
2973
  size_t length;
2974
  char *arg;
2975
  Tcl_HashTable *tagTblPtr, *dimTblPtr;
2976
  Tcl_HashSearch search;
2977
  int *dimPtr;
2978
 
2979
  movetitle     = 1;
2980
  movetag       = 1;
2981
  movedim       = 1;
2982
  maxcol        = tablePtr->cols-1+tablePtr->colOffset;
2983
  maxrow        = tablePtr->rows-1+tablePtr->rowOffset;
2984
  for (i = 3; i < argc; i++) {
2985
    arg = argv[i];
2986
    if (arg[0] != '-') {
2987
      break;
2988
    }
2989
    length = strlen(arg);
2990
    if (length < 2) {
2991
    badSwitch:
2992
      Tcl_AppendResult(interp, "bad switch \"", arg,
2993
                       "\": must be -cols, -keeptitles, -holddimensions, ",
2994
                       "-holdtags, -rows, or --",
2995
                       (char *) NULL);
2996
      return TCL_ERROR;
2997
    }
2998
    c = arg[1];
2999
    if ((c == 'h') && (length > 5) &&
3000
        (strncmp(argv[i], "-holddimensions", length) == 0)) {
3001
      movedim = 0;
3002
    } else if ((c == 'h') && (length > 5) &&
3003
               (strncmp(argv[i], "-holdtags", length) == 0)) {
3004
      movetag = 0;
3005
    } else if ((c == 'k') && (strncmp(argv[i], "-keeptitles", length) == 0)) {
3006
      movetitle = 0;
3007
    } else if ((c == 'c') && (strncmp(argv[i], "-cols", length) == 0)) {
3008
      if (i >= (argc-1)) {
3009
        Tcl_SetResult(interp, "no value given for \"-cols\" option",
3010
                      TCL_STATIC);
3011
        return TCL_ERROR;
3012
      }
3013
      if (Tcl_GetInt(interp, argv[++i], &maxcol) != TCL_OK) {
3014
        return TCL_ERROR;
3015
      }
3016
      maxcol = MAX(maxcol, tablePtr->titleCols+tablePtr->colOffset);
3017
    } else if ((c == 'r') && (strncmp(argv[i], "-rows", length) == 0)) {
3018
      if (i >= (argc-1)) {
3019
        Tcl_SetResult(interp, "no value given for \"-rows\" option",
3020
                      TCL_STATIC);
3021
        return TCL_ERROR;
3022
      }
3023
      if (Tcl_GetInt(interp, argv[++i], &maxrow) != TCL_OK) {
3024
        return TCL_ERROR;
3025
      }
3026
      maxrow = MAX(maxrow, tablePtr->titleRows+tablePtr->rowOffset);
3027
    } else if ((c == '-') && (strncmp(argv[i], "--", length) == 0)) {
3028
      i++;
3029
      break;
3030
    } else {
3031
      goto badSwitch;
3032
    }
3033
  }
3034
  argsLeft = argc - i;
3035
  if (argsLeft != 1 && argsLeft != 2) {
3036
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3037
                     (type == CMD_DELETE) ? " delete" : " insert",
3038
                     (which == MOD_COLS) ? " cols" : " rows",
3039
                     " ?switches? index ?count?\"", (char *) NULL);
3040
    return TCL_ERROR;
3041
  }
3042
 
3043
  c = 1;
3044
  if (strcmp(argv[i], "end") == 0) {
3045
    /* allow "end" to be specified as an index */
3046
    idx = (which == MOD_COLS) ? maxcol : maxrow;
3047
  } else if (Tcl_GetInt(interp, argv[i], &idx) != TCL_OK) {
3048
    return TCL_ERROR;
3049
  }
3050
  if (argsLeft == 2 && Tcl_GetInt(interp, argv[++i], &c) != TCL_OK) {
3051
    return TCL_ERROR;
3052
  }
3053
  if (tablePtr->state == STATE_DISABLED || c == 0)
3054
    return TCL_OK;
3055
 
3056
  if (which == MOD_COLS) {
3057
    maxkey      = maxcol;
3058
    minkey      = tablePtr->colOffset+tablePtr->titleCols;
3059
    lo          = tablePtr->rowOffset+(movetitle?0:tablePtr->titleRows);
3060
    hi          = maxrow;
3061
    offset      = tablePtr->colOffset;
3062
    tagTblPtr   = tablePtr->colStyles;
3063
    dimTblPtr   = tablePtr->colWidths;
3064
    dimPtr      = &(tablePtr->cols);
3065
  } else {
3066
    maxkey      = maxrow;
3067
    minkey      = tablePtr->rowOffset+tablePtr->titleRows;
3068
    lo          = tablePtr->colOffset+(movetitle?0:tablePtr->titleCols);
3069
    hi          = maxcol;
3070
    offset      = tablePtr->rowOffset;
3071
    tagTblPtr   = tablePtr->rowStyles;
3072
    dimTblPtr   = tablePtr->rowHeights;
3073
    dimPtr      = &(tablePtr->rows);
3074
  }
3075
 
3076
  if (type == CMD_DELETE) {
3077
    /* Handle row/col deletion */
3078
    first = MAX(MIN(idx,idx+c), minkey);
3079
    /* (index = i && count = 1) == (index = i && count = -1) */
3080
    if (c < 0) {
3081
      /* if the count is negative, make sure that the col count will delete
3082
       * no greater than the original index */
3083
      c = idx-first;
3084
      first++;
3085
    }
3086
    if (movedim) {
3087
      *dimPtr -= c;
3088
    }
3089
    for (i = first; i <= maxkey; i++) {
3090
      TableModifyRCaux(tablePtr, type, which, movetag, tagTblPtr,
3091
                       dimTblPtr, offset, i, i+c, lo, hi, ((i+c)>maxkey));
3092
    }
3093
  } else {
3094
    /* Handle row/col insertion */
3095
    first = MAX(MIN(idx, maxkey), minkey);
3096
    /* +count means insert after index, -count means insert before index */
3097
    if (c < 0) {
3098
      c = -c;
3099
    } else {
3100
      first++;
3101
    }
3102
    if (movedim) {
3103
      maxkey += c;
3104
      *dimPtr += c;
3105
    }
3106
    for (i = maxkey; i >= first; i--) {
3107
      /* move row/col style && width/height here */
3108
      TableModifyRCaux(tablePtr, type, which, movetag, tagTblPtr,
3109
                       dimTblPtr, offset, i, i-c, lo, hi, ((i-c)<first));
3110
    }
3111
  }
3112
  if (Tcl_FirstHashEntry(tablePtr->selCells, &search) != NULL) {
3113
    /* clear selection - forceful, but effective */
3114
    Tcl_DeleteHashTable(tablePtr->selCells);
3115
    ckfree((char *) (tablePtr->selCells));
3116
    tablePtr->selCells = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
3117
    Tcl_InitHashTable(tablePtr->selCells, TCL_STRING_KEYS);
3118
  }
3119
 
3120
  TableAdjustParams(tablePtr);
3121
  if (which == MOD_COLS) {
3122
    TableCellCoords(tablePtr, 0, first, &x, &y, &offset, &offset);
3123
    TableInvalidate(tablePtr, x, y,
3124
                    Tk_Width(tablePtr->tkwin)-x,
3125
                    Tk_Height(tablePtr->tkwin), 0);
3126
  } else {
3127
    TableCellCoords(tablePtr, first, 0, &x, &y, &offset, &offset);
3128
    TableInvalidate(tablePtr, x, y,
3129
                    Tk_Width(tablePtr->tkwin),
3130
                    Tk_Height(tablePtr->tkwin)-y, 0);
3131
  }
3132
  return TCL_OK;
3133
}
3134
 
3135
/*
3136
 *--------------------------------------------------------------
3137
 *
3138
 * TableWidgetCmd --
3139
 *      This procedure is invoked to process the Tcl command
3140
 *      that corresponds to a widget managed by this module.
3141
 *      See the user documentation for details on what it does.
3142
 *
3143
 * Results:
3144
 *      A standard Tcl result.
3145
 *
3146
 * Side effects:
3147
 *      See the user documentation.
3148
 *
3149
 *--------------------------------------------------------------
3150
 */
3151
static int
3152
TableWidgetCmd(clientData, interp, argc, argv)
3153
     ClientData clientData;             /* Information about listbox widget. */
3154
     Tcl_Interp *interp;                /* Current interpreter. */
3155
     int argc;                          /* Number of arguments. */
3156
     char **argv;                       /* Argument strings. */
3157
{
3158
  Tcl_HashEntry *entryPtr;
3159
  Tcl_HashSearch search;
3160
  Tcl_HashTable *hashTablePtr;
3161
  int result, retval, sub_retval, row, col, x, y;
3162
  int i, width, height, dummy, key, value, posn, offset;
3163
  char buf1[INDEX_BUFSIZE], buf2[INDEX_BUFSIZE];
3164
 
3165
  Table *tablePtr = (Table *) clientData;
3166
 
3167
  if (argc < 2) {
3168
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3169
                     " option ?arg arg ...?\"", (char *) NULL);
3170
    return TCL_ERROR;
3171
  }
3172
  Tcl_Preserve(clientData);
3173
 
3174
  result = TCL_OK;
3175
  /* parse the first parameter */
3176
  retval = Cmd_Parse(interp, main_cmds, argv[1]);
3177
 
3178
  /* Switch on the parameter value */
3179
  switch (retval) {
3180
  case 0:
3181
    /* error, the return string is already set up */
3182
    result = TCL_ERROR;
3183
    break;
3184
 
3185
  case CMD_ACTIVATE:
3186
    if (argc != 3) {
3187
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3188
                       " activate index\"", (char *)NULL);
3189
      result = TCL_ERROR;
3190
    } else if (TableGetIndex(tablePtr, argv[2], &row, &col) == TCL_ERROR) {
3191
      result = TCL_ERROR;
3192
    } else {
3193
      /* convert to valid active index in real coords */
3194
      row -= tablePtr->rowOffset;
3195
      col -= tablePtr->colOffset;
3196
      /* we do this regardless, to avoid cell commit problems */
3197
      if ((tablePtr->flags & HAS_ACTIVE) &&
3198
          (tablePtr->flags & TEXT_CHANGED)) {
3199
        tablePtr->flags &= ~TEXT_CHANGED;
3200
        TableSetCellValue(tablePtr, tablePtr->activeRow+tablePtr->rowOffset,
3201
                          tablePtr->activeCol+tablePtr->colOffset,
3202
                          tablePtr->activeBuf);
3203
      }
3204
      if (row != tablePtr->activeRow || col != tablePtr->activeCol) {
3205
        if (tablePtr->flags & HAS_ACTIVE) {
3206
          TableMakeArrayIndex(tablePtr->activeRow+tablePtr->rowOffset,
3207
                              tablePtr->activeCol+tablePtr->colOffset, buf1);
3208
        } else {
3209
          buf1[0] = '\0';
3210
        }
3211
        tablePtr->flags |= HAS_ACTIVE;
3212
        tablePtr->flags &= ~ACTIVE_DISABLED;
3213
        tablePtr->activeRow = row;
3214
        tablePtr->activeCol = col;
3215
        if (tablePtr->activeLayout) {
3216
          Tk_FreeTextLayout(tablePtr->activeLayout);
3217
          tablePtr->activeLayout = NULL;
3218
        }
3219
        TableAdjustActive(tablePtr);
3220
        TableConfigCursor(tablePtr);
3221
        if (!(tablePtr->flags & BROWSE_CMD) && tablePtr->browseCmd != NULL) {
3222
          Tcl_DString script;
3223
          tablePtr->flags |= BROWSE_CMD;
3224
          row = tablePtr->activeRow+tablePtr->rowOffset;
3225
          col = tablePtr->activeCol+tablePtr->colOffset;
3226
          TableMakeArrayIndex(row, col, buf2);
3227
          Tcl_DStringInit(&script);
3228
          ExpandPercents(tablePtr, tablePtr->browseCmd, row, col, buf1, buf2,
3229
                         tablePtr->icursor, &script, CMD_ACTIVATE);
3230
          result = Tcl_GlobalEval(interp, Tcl_DStringValue(&script));
3231
          if (result == TCL_OK || result == TCL_RETURN)
3232
            Tcl_ResetResult(interp);
3233
          Tcl_DStringFree(&script);
3234
          tablePtr->flags &= ~BROWSE_CMD;
3235
        }
3236
      } else if ((tablePtr->activeLayout != NULL) &&
3237
                 !(tablePtr->flags & ACTIVE_DISABLED) && argv[2][0] == '@' &&
3238
                 TableCellVCoords(tablePtr, row, col, &x, &y,
3239
                                  &dummy, &dummy, 0)) {
3240
        /* we are clicking into the same cell */
3241
        /* If it was activated with @x,y indexing, find the closest char */
3242
        char *p;
3243
 
3244
        /* no error checking because GetIndex did it for us */
3245
        p = argv[2]+1;
3246
        x = strtol(p, &p, 0) - x - tablePtr->activeX;
3247
        y = strtol(++p, &p, 0) - y - tablePtr->activeY;
3248
 
3249
        tablePtr->icursor = Tk_PointToChar(tablePtr->activeLayout, x, y);
3250
        TableConfigCursor(tablePtr);
3251
      }
3252
      tablePtr->flags |= HAS_ACTIVE;
3253
    }
3254
    break;      /* ACTIVATE */
3255
 
3256
  case CMD_BBOX: {
3257
    /* bounding box of cell(s) */
3258
    if (argc < 3 || argc > 4) {
3259
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3260
                        " bbox first ?last?\"", (char *) NULL);
3261
      result = TCL_ERROR;
3262
    } else if (TableGetIndex(tablePtr, argv[2], &row, &col) == TCL_ERROR) {
3263
      result = TCL_ERROR;
3264
    } else if (argc == 3) {
3265
      row -= tablePtr->rowOffset; col -= tablePtr->colOffset;
3266
      if (TableCellVCoords(tablePtr, row, col, &x, &y, &width, &height, 0)) {
3267
        sprintf(buf1, "%d %d %d %d", x, y, width, height);
3268
        Tcl_SetResult(interp, buf1, TCL_VOLATILE);
3269
      }
3270
    } else if (TableGetIndex(tablePtr, argv[3], &x, &y) == TCL_ERROR) {
3271
      result = TCL_ERROR;
3272
    } else {
3273
      int r1, c1, r2, c2, minX = 99999, minY = 99999, maxX = 0, maxY = 0;
3274
      row -= tablePtr->rowOffset; col -= tablePtr->colOffset;
3275
      x -= tablePtr->rowOffset; y -= tablePtr->colOffset;
3276
      r1 = MIN(row,x); r2 = MAX(row,x);
3277
      c1 = MIN(col,y); c2 = MAX(col,y);
3278
      key = 0;
3279
      for (row = r1; row <= r2; row++) {
3280
        for (col = c1; col <= c2; col++) {
3281
          if (TableCellVCoords(tablePtr, row, col, &x, &y,
3282
                               &width, &height, 0)) {
3283
            /* Get max bounding box */
3284
            if (x < minX) minX = x;
3285
            if (y < minY) minY = y;
3286
            if (x+width > maxX) maxX = x+width;
3287
            if (y+height > maxY) maxY = y+height;
3288
            key++;
3289
          }
3290
          /* FIX - This could break on else for speed */
3291
        }
3292
      }
3293
      if (key) {
3294
        sprintf(buf1, "%d %d %d %d", minX, minY, maxX-minX, maxY-minY);
3295
        Tcl_SetResult(interp, buf1, TCL_VOLATILE);
3296
      }
3297
    }
3298
  }
3299
  break;        /* BBOX */
3300
 
3301
  case CMD_BORDER:
3302
    if (argc > 6) {
3303
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3304
                       " border mark|dragto x y ?r|c?\"", (char *) NULL);
3305
      result = TCL_ERROR;
3306
      break;
3307
    }
3308
    sub_retval = Cmd_Parse(interp, bd_cmds, argv[2]);
3309
    if (sub_retval == 0 || Tcl_GetInt(interp, argv[3], &x) != TCL_OK ||
3310
        Tcl_GetInt(interp, argv[4], &y) != TCL_OK) {
3311
      result = TCL_ERROR;
3312
      break;
3313
    }
3314
    switch (sub_retval) {
3315
    case BD_MARK:
3316
      /* Use x && y to determine if we are over a border */
3317
      value = TableAtBorder(tablePtr, x, y, &row, &col);
3318
      /* Cache the row && col for use in DRAGTO */
3319
      tablePtr->scanMarkRow = row;
3320
      tablePtr->scanMarkCol = col;
3321
      if (!value) break;
3322
      TableCellCoords(tablePtr, row, col, &x, &y, &dummy, &dummy);
3323
      tablePtr->scanMarkX = x;
3324
      tablePtr->scanMarkY = y;
3325
      if (argc == 5 || argv[5][0] == 'r') {
3326
        if (row < 0)
3327
          buf1[0] = '\0';
3328
        else
3329
          sprintf(buf1, "%d", row+tablePtr->rowOffset);
3330
        Tcl_AppendElement(interp, buf1);
3331
      }
3332
      if (argc == 5 || argv[5][0] == 'c') {
3333
        if (col < 0)
3334
          buf1[0] = '\0';
3335
        else
3336
          sprintf(buf1, "%d", col+tablePtr->colOffset);
3337
        Tcl_AppendElement(interp, buf1);
3338
      }
3339
      break;    /* BORDER MARK */
3340
    case BD_DRAGTO:
3341
      /* check to see if we want to resize any borders */
3342
      if (tablePtr->resize == SEL_NONE) break;
3343
      row = tablePtr->scanMarkRow;
3344
      col = tablePtr->scanMarkCol;
3345
      TableCellCoords(tablePtr, row, col, &width, &height, &dummy, &dummy);
3346
      key = 0;
3347
      if (row >= 0 && (tablePtr->resize & SEL_ROW)) {
3348
        /* row border was active, move it */
3349
        /* FIX should this be 1 or 2 bds off? */
3350
        value = y-height-tablePtr->borderWidth;
3351
        if (value < -1) value = -1;
3352
        if (value != tablePtr->scanMarkY) {
3353
          entryPtr = Tcl_CreateHashEntry(tablePtr->rowHeights,
3354
                                         (char *) row, &dummy);
3355
          /* -value means rowHeight will be interp'd as pixels, not lines */
3356
          Tcl_SetHashValue(entryPtr, (ClientData) MIN(0,-value));
3357
          tablePtr->scanMarkY = value;
3358
          key++;
3359
        }
3360
      }
3361
      if (col >= 0 && (tablePtr->resize & SEL_COL)) {
3362
        /* col border was active, move it */
3363
        value = x-width-tablePtr->borderWidth;
3364
        if (value < -1) value = -1;
3365
        if (value != tablePtr->scanMarkX) {
3366
          entryPtr = Tcl_CreateHashEntry(tablePtr->colWidths,
3367
                                         (char *) col, &dummy);
3368
          /* -value means colWidth will be interp'd as pixels, not chars */
3369
          Tcl_SetHashValue(entryPtr, (ClientData) MIN(0,-value));
3370
          tablePtr->scanMarkX = value;
3371
          key++;
3372
        }
3373
      }
3374
      /* Only if something changed do we want to update */
3375
      if (key) {
3376
        TableAdjustParams(tablePtr);
3377
        /* Only rerequest geometry if the basis is the #rows &| #cols */
3378
        if (tablePtr->maxReqCols || tablePtr->maxReqRows)
3379
          TableGeometryRequest(tablePtr);
3380
        TableInvalidateAll(tablePtr, 0);
3381
      }
3382
      break;    /* BORDER DRAGTO */
3383
    }
3384
    break;      /* BORDER */
3385
 
3386
  case CMD_CGET:
3387
    if (argc != 3) {
3388
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3389
                       " cget option\"", (char *) NULL);
3390
      result = TCL_ERROR;
3391
      break;
3392
    }
3393
    result = Tk_ConfigureValue(interp, tablePtr->tkwin, TableConfig,
3394
                               (char *) tablePtr, argv[2], 0);
3395
    break;      /* CGET */
3396
 
3397
  case CMD_CLEAR:
3398
    if (argc < 3 || argc > 5) {
3399
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3400
                       " clear option ?first? ?last?\"", (char *) NULL);
3401
      result = TCL_ERROR;
3402
      break;
3403
    }
3404
 
3405
    sub_retval = Cmd_Parse(interp, clear_cmds, argv[2]);
3406
    result = TableClear(tablePtr, sub_retval,
3407
                        (argc>3)?argv[3]:NULL, (argc>4)?argv[4]:NULL);
3408
    break;      /* CLEAR */
3409
 
3410
  case CMD_CONFIGURE:
3411
    switch (argc) {
3412
    case 2:
3413
      result = Tk_ConfigureInfo(interp, tablePtr->tkwin, TableConfig,
3414
                                (char *) tablePtr, (char *) NULL, 0);
3415
      break;
3416
    case 3:
3417
      result = Tk_ConfigureInfo(interp, tablePtr->tkwin, TableConfig,
3418
                                (char *) tablePtr, argv[2], 0);
3419
      break;
3420
    default:
3421
      result = TableConfigure(interp, tablePtr, argc - 2, argv + 2,
3422
                              TK_CONFIG_ARGV_ONLY, 0);
3423
    }
3424
    break;      /* CONFIGURE */
3425
 
3426
  case CMD_CURVALUE:
3427
    /* Get current active cell buffer */
3428
    if (argc > 3) {
3429
      Tcl_AppendResult(interp, "wrong # args: should be \"",
3430
                       argv[0], " curvalue ?<value>?\"", (char *)NULL);
3431
      result = TCL_ERROR;
3432
    } else if (tablePtr->flags & HAS_ACTIVE) {
3433
      if (argc == 3 && strcmp(argv[2], tablePtr->activeBuf)) {
3434
        key = TCL_OK;
3435
        /* validate potential new active buffer contents
3436
         * only accept if validation returns acceptance. */
3437
        if (tablePtr->validate &&
3438
            TableValidateChange(tablePtr,
3439
                                tablePtr->activeRow+tablePtr->rowOffset,
3440
                                tablePtr->activeCol+tablePtr->colOffset,
3441
                                tablePtr->activeBuf,
3442
                                argv[2], tablePtr->icursor) != TCL_OK) {
3443
          break;
3444
        }
3445
        tablePtr->activeBuf = (char *)ckrealloc(tablePtr->activeBuf,
3446
                                                strlen(argv[2])+1);
3447
        strcpy(tablePtr->activeBuf, argv[2]);
3448
        /* mark the text as changed */
3449
        tablePtr->flags |= TEXT_CHANGED;
3450
        TableSetActiveIndex(tablePtr);
3451
        /* check for possible adjustment of icursor */
3452
        TableGetIcursor(tablePtr, "insert", (int *)0);
3453
        TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
3454
                     CELL|INV_FORCE);
3455
        if (key == TCL_ERROR) {
3456
          result = TCL_ERROR;
3457
          break;
3458
        }
3459
      }
3460
      Tcl_AppendResult(interp, tablePtr->activeBuf, (char *)NULL);
3461
    }
3462
    break;      /* CURVALUE */
3463
 
3464
  case CMD_CURSELECTION:
3465
    if ((argc != 2 && argc != 4) ||
3466
        (argc == 4 && (argv[2][0] == '\0' ||
3467
                       strncmp(argv[2], "set", strlen(argv[2]))))) {
3468
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3469
                       " curselection ?set <value>?\"", (char *)NULL);
3470
      result = TCL_ERROR;
3471
      break;
3472
    }
3473
    /* make sure there is a data source to accept set */
3474
    if (argc == 4 && (tablePtr->state == STATE_DISABLED ||
3475
                      (tablePtr->dataSource == DATA_NONE)))
3476
      break;
3477
    for (entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
3478
         entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
3479
      if (argc == 2) {
3480
        Tcl_AppendElement(interp,
3481
                          Tcl_GetHashKey(tablePtr->selCells, entryPtr));
3482
      } else {
3483
        TableParseArrayIndex(&row, &col,
3484
                             Tcl_GetHashKey(tablePtr->selCells, entryPtr));
3485
        TableSetCellValue(tablePtr, row, col, argv[3]);
3486
        row -= tablePtr->rowOffset;
3487
        col -= tablePtr->colOffset;
3488
        if (row == tablePtr->activeRow && col == tablePtr->activeCol) {
3489
          TableGetActiveBuf(tablePtr);
3490
        }
3491
        TableCellCoords(tablePtr, row, col, &x, &y, &width, &height);
3492
        TableInvalidate(tablePtr, x, y, width, height, 0);
3493
      }
3494
    }
3495
    if (argc == 2) {
3496
      Tcl_SetResult(interp,
3497
                    TableCellSort(tablePtr, Tcl_GetStringResult(interp)),
3498
                    TCL_DYNAMIC);
3499
    }
3500
    break;      /* CURSELECTION */
3501
 
3502
  case CMD_DELETE:
3503
    if (argc < 4) {
3504
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3505
                        " delete option ?switches? arg ?arg?\"",
3506
                        (char *) NULL);
3507
      result = TCL_ERROR;
3508
      break;
3509
    }
3510
    sub_retval = Cmd_Parse (interp, mod_cmds, argv[2]);
3511
    switch (sub_retval) {
3512
    case 0:
3513
      result = TCL_ERROR;
3514
      break;
3515
    case MOD_ACTIVE:
3516
      if (argc > 5) {
3517
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3518
                         " delete active first ?last?\"", (char *) NULL);
3519
        result = TCL_ERROR;
3520
        break;
3521
      }
3522
      if (TableGetIcursor(tablePtr, argv[3], &posn) == TCL_ERROR) {
3523
        result = TCL_ERROR;
3524
        break;
3525
      }
3526
      if (argc == 4) {
3527
        value = posn+1;
3528
      } else if (TableGetIcursor(tablePtr, argv[4], &value) == TCL_ERROR) {
3529
        result = TCL_ERROR;
3530
        break;
3531
      }
3532
      if (value >= posn && (tablePtr->flags & HAS_ACTIVE) &&
3533
          !(tablePtr->flags & ACTIVE_DISABLED) &&
3534
          tablePtr->state == STATE_NORMAL)
3535
        TableDeleteChars(tablePtr, posn, value-posn);
3536
      break;    /* DELETE ACTIVE */
3537
    case MOD_COLS:
3538
    case MOD_ROWS:
3539
      result = TableModifyRC(tablePtr, interp, CMD_DELETE, sub_retval,
3540
                             argc, argv);
3541
      break;    /* DELETE ROWS */
3542
    }
3543
    break;      /* DELETE */
3544
 
3545
  case CMD_GET: {
3546
    int r1, c1, r2, c2;
3547
 
3548
    if (argc < 3 || argc > 4) {
3549
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3550
                       " get first ?last?\"", (char *)NULL);
3551
      result = TCL_ERROR;
3552
    } else if (TableGetIndex(tablePtr, argv[2], &row, &col) == TCL_ERROR) {
3553
      result = TCL_ERROR;
3554
    } else if (argc == 3) {
3555
      Tcl_SetResult(interp, TableGetCellValue(tablePtr, row, col), TCL_STATIC);
3556
    } else if (TableGetIndex(tablePtr, argv[3], &r2, &c2) == TCL_ERROR) {
3557
      result = TCL_ERROR;
3558
    } else {
3559
      r1 = MIN(row,r2); r2 = MAX(row,r2);
3560
      c1 = MIN(col,c2); c2 = MAX(col,c2);
3561
      for ( row = r1; row <= r2; row++ ) {
3562
        for ( col = c1; col <= c2; col++ ) {
3563
          Tcl_AppendElement(interp, TableGetCellValue(tablePtr, row, col));
3564
        }
3565
      }
3566
    }
3567
  }
3568
  break;        /* GET */
3569
 
3570
  case CMD_FLUSH: /* FIX - DEPRECATED */
3571
    if (argc > 4) {
3572
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3573
                       " flush ?first? ?last?\"", (char *) NULL);
3574
      result = TCL_ERROR;
3575
    } else {
3576
      result = TableClear(tablePtr, CLEAR_CACHE,
3577
                          (argc>2)?argv[2]:NULL, (argc>3)?argv[3]:NULL);
3578
    }
3579
    break;      /* FLUSH */
3580
 
3581
  case CMD_HEIGHT:
3582
  case CMD_WIDTH:
3583
    /* changes the width/height of certain selected columns */
3584
    if (argc != 3 && (argc & 1)) {
3585
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3586
                       (retval == CMD_WIDTH) ?
3587
                       " width ?col? ?width col width ...?\"" :
3588
                       " height ?row? ?height row height ...?\"",
3589
                       (char *) NULL);
3590
      result = TCL_ERROR;
3591
      break;
3592
    }
3593
    if (retval == CMD_WIDTH) {
3594
      hashTablePtr = tablePtr->colWidths;
3595
      offset = tablePtr->colOffset;
3596
    } else {
3597
      hashTablePtr = tablePtr->rowHeights;
3598
      offset = tablePtr->rowOffset;
3599
    }
3600
 
3601
    if (argc == 2) {
3602
      /* print out all the preset column widths or row heights */
3603
      entryPtr = Tcl_FirstHashEntry(hashTablePtr, &search);
3604
      while (entryPtr != NULL) {
3605
        posn = ((int) Tcl_GetHashKey(hashTablePtr, entryPtr)) + offset;
3606
        value = (int) Tcl_GetHashValue(entryPtr);
3607
        sprintf(buf1, "%d %d", posn, value);
3608
        Tcl_AppendElement(interp, buf1);
3609
        entryPtr = Tcl_NextHashEntry(&search);
3610
      }
3611
    } else if (argc == 3) {
3612
      /* get the width/height of a particular row/col */
3613
      if (Tcl_GetInt(interp, argv[2], &posn) != TCL_OK) {
3614
        result = TCL_ERROR;
3615
        break;
3616
      }
3617
      /* no range check is done, why bother? */
3618
      posn -= offset;
3619
      entryPtr = Tcl_FindHashEntry(hashTablePtr, (char *) posn);
3620
      if (entryPtr != NULL) {
3621
        sprintf(buf1, "%d", (int) Tcl_GetHashValue(entryPtr));
3622
        Tcl_SetResult(interp, buf1, TCL_VOLATILE);
3623
      } else {
3624
        sprintf(buf1, "%d", (retval == CMD_WIDTH) ?
3625
                tablePtr->defColWidth : tablePtr->defRowHeight);
3626
        Tcl_SetResult(interp, buf1, TCL_VOLATILE);
3627
      }
3628
    } else {
3629
      for (i=2; i<argc; i++) {
3630
        /* set new width|height here */
3631
        value = -999999;
3632
        if (Tcl_GetInt(interp, argv[i++], &posn) != TCL_OK ||
3633
            (strncmp(argv[i], "default", strlen(argv[i])) &&
3634
             Tcl_GetInt(interp, argv[i], &value) != TCL_OK)) {
3635
          result = TCL_ERROR;
3636
          break;
3637
        }
3638
        posn -= offset;
3639
        if (value == -999999) {
3640
          /* reset that field */
3641
          if ((entryPtr = Tcl_FindHashEntry(hashTablePtr, (char *) posn)))
3642
            Tcl_DeleteHashEntry(entryPtr);
3643
        } else {
3644
          entryPtr = Tcl_CreateHashEntry(hashTablePtr, (char *) posn, &dummy);
3645
          Tcl_SetHashValue(entryPtr, (ClientData) value);
3646
        }
3647
      }
3648
      TableAdjustParams(tablePtr);
3649
      /* rerequest geometry */
3650
      TableGeometryRequest(tablePtr);
3651
      /*
3652
       * Invalidate the whole window as TableAdjustParams
3653
       * will only check to see if the top left cell has moved
3654
       * FIX: should just move from lowest order visible cell to edge of window
3655
       */
3656
      TableInvalidateAll(tablePtr, 0);
3657
    }
3658
    break;      /* HEIGHT && WIDTH */
3659
 
3660
  case CMD_ICURSOR:
3661
    /* set the insertion cursor */
3662
    if (!(tablePtr->flags & HAS_ACTIVE) ||
3663
        (tablePtr->flags & ACTIVE_DISABLED) ||
3664
         tablePtr->state == STATE_DISABLED)
3665
      break;
3666
    switch (argc) {
3667
    case 2:
3668
      sprintf(buf1, "%d", tablePtr->icursor);
3669
      Tcl_SetResult(interp, buf1, TCL_VOLATILE);
3670
      break;
3671
    case 3:
3672
      if (TableGetIcursor(tablePtr, argv[2], (int *)0) != TCL_OK) {
3673
        result = TCL_ERROR;
3674
        break;
3675
      }
3676
      TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol, CELL);
3677
      break;
3678
    default:
3679
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3680
                       " icursor arg\"", (char *) NULL);
3681
      result = TCL_ERROR;
3682
      break;
3683
    }
3684
    break;      /* ICURSOR */
3685
 
3686
  case CMD_INDEX:
3687
    if (argc < 3 || argc > 4 ||
3688
        TableGetIndex(tablePtr, argv[2], &row, &col) == TCL_ERROR ||
3689
        (argc == 4 && (strcmp(argv[3], "row") && strcmp(argv[3], "col")))) {
3690
      if (!strlen(Tcl_GetStringResult(interp))) {
3691
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3692
                         " index index ?row|col?\"", (char *)NULL);
3693
      }
3694
      result = TCL_ERROR;
3695
      break;
3696
    }
3697
    if (argc == 3) {
3698
      TableMakeArrayIndex(row, col, buf1);
3699
    } else if (argv[3][0] == 'r') { /* INDEX row */
3700
      sprintf(buf1, "%d", row);
3701
    } else {    /* INDEX col */
3702
      sprintf(buf1, "%d", col);
3703
    }
3704
    Tcl_SetResult(interp, buf1, TCL_VOLATILE);
3705
    break;      /* INDEX */
3706
 
3707
  case CMD_INSERT:
3708
    /* are edits enabled */
3709
    if (argc < 4) {
3710
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3711
                       " insert option ?switches? arg ?arg?\"", (char *)NULL);
3712
      result = TCL_ERROR;
3713
      break;
3714
    }
3715
    sub_retval = Cmd_Parse(interp, mod_cmds, argv[2]);
3716
    switch (sub_retval) {
3717
    case 0:
3718
      result = TCL_ERROR;
3719
      break;
3720
    case MOD_ACTIVE:
3721
      if (argc != 5) {
3722
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3723
                         " insert active index string\"", (char *)NULL);
3724
        result = TCL_ERROR;
3725
      } else if (TableGetIcursor(tablePtr, argv[3], &posn) != TCL_OK) {
3726
        result = TCL_ERROR;
3727
      } else if ((tablePtr->flags & HAS_ACTIVE) &&
3728
                 !(tablePtr->flags & ACTIVE_DISABLED) &&
3729
                 tablePtr->state == STATE_NORMAL) {
3730
        TableInsertChars(tablePtr, posn, argv[4]);
3731
      }
3732
      break;    /* INSERT ACTIVE */
3733
    case MOD_COLS:
3734
    case MOD_ROWS:
3735
      result = TableModifyRC(tablePtr, interp, CMD_INSERT, sub_retval,
3736
                             argc, argv);
3737
      break;
3738
    }
3739
    break;      /* INSERT */
3740
 
3741
  case CMD_REREAD:
3742
    /* this rereads the selection from the array */
3743
    if (!(tablePtr->flags & HAS_ACTIVE) ||
3744
        (tablePtr->flags & ACTIVE_DISABLED) ||
3745
        tablePtr->state == STATE_DISABLED)
3746
      break;
3747
    TableGetActiveBuf(tablePtr);
3748
    TableRefresh(tablePtr, tablePtr->activeRow, tablePtr->activeCol,
3749
                 CELL|INV_FORCE);
3750
    break;      /* REREAD */
3751
 
3752
  case CMD_SCAN:
3753
    if (argc != 5) {
3754
      Tcl_AppendResult(interp, "wrong # args: should be \"",
3755
                       argv[0], " scan mark|dragto x y\"", (char *) NULL);
3756
      result = TCL_ERROR;
3757
      break;
3758
    } else if (Tcl_GetInt(interp, argv[3], &x) == TCL_ERROR ||
3759
               Tcl_GetInt(interp, argv[4], &y) == TCL_ERROR) {
3760
      result = TCL_ERROR;
3761
      break;
3762
    }
3763
    if ((argv[2][0] == 'm')
3764
        && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
3765
      TableWhatCell(tablePtr, x, y, &row, &col);
3766
      tablePtr->scanMarkRow = row-tablePtr->topRow;
3767
      tablePtr->scanMarkCol = col-tablePtr->leftCol;
3768
      tablePtr->scanMarkX = x;
3769
      tablePtr->scanMarkY = y;
3770
    } else if ((argv[2][0] == 'd')
3771
               && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
3772
      int oldTop = tablePtr->topRow, oldLeft = tablePtr->leftCol;
3773
      y += (5*(y-tablePtr->scanMarkY));
3774
      x += (5*(x-tablePtr->scanMarkX));
3775
 
3776
      TableWhatCell(tablePtr, x, y, &row, &col);
3777
 
3778
      /* maintain appropriate real index */
3779
      tablePtr->topRow  = MAX(MIN(row-tablePtr->scanMarkRow,
3780
                                  tablePtr->rows-1), tablePtr->titleRows);
3781
      tablePtr->leftCol = MAX(MIN(col-tablePtr->scanMarkCol,
3782
                                  tablePtr->cols-1), tablePtr->titleCols);
3783
 
3784
      /* Adjust the table if new top left */
3785
      if (oldTop != tablePtr->topRow || oldLeft != tablePtr->leftCol)
3786
        TableAdjustParams(tablePtr);
3787
    } else {
3788
      Tcl_AppendResult(interp, "bad scan option \"", argv[2],
3789
                       "\": must be mark or dragto", (char *) NULL);
3790
      result = TCL_ERROR;
3791
      break;
3792
    }
3793
    break;      /* SCAN */
3794
 
3795
  case CMD_SEE:
3796
    if (argc!=3 || TableGetIndex(tablePtr,argv[2],&row,&col)==TCL_ERROR) {
3797
      if (!strlen(Tcl_GetStringResult(interp))) {
3798
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3799
                         " see index\"", (char *)NULL);
3800
      }
3801
      result = TCL_ERROR;
3802
      break;
3803
    }
3804
    /* Adjust from user to master coords */
3805
    row -= tablePtr->rowOffset;
3806
    col -= tablePtr->colOffset;
3807
    if (!TableCellVCoords(tablePtr, row, col, &x, &x, &x, &x, 1)) {
3808
      tablePtr->topRow  = row-1;
3809
      tablePtr->leftCol = col-1;
3810
      TableAdjustParams(tablePtr);
3811
    }
3812
    break;      /* SEE */
3813
 
3814
  case CMD_SELECTION:
3815
    if (argc<3) {
3816
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3817
                       " selection option args\"", (char *)NULL);
3818
      result=TCL_ERROR;
3819
      break;
3820
    }
3821
    retval = Cmd_Parse(interp, sel_cmds, argv[2]);
3822
    switch(retval) {
3823
    case 0:              /* failed to parse the argument, error */
3824
      return TCL_ERROR;
3825
    case SEL_ANCHOR:
3826
      if (argc != 4 || TableGetIndex(tablePtr,argv[3],&row,&col) != TCL_OK) {
3827
        if (!strlen(Tcl_GetStringResult(interp)))
3828
          Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3829
                           " selection anchor index\"", (char *)NULL);
3830
        result=TCL_ERROR;
3831
        break;
3832
      }
3833
      tablePtr->flags |= HAS_ANCHOR;
3834
      /* maintain appropriate real index */
3835
      if (tablePtr->selectTitles) {
3836
        tablePtr->anchorRow = MIN(MAX(0,row-tablePtr->rowOffset),
3837
                                  tablePtr->rows-1);
3838
        tablePtr->anchorCol = MIN(MAX(0,col-tablePtr->colOffset),
3839
                                  tablePtr->cols-1);
3840
      } else {
3841
        tablePtr->anchorRow = MIN(MAX(tablePtr->titleRows,
3842
                                      row-tablePtr->rowOffset),
3843
                                  tablePtr->rows-1);
3844
        tablePtr->anchorCol = MIN(MAX(tablePtr->titleCols,
3845
                                      col-tablePtr->colOffset),
3846
                                  tablePtr->cols-1);
3847
      }
3848
      break;
3849
    case SEL_CLEAR:
3850
      if ( argc != 4 && argc != 5 ) {
3851
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3852
                         " selection clear all|<first> ?<last>?\"",
3853
                         (char *)NULL);
3854
        result=TCL_ERROR;
3855
        break;
3856
      }
3857
      if (strcmp(argv[3],"all") == 0) {
3858
        for(entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
3859
            entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
3860
          TableParseArrayIndex(&row, &col,
3861
                               Tcl_GetHashKey(tablePtr->selCells,entryPtr));
3862
          Tcl_DeleteHashEntry(entryPtr);
3863
          TableCellCoords(tablePtr, row-tablePtr->rowOffset,
3864
                          col-tablePtr->colOffset, &x, &y, &width, &height);
3865
          TableInvalidate(tablePtr, x, y, width, height, 0);
3866
        }
3867
      } else {
3868
        int clo=0,chi=0,r1,c1,r2,c2;
3869
        if (TableGetIndex(tablePtr,argv[3],&row,&col) == TCL_ERROR ||
3870
            (argc==5 && TableGetIndex(tablePtr,argv[4],&r2,&c2)==TCL_ERROR)) {
3871
          result = TCL_ERROR;
3872
          break;
3873
        }
3874
        key = 0;
3875
        if (argc == 4) {
3876
          r1 = r2 = row;
3877
          c1 = c2 = col;
3878
        } else {
3879
          r1 = MIN(row,r2); r2 = MAX(row,r2);
3880
          c1 = MIN(col,c2); c2 = MAX(col,c2);
3881
        }
3882
        switch (tablePtr->selectType) {
3883
        case SEL_BOTH:
3884
          clo = c1; chi = c2;
3885
          c1 = tablePtr->colOffset;
3886
          c2 = tablePtr->cols-1+c1;
3887
          key = 1;
3888
          goto CLEAR_CELLS;
3889
        CLEAR_BOTH:
3890
          key = 0;
3891
          c1 = clo; c2 = chi;
3892
        case SEL_COL:
3893
          r1 = tablePtr->rowOffset;
3894
          r2 = tablePtr->rows-1+r1;
3895
          break;
3896
        case SEL_ROW:
3897
          c1 = tablePtr->colOffset;
3898
          c2 = tablePtr->cols-1+c1;
3899
          break;
3900
        }
3901
        /* row/col are in user index coords */
3902
      CLEAR_CELLS:
3903
        for ( row = r1; row <= r2; row++ ) {
3904
          for ( col = c1; col <= c2; col++ ) {
3905
            TableMakeArrayIndex(row, col, buf1);
3906
            if ((entryPtr=Tcl_FindHashEntry(tablePtr->selCells, buf1))!=NULL) {
3907
              Tcl_DeleteHashEntry(entryPtr);
3908
              TableCellCoords(tablePtr, row-tablePtr->rowOffset,
3909
                              col-tablePtr->colOffset,&x,&y,&width,&height);
3910
              TableInvalidate(tablePtr, x, y, width, height, 0);
3911
            }
3912
          }
3913
        }
3914
        if (key) goto CLEAR_BOTH;
3915
      }
3916
      break;    /* SELECTION CLEAR */
3917
    case SEL_INCLUDES:
3918
      if (argc != 4) {
3919
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3920
                         " selection includes index\"", (char *)NULL);
3921
        result = TCL_ERROR;
3922
      } else if (TableGetIndex(tablePtr, argv[3], &row, &col) == TCL_ERROR) {
3923
        result = TCL_ERROR;
3924
      } else {
3925
        TableMakeArrayIndex(row, col, buf1);
3926
        if (Tcl_FindHashEntry(tablePtr->selCells, buf1)) {
3927
          Tcl_SetResult(interp, "1", TCL_STATIC);
3928
        } else {
3929
          Tcl_SetResult(interp, "0", TCL_STATIC);
3930
        }
3931
      }
3932
      break;    /* SELECTION INCLUDES */
3933
    case SEL_SET: {
3934
      int clo=0, chi=0, r1, c1, r2, c2, titleRows, titleCols;
3935
      if (argc < 4 || argc > 5) {
3936
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
3937
                         " selection set first ?last?\"", (char *)NULL);
3938
        result = TCL_ERROR;
3939
        break;
3940
      }
3941
      if (TableGetIndex(tablePtr,argv[3],&row,&col) == TCL_ERROR ||
3942
          (argc==5 && TableGetIndex(tablePtr,argv[4],&r2,&c2)==TCL_ERROR)) {
3943
        result = TCL_ERROR;
3944
        break;
3945
      }
3946
      key = 0;
3947
      if (tablePtr->selectTitles) {
3948
        titleRows = 0;
3949
        titleCols = 0;
3950
      } else {
3951
        titleRows = tablePtr->titleRows;
3952
        titleCols = tablePtr->titleCols;
3953
      }
3954
      /* maintain appropriate user index */
3955
      row = MIN(MAX(titleRows+tablePtr->rowOffset, row),
3956
                tablePtr->rows-1+tablePtr->rowOffset);
3957
      col = MIN(MAX(titleCols+tablePtr->colOffset, col),
3958
                tablePtr->cols-1+tablePtr->colOffset);
3959
      if (argc == 4) {
3960
        r1 = r2 = row;
3961
        c1 = c2 = col;
3962
      } else {
3963
        r2 = MIN(MAX(titleRows+tablePtr->rowOffset, r2),
3964
                 tablePtr->rows-1+tablePtr->rowOffset);
3965
        c2 = MIN(MAX(titleCols+tablePtr->colOffset, c2),
3966
                 tablePtr->cols-1+tablePtr->colOffset);
3967
        r1 = MIN(row,r2); r2 = MAX(row,r2);
3968
        c1 = MIN(col,c2); c2 = MAX(col,c2);
3969
      }
3970
      switch (tablePtr->selectType) {
3971
      case SEL_BOTH:
3972
        clo = c1; chi = c2;
3973
        c1 = titleCols+tablePtr->colOffset;
3974
        c2 = tablePtr->cols-1+tablePtr->colOffset;
3975
        key = 1;
3976
        goto SET_CELLS;
3977
      SET_BOTH:
3978
        key = 0;
3979
        c1 = clo; c2 = chi;
3980
      case SEL_COL:
3981
        r1 = titleRows+tablePtr->rowOffset;
3982
        r2 = tablePtr->rows-1+tablePtr->rowOffset;
3983
        break;
3984
      case SEL_ROW:
3985
        c1 = titleCols+tablePtr->colOffset;
3986
        c2 = tablePtr->cols-1+tablePtr->colOffset;
3987
        break;
3988
      }
3989
    SET_CELLS:
3990
      entryPtr = Tcl_FirstHashEntry(tablePtr->selCells, &search);
3991
      for ( row = r1; row <= r2; row++ ) {
3992
        for ( col = c1; col <= c2; col++ ) {
3993
          TableMakeArrayIndex(row, col, buf1);
3994
          if (Tcl_FindHashEntry(tablePtr->selCells, buf1) == NULL) {
3995
            Tcl_CreateHashEntry(tablePtr->selCells, buf1, &dummy);
3996
            TableCellCoords(tablePtr, row-tablePtr->rowOffset,
3997
                            col-tablePtr->colOffset, &x, &y, &width, &height);
3998
            TableInvalidate(tablePtr, x, y, width, height, 0);
3999
          }
4000
        }
4001
      }
4002
      if (key) goto SET_BOTH;
4003
 
4004
      /* Adjust the table for top left, selection on screen etc */
4005
      TableAdjustParams(tablePtr);
4006
 
4007
      /* If the table was previously empty and we want to export the
4008
       * selection, we should grab it now */
4009
      if (entryPtr==NULL && tablePtr->exportSelection) {
4010
        Tk_OwnSelection(tablePtr->tkwin, XA_PRIMARY, TableLostSelection,
4011
                        (ClientData) tablePtr);
4012
      }
4013
    }
4014
    break;      /* SELECTION SET */
4015
    }
4016
    break;      /* SELECTION */
4017
 
4018
  case CMD_SET:
4019
    /* sets any number of tags/indices to a given value */
4020
    if (argc < 3 || (argc > 3 && (argc & 1))) {
4021
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
4022
                       " set index ?value? ?index value ...?\"",
4023
                       (char *) NULL);
4024
      result = TCL_ERROR;
4025
      break;
4026
    }
4027
    /* make sure there is a data source to accept set */
4028
    if (tablePtr->dataSource == DATA_NONE)
4029
      break;
4030
    if (argc == 3) {
4031
      if (TableGetIndex(tablePtr, argv[2], &row, &col) != TCL_OK) {
4032
        result = TCL_ERROR;
4033
        break;
4034
      }
4035
      Tcl_SetResult(interp, TableGetCellValue(tablePtr, row, col),
4036
                    TCL_STATIC);
4037
    } else if (tablePtr->state == STATE_NORMAL) {
4038
      for (i=2; i<argc; i++) {
4039
        if (TableGetIndex(tablePtr, argv[i], &row, &col) != TCL_OK) {
4040
          result = TCL_ERROR;
4041
          break;
4042
        }
4043
        if (TableSetCellValue(tablePtr, row, col, argv[++i]) == TCL_ERROR) {
4044
          result = TCL_ERROR;
4045
          break;
4046
        }
4047
        row -= tablePtr->rowOffset;
4048
        col -= tablePtr->colOffset;
4049
        if (row == tablePtr->activeRow && col == tablePtr->activeCol) {
4050
          TableGetActiveBuf(tablePtr);
4051
        }
4052
        TableCellCoords(tablePtr, row, col, &x, &y, &width, &height);
4053
        TableInvalidate(tablePtr, x, y, width, height, 0);
4054
      }
4055
    }
4056
    break;
4057
 
4058
  case CMD_TAG:
4059
    /* a veritable plethora of tag commands */
4060
    /* do we have another argument */
4061
    if (argc < 3) {
4062
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
4063
                       " tag option ?arg arg ...?\"", (char *) NULL);
4064
      result = TCL_ERROR;
4065
      break;
4066
    }
4067
    /* all the rest is now done in a separate function */
4068
    result = TableTagCmd(tablePtr, interp, argc, argv);
4069
    break;      /* TAG */
4070
 
4071
  case CMD_VALIDATE:
4072
    if (argc != 3) {
4073
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
4074
                       " validate index\"", (char *) NULL);
4075
      result = TCL_ERROR;
4076
    } else if (TableGetIndex(tablePtr, argv[2], &row, &col) == TCL_ERROR) {
4077
      result = TCL_ERROR;
4078
    } else {
4079
      value = tablePtr->validate;
4080
      tablePtr->validate = 1;
4081
      key = TableValidateChange(tablePtr, row, col, (char *) NULL,
4082
                                (char *) NULL, -1);
4083
      tablePtr->validate = value;
4084
      sprintf(buf1, "%d", (key == TCL_OK) ? 1 : 0);
4085
      Tcl_SetResult(interp, buf1, TCL_VOLATILE);
4086
    }
4087
    break;
4088
 
4089
  case CMD_VERSION:
4090
    if (argc != 2) {
4091
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
4092
                       " version\"", (char *) NULL);
4093
      result = TCL_ERROR;
4094
    } else {
4095
      Tcl_SetResult(interp, TBL_VERSION, TCL_VOLATILE);
4096
    }
4097
    break;
4098
 
4099
  case CMD_WINDOW:
4100
    /* a veritable plethora of window commands */
4101
    /* do we have another argument */
4102
    if (argc < 3) {
4103
      Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
4104
                       " window option ?arg arg ...?\"", (char *) NULL);
4105
      result = TCL_ERROR;
4106
      break;
4107
    }
4108
    /* all the rest is now done in a separate function */
4109
    result = TableWindowCmd(tablePtr, interp, argc, argv);
4110
    break;
4111
 
4112
  case CMD_XVIEW:
4113
  case CMD_YVIEW:
4114
    if (argc == 2) {
4115
      int diff;
4116
      double first, last;
4117
      TableGetLastCell(tablePtr, &row, &col);
4118
      TableCellVCoords(tablePtr, row, col, &x, &y, &width, &height, 0);
4119
      if (retval == CMD_YVIEW) {
4120
        if (row < tablePtr->titleRows) {
4121
          first = 0;
4122
          last  = 1;
4123
        } else {
4124
          diff = tablePtr->rowStarts[tablePtr->titleRows];
4125
          last = (double) (tablePtr->rowStarts[tablePtr->rows]-diff);
4126
          first = (tablePtr->rowStarts[tablePtr->topRow]-diff) / last;
4127
          last  = (height+tablePtr->rowStarts[row]-diff) / last;
4128
        }
4129
      } else {
4130
        if (col < tablePtr->titleCols) {
4131
          first = 0;
4132
          last  = 1;
4133
        } else {
4134
          diff = tablePtr->colStarts[tablePtr->titleCols];
4135
          last = (double) (tablePtr->colStarts[tablePtr->cols]-diff);
4136
          first = (tablePtr->colStarts[tablePtr->leftCol]-diff) / last;
4137
          last  = (width+tablePtr->colStarts[col]-diff) / last;
4138
        }
4139
      }
4140
      sprintf(buf1, "%g %g", first, last);
4141
      Tcl_SetResult(interp, buf1, TCL_VOLATILE);
4142
    } else {
4143
      /* cache old topleft to see if it changes */
4144
      int oldTop = tablePtr->topRow, oldLeft = tablePtr->leftCol;
4145
      if (argc == 3) {
4146
        if (Tcl_GetInt(interp, argv[2], &value) != TCL_OK) {
4147
          result = TCL_ERROR;
4148
          break;
4149
        }
4150
        if (retval == CMD_YVIEW) {
4151
          tablePtr->topRow  = value + tablePtr->titleRows;
4152
        } else {
4153
          tablePtr->leftCol = value + tablePtr->titleCols;
4154
        }
4155
      } else {
4156
        double frac;
4157
        sub_retval = Tk_GetScrollInfo(interp, argc, argv, &frac, &value);
4158
        switch (sub_retval) {
4159
        case TK_SCROLL_ERROR:
4160
          result = TCL_ERROR;
4161
          break;
4162
        case TK_SCROLL_MOVETO:
4163
          if (frac < 0) frac = 0;
4164
          if (retval == CMD_YVIEW) {
4165
            tablePtr->topRow = (int)(frac*tablePtr->rows)+tablePtr->titleRows;
4166
          } else {
4167
            tablePtr->leftCol = (int)(frac*tablePtr->cols)+tablePtr->titleCols;
4168
          }
4169
          break;
4170
        case TK_SCROLL_PAGES:
4171
          TableGetLastCell(tablePtr, &row, &col);
4172
          if (retval == CMD_YVIEW) {
4173
            tablePtr->topRow  += value * (row-tablePtr->topRow+1);
4174
          } else {
4175
            tablePtr->leftCol += value * (col-tablePtr->leftCol+1);
4176
          }
4177
          break;
4178
        case TK_SCROLL_UNITS:
4179
          if (retval == CMD_YVIEW) {
4180
            tablePtr->topRow  += value;
4181
          } else {
4182
            tablePtr->leftCol += value;
4183
          }
4184
          break;
4185
        }
4186
      }
4187
      /* maintain appropriate real index */
4188
      tablePtr->topRow  = MAX(tablePtr->titleRows,
4189
                              MIN(tablePtr->topRow, tablePtr->rows-1));
4190
      tablePtr->leftCol = MAX(tablePtr->titleCols,
4191
                              MIN(tablePtr->leftCol, tablePtr->cols-1));
4192
      /* Do the table adjustment if topRow || leftCol changed */
4193
      if (oldTop != tablePtr->topRow || oldLeft != tablePtr->leftCol)
4194
        TableAdjustParams(tablePtr);
4195
    }
4196
    break; /* XVIEW/YVIEW */
4197
  }
4198
  Tcl_Release(clientData);
4199
  return result;
4200
}
4201
 
4202
/*
4203
 *----------------------------------------------------------------------
4204
 *
4205
 * TableDestroy --
4206
 *      This procedure is invoked by Tcl_EventuallyFree
4207
 *      to clean up the internal structure of a table at a safe time
4208
 *      (when no-one is using it anymore).
4209
 *
4210
 * Results:
4211
 *      None.
4212
 *
4213
 * Side effects:
4214
 *      Everything associated with the table is freed up (hopefully).
4215
 *
4216
 *----------------------------------------------------------------------
4217
 */
4218
static void
4219
TableDestroy(ClientData clientdata)
4220
{
4221
  register Table *tablePtr = (Table *) clientdata;
4222
  Tcl_HashEntry *entryPtr;
4223
  Tcl_HashSearch search;
4224
 
4225
  /* These may be repetitive from DestroyNotify, but it doesn't hurt */
4226
  /* cancel any pending update or timer */
4227
  if (tablePtr->flags & REDRAW_PENDING) {
4228
    Tcl_CancelIdleCall(TableDisplay, (ClientData) tablePtr);
4229
    tablePtr->flags &= ~REDRAW_PENDING;
4230
  }
4231
  Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
4232
  Tcl_DeleteTimerHandler(tablePtr->flashTimer);
4233
 
4234
  /* delete the variable trace */
4235
  if (tablePtr->arrayVar != NULL) {
4236
    Tcl_UntraceVar(tablePtr->interp, tablePtr->arrayVar,
4237
                   TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY,
4238
                   (Tcl_VarTraceProc *)TableVarProc, (ClientData) tablePtr);
4239
  }
4240
 
4241
  /* delete cached activeLayout */
4242
  if (tablePtr->activeLayout != NULL) {
4243
    Tk_FreeTextLayout(tablePtr->activeLayout);
4244
    tablePtr->activeLayout = NULL;
4245
  }
4246
  /* free the arrays with row/column pixel sizes */
4247
  if (tablePtr->colPixels) ckfree((char *) tablePtr->colPixels);
4248
  if (tablePtr->rowPixels) ckfree((char *) tablePtr->rowPixels);
4249
  if (tablePtr->colStarts) ckfree((char *) tablePtr->colStarts);
4250
  if (tablePtr->rowStarts) ckfree((char *) tablePtr->rowStarts);
4251
 
4252
  /* free the selection buffer */
4253
  if (tablePtr->activeBuf != NULL) ckfree(tablePtr->activeBuf);
4254
 
4255
  /* delete the cache, row, column and cell style hash tables */
4256
  Tcl_DeleteHashTable(tablePtr->cache);
4257
  ckfree((char *) (tablePtr->cache));
4258
  Tcl_DeleteHashTable(tablePtr->rowStyles);
4259
  ckfree((char *) (tablePtr->rowStyles));
4260
  Tcl_DeleteHashTable(tablePtr->colStyles);
4261
  ckfree((char *) (tablePtr->colStyles));
4262
  Tcl_DeleteHashTable(tablePtr->cellStyles);
4263
  ckfree((char *) (tablePtr->cellStyles));
4264
  Tcl_DeleteHashTable(tablePtr->flashCells);
4265
  ckfree((char *) (tablePtr->flashCells));
4266
  Tcl_DeleteHashTable(tablePtr->selCells);
4267
  ckfree((char *) (tablePtr->selCells));
4268
  Tcl_DeleteHashTable(tablePtr->colWidths);
4269
  ckfree((char *) (tablePtr->colWidths));
4270
  Tcl_DeleteHashTable(tablePtr->rowHeights);
4271
  ckfree((char *) (tablePtr->rowHeights));
4272
 
4273
  /* Now free up all the tag information */
4274
  for (entryPtr = Tcl_FirstHashEntry(tablePtr->tagTable, &search);
4275
       entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
4276
    TableCleanupTag(tablePtr, (TableTag *) Tcl_GetHashValue(entryPtr));
4277
    ckfree((char *) Tcl_GetHashValue(entryPtr));
4278
  }
4279
  /* free up the stuff in the default tag */
4280
  TableCleanupTag(tablePtr, &(tablePtr->defaultTag));
4281
  /* And delete the actual hash table */
4282
  Tcl_DeleteHashTable(tablePtr->tagTable);
4283
  ckfree((char *) (tablePtr->tagTable));
4284
 
4285
  /* Now free up all the embedded window info */
4286
  for (entryPtr = Tcl_FirstHashEntry(tablePtr->winTable, &search);
4287
       entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
4288
    EmbWinDelete(tablePtr, (TableEmbWindow *) Tcl_GetHashValue(entryPtr));
4289
  }
4290
  /* And delete the actual hash table */
4291
  Tcl_DeleteHashTable(tablePtr->winTable);
4292
  ckfree((char *) (tablePtr->winTable));
4293
 
4294
  /* free the configuration options in the widget */
4295
  Tk_FreeOptions(TableConfig, (char *) tablePtr, tablePtr->display, 0);
4296
 
4297
  /* and free the widget memory at last! */
4298
  ckfree((char *) (tablePtr));
4299
}
4300
 
4301
/*
4302
 *--------------------------------------------------------------
4303
 *
4304
 * TableEventProc --
4305
 *      This procedure is invoked by the Tk dispatcher for various
4306
 *      events on tables.
4307
 *
4308
 * Results:
4309
 *      None.
4310
 *
4311
 * Side effects:
4312
 *      When the window gets deleted, internal structures get
4313
 *      cleaned up.  When it gets exposed, it is redisplayed.
4314
 *
4315
 *--------------------------------------------------------------
4316
 */
4317
static void
4318
TableEventProc(clientData, eventPtr)
4319
     ClientData clientData;     /* Information about window. */
4320
     XEvent *eventPtr;          /* Information about event. */
4321
{
4322
  Table *tablePtr = (Table *) clientData;
4323
  int row, col;
4324
 
4325
  switch (eventPtr->type) {
4326
 
4327
  case MotionNotify:
4328
    if (!(tablePtr->resize & SEL_NONE) && (tablePtr->bdcursor != None) &&
4329
        TableAtBorder(tablePtr, eventPtr->xmotion.x, eventPtr->xmotion.y,
4330
                      &row, &col) &&
4331
        ((row>=0 && (tablePtr->resize & SEL_ROW)) ||
4332
         (col>=0 && (tablePtr->resize & SEL_COL)))) {
4333
      /* The bordercursor is defined and we meet the criteria for being
4334
       * over a border.  Set the cursor to border if not already so */
4335
      if (!(tablePtr->flags & OVER_BORDER)) {
4336
        tablePtr->flags |= OVER_BORDER;
4337
        Tk_DefineCursor(tablePtr->tkwin, tablePtr->bdcursor);
4338
      }
4339
    } else if (tablePtr->flags & OVER_BORDER) {
4340
      tablePtr->flags &= ~OVER_BORDER;
4341
      if (tablePtr->cursor != None) {
4342
        Tk_DefineCursor(tablePtr->tkwin, tablePtr->cursor);
4343
      } else {
4344
        Tk_UndefineCursor(tablePtr->tkwin);
4345
      }
4346
    }
4347
    break;
4348
 
4349
  case Expose:
4350
    TableInvalidate(tablePtr, eventPtr->xexpose.x, eventPtr->xexpose.y,
4351
                    eventPtr->xexpose.width, eventPtr->xexpose.height,
4352
                    INV_HIGHLIGHT);
4353
    break;
4354
 
4355
  case DestroyNotify:
4356
    /* remove the command from the interpreter */
4357
    if (tablePtr->tkwin != NULL) {
4358
      tablePtr->tkwin = NULL;
4359
      Tcl_DeleteCommandFromToken(tablePtr->interp, tablePtr->widgetCmd);
4360
    }
4361
 
4362
    /* cancel any pending update or timer */
4363
    if (tablePtr->flags & REDRAW_PENDING) {
4364
      Tcl_CancelIdleCall(TableDisplay, (ClientData) tablePtr);
4365
      tablePtr->flags &= ~REDRAW_PENDING;
4366
    }
4367
    Tcl_DeleteTimerHandler(tablePtr->cursorTimer);
4368
    Tcl_DeleteTimerHandler(tablePtr->flashTimer);
4369
 
4370
    Tcl_EventuallyFree((ClientData) tablePtr, (Tcl_FreeProc *) TableDestroy);
4371
    break;
4372
 
4373
  case MapNotify: /* redraw table when remapped if it changed */
4374
    if (tablePtr->flags & REDRAW_ON_MAP) {
4375
      tablePtr->flags &= ~REDRAW_ON_MAP;
4376
      Tcl_Preserve((ClientData) tablePtr);
4377
      TableAdjustParams(tablePtr);
4378
      TableInvalidateAll(tablePtr, INV_FORCE|INV_HIGHLIGHT);
4379
      Tcl_Release((ClientData) tablePtr);
4380
    }
4381
    break;
4382
 
4383
  case ConfigureNotify:
4384
    Tcl_Preserve((ClientData) tablePtr);
4385
    TableAdjustParams(tablePtr);
4386
    TableInvalidateAll(tablePtr, INV_FORCE|INV_HIGHLIGHT);
4387
    Tcl_Release((ClientData) tablePtr);
4388
    break;
4389
 
4390
  case FocusIn:
4391
  case FocusOut:
4392
    if (eventPtr->xfocus.detail != NotifyInferior) {
4393
      tablePtr->flags |= REDRAW_BORDER;
4394
      if (eventPtr->type == FocusOut) {
4395
        tablePtr->flags &= ~HAS_FOCUS;
4396
      } else {
4397
        tablePtr->flags |= HAS_FOCUS;
4398
      }
4399
      TableRedrawHighlight(tablePtr);
4400
      /* cancel the timer */
4401
      TableConfigCursor(tablePtr);
4402
    }
4403
    break;
4404
  }
4405
}
4406
 
4407
/*
4408
 *----------------------------------------------------------------------
4409
 *
4410
 * TableConfigure --
4411
 *      This procedure is called to process an argv/argc list, plus
4412
 *      the Tk option database, in order to configure (or reconfigure)
4413
 *      a table widget.
4414
 *
4415
 * Results:
4416
 *      The return value is a standard Tcl result.  If TCL_ERROR is
4417
 *      returned, then interp result contains an error message.
4418
 *
4419
 * Side effects:
4420
 *      Configuration information, such as colors, border width, etc.
4421
 *      get set for tablePtr; old resources get freed, if there were any.
4422
 *      Certain values might be constrained.
4423
 *
4424
 *----------------------------------------------------------------------
4425
 */
4426
static int
4427
TableConfigure(interp, tablePtr, argc, argv, flags, forceUpdate)
4428
    Tcl_Interp *interp;         /* Used for error reporting. */
4429
    register Table *tablePtr;   /* Information about widget;  may or may
4430
                                 * not already have values for some fields. */
4431
    int argc;                   /* Number of valid entries in argv. */
4432
    char **argv;                /* Arguments. */
4433
    int flags;                  /* Flags to pass to Tk_ConfigureWidget. */
4434
    int forceUpdate;            /* Whether to force an update - required
4435
                                 * for initial configuration */
4436
{
4437
  Tcl_HashSearch search;
4438
  int oldUse, oldCaching, oldExport, result = TCL_OK;
4439
  char *oldVar;
4440
  Tcl_DString error;
4441
  Tk_FontMetrics fm;
4442
 
4443
  oldExport     = tablePtr->exportSelection;
4444
  oldCaching    = tablePtr->caching;
4445
  oldUse        = tablePtr->useCmd;
4446
  oldVar        = tablePtr->arrayVar;
4447
 
4448
  /* Do the configuration */
4449
  if (Tk_ConfigureWidget(interp, tablePtr->tkwin, TableConfig, argc, argv,
4450
                         (char *) tablePtr, flags) != TCL_OK)
4451
    return TCL_ERROR;
4452
 
4453
  Tcl_DStringInit(&error);
4454
 
4455
  /* Any time we configure, reevaluate what our data source is */
4456
  tablePtr->dataSource = DATA_NONE;
4457
  if (tablePtr->caching) {
4458
    tablePtr->dataSource |= DATA_CACHE;
4459
  }
4460
  if (tablePtr->command && tablePtr->useCmd) {
4461
    tablePtr->dataSource |= DATA_COMMAND;
4462
  } else if (tablePtr->arrayVar) {
4463
    tablePtr->dataSource |= DATA_ARRAY;
4464
  }
4465
 
4466
  /* Check to see if the array variable was changed */
4467
  if (strcmp((tablePtr->arrayVar?tablePtr->arrayVar:""),(oldVar?oldVar:""))) {
4468
    /* only do the following if arrayVar is our data source */
4469
    if (tablePtr->dataSource & DATA_ARRAY) {
4470
      /* ensure that the cache will flush later so it gets the new values */
4471
      oldCaching = !(tablePtr->caching);
4472
    }
4473
    /* remove the trace on the old array variable if there was one */
4474
    if (oldVar != NULL)
4475
      Tcl_UntraceVar(interp, oldVar,
4476
                     TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY,
4477
                     (Tcl_VarTraceProc *)TableVarProc, (ClientData)tablePtr);
4478
    /* Check whether variable is an array and trace it if it is */
4479
    if (tablePtr->arrayVar != NULL) {
4480
      /* does the variable exist as an array? */
4481
      if (Tcl_SetVar2(interp, tablePtr->arrayVar, TEST_KEY, "",
4482
                      TCL_GLOBAL_ONLY) == NULL) {
4483
        Tcl_DStringAppend(&error, "invalid variable value \"", -1);
4484
        Tcl_DStringAppend(&error, tablePtr->arrayVar, -1);
4485
        Tcl_DStringAppend(&error, "\": could not be made an array", -1);
4486
        ckfree(tablePtr->arrayVar);
4487
        tablePtr->arrayVar = NULL;
4488
        tablePtr->dataSource &= ~DATA_ARRAY;
4489
        result = TCL_ERROR;
4490
      } else {
4491
        Tcl_UnsetVar2(interp, tablePtr->arrayVar, TEST_KEY, TCL_GLOBAL_ONLY);
4492
        /* remove the effect of the evaluation */
4493
        /* set a trace on the variable */
4494
        Tcl_TraceVar(interp, tablePtr->arrayVar,
4495
                     TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY,
4496
                     (Tcl_VarTraceProc *)TableVarProc, (ClientData) tablePtr);
4497
 
4498
        /* only do the following if arrayVar is our data source */
4499
        if (tablePtr->dataSource & DATA_ARRAY) {
4500
          /* get the current value of the selection */
4501
          TableGetActiveBuf(tablePtr);
4502
        }
4503
      }
4504
    }
4505
  }
4506
  if ((tablePtr->command && tablePtr->useCmd && !oldUse) ||
4507
      (tablePtr->arrayVar && !(tablePtr->useCmd) && oldUse)) {
4508
    /* our effective data source changed, so flush and
4509
     * retrieve new active buffer */
4510
    TableFlushCache(tablePtr);
4511
    TableGetActiveBuf(tablePtr);
4512
    forceUpdate = 1;
4513
  } else if (oldCaching != tablePtr->caching) {
4514
    /* caching changed, so just clear the cache for safety */
4515
    TableFlushCache(tablePtr);
4516
    forceUpdate = 1;
4517
  }
4518
 
4519
  /* set up the default column width and row height */
4520
  Tk_GetFontMetrics(tablePtr->defaultTag.tkfont, &fm);
4521
  tablePtr->charWidth = Tk_TextWidth(tablePtr->defaultTag.tkfont, "0", 1);
4522
  tablePtr->charHeight = fm.linespace + 2;
4523
 
4524
  if (tablePtr->insertWidth <= 0) {
4525
    tablePtr->insertWidth = 2;
4526
  }
4527
  if (tablePtr->insertBorderWidth > tablePtr->insertWidth/2) {
4528
    tablePtr->insertBorderWidth = tablePtr->insertWidth/2;
4529
  }
4530
  tablePtr->highlightWidth = MAX(0,tablePtr->highlightWidth);
4531
  /* the border must be >= 0 */
4532
  tablePtr->borderWidth = MAX(0,tablePtr->borderWidth);
4533
  /* when drawing fast or single, the border must be <= 1 */
4534
  if (tablePtr->drawMode & (DRAW_MODE_SINGLE|DRAW_MODE_FAST)) {
4535
    tablePtr->borderWidth = MIN(1,tablePtr->borderWidth);
4536
  }
4537
 
4538
  /* Ensure that certain values are within proper constraints */
4539
  tablePtr->rows = MAX(1,tablePtr->rows);
4540
  tablePtr->cols = MAX(1,tablePtr->cols);
4541
  tablePtr->titleRows = MIN(MAX(0,tablePtr->titleRows),tablePtr->rows);
4542
  tablePtr->titleCols = MIN(MAX(0,tablePtr->titleCols),tablePtr->cols);
4543
  tablePtr->padX = MAX(0,tablePtr->padX);
4544
  tablePtr->padY = MAX(0,tablePtr->padY);
4545
  tablePtr->maxReqCols = MAX(0,tablePtr->maxReqCols);
4546
  tablePtr->maxReqRows = MAX(0,tablePtr->maxReqRows);
4547
 
4548
  /*
4549
   * Claim the selection if we've suddenly started exporting it and
4550
   * there is a selection to export.
4551
   */
4552
  if (tablePtr->exportSelection && !oldExport &&
4553
      (Tcl_FirstHashEntry(tablePtr->selCells, &search) != NULL)) {
4554
    Tk_OwnSelection(tablePtr->tkwin, XA_PRIMARY, TableLostSelection,
4555
                    (ClientData) tablePtr);
4556
  }
4557
 
4558
  /* only do the full reconfigure if absolutely necessary */
4559
  if (!forceUpdate) {
4560
    int i;
4561
    for (i = 0; i < argc-1; i += 2) {
4562
      if (Cmd_GetValue(update_config, argv[i])) {
4563
        forceUpdate = 1;
4564
        break;
4565
      }
4566
    }
4567
  }
4568
  if (forceUpdate) {
4569
    /*
4570
     * Calculate the row and column starts
4571
     * Adjust the top left corner of the internal display
4572
     */
4573
    TableAdjustParams(tablePtr);
4574
    /* reset the cursor */
4575
    TableConfigCursor(tablePtr);
4576
    /* set up the background colour in the window */
4577
    Tk_SetBackgroundFromBorder(tablePtr->tkwin, tablePtr->defaultTag.bg);
4578
    /* set the geometry and border */
4579
    TableGeometryRequest(tablePtr);
4580
    Tk_SetInternalBorder(tablePtr->tkwin, tablePtr->highlightWidth);
4581
    /* invalidate the whole table */
4582
    TableInvalidateAll(tablePtr, INV_HIGHLIGHT);
4583
  }
4584
  /* FIX this is goofy because the result could be munged by other
4585
   * functions.  Needs to be improved */
4586
  Tcl_ResetResult(interp);
4587
  if (result == TCL_ERROR) {
4588
    Tcl_AddErrorInfo(interp, "\t(configuring table widget)");
4589
    Tcl_DStringResult(interp, &error);
4590
  }
4591
  Tcl_DStringFree(&error);
4592
  return result;
4593
}
4594
 
4595
#ifndef CLASSPATCH
4596
/*
4597
 * As long as we wait for the Function in general
4598
 *
4599
 * This parses the "-class" option for the table.
4600
 */
4601
static void
4602
Tk_ClassOption(Tk_Window tkwin, char *defaultclass, int *argcp, char ***argvp)
4603
{
4604
  char *classname = (((*argcp)<3) || (strcmp((*argvp)[2],"-class"))) ?
4605
    defaultclass : ((*argcp)-=2,(*argcp)+=2,(*argvp)[1]);
4606
  Tk_SetClass(tkwin,classname);
4607
}
4608
#endif
4609
 
4610
/*
4611
 *----------------------------------------------------------------------
4612
 *
4613
 * TableCmdDeletedProc --
4614
 *
4615
 *      This procedure is invoked when a widget command is deleted.  If
4616
 *      the widget isn't already in the process of being destroyed,
4617
 *      this command destroys it.
4618
 *
4619
 * Results:
4620
 *      None.
4621
 *
4622
 * Side effects:
4623
 *      The widget is destroyed.
4624
 *
4625
 *----------------------------------------------------------------------
4626
 */
4627
static void
4628
TableCmdDeletedProc(ClientData clientData)
4629
{
4630
  Table *tablePtr = (Table *) clientData;
4631
  Tk_Window tkwin;
4632
 
4633
  /*
4634
   * This procedure could be invoked either because the window was
4635
   * destroyed and the command was then deleted (in which case tkwin
4636
   * is NULL) or because the command was deleted, and then this procedure
4637
   * destroys the widget.
4638
   */
4639
 
4640
  /* This is needed to avoid bug where the DLL is unloaded before
4641
   * the table is properly destroyed */
4642
  Tcl_DeleteExitHandler((Tcl_ExitProc *) TableCmdDeletedProc,
4643
                        (ClientData) tablePtr);
4644
  if (tablePtr->tkwin != NULL) {
4645
    tkwin = tablePtr->tkwin;
4646
    tablePtr->tkwin = NULL;
4647
    Tk_DestroyWindow(tkwin);
4648
  }
4649
}
4650
 
4651
/*
4652
 *--------------------------------------------------------------
4653
 *
4654
 * TableCmd --
4655
 *      This procedure is invoked to process the "table" Tcl
4656
 *      command.  See the user documentation for details on what
4657
 *      it does.
4658
 *
4659
 * Results:
4660
 *      A standard Tcl result.
4661
 *
4662
 * Side effects:
4663
 *      See the user documentation.
4664
 *
4665
 *--------------------------------------------------------------
4666
 */
4667
static int
4668
TableCmd(clientData, interp, argc, argv)
4669
     ClientData clientData;     /* Main window associated with
4670
                                 * interpreter. */
4671
     Tcl_Interp *interp;        /* Current interpreter. */
4672
     int argc;                  /* Number of arguments. */
4673
     char **argv;               /* Argument strings. */
4674
{
4675
  register Table *tablePtr;
4676
  Tk_Window tkwin = (Tk_Window) clientData;
4677
  Tk_Window new;
4678
 
4679
  if (argc < 2) {
4680
    Tcl_AppendResult(interp, "wrong # args: should be \"",
4681
                     argv[0], " pathname ?options?\"", (char *) NULL);
4682
    return TCL_ERROR;
4683
  }
4684
 
4685
  new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
4686
  if (new == NULL) {
4687
    return TCL_ERROR;
4688
  }
4689
 
4690
  tablePtr                      = (Table *) ckalloc(sizeof(Table));
4691
  tablePtr->tkwin               = new;
4692
  tablePtr->display             = Tk_Display(new);
4693
  tablePtr->interp              = interp;
4694
 
4695
  tablePtr->topRow              = 0;
4696
  tablePtr->leftCol             = 0;
4697
  tablePtr->anchorRow           = -1;
4698
  tablePtr->anchorCol           = -1;
4699
  tablePtr->activeRow           = -1;
4700
  tablePtr->activeCol           = -1;
4701
  tablePtr->oldTopRow           = -1;
4702
  tablePtr->oldLeftCol          = -1;
4703
  tablePtr->oldActRow           = -1;
4704
  tablePtr->oldActCol           = -1;
4705
  tablePtr->seen[0]              = -1;
4706
  tablePtr->icursor             = 0;
4707
  tablePtr->flags               = 0;
4708
 
4709
  tablePtr->colPixels           = (int *) 0;
4710
  tablePtr->rowPixels           = (int *) 0;
4711
  tablePtr->colStarts           = (int *) 0;
4712
  tablePtr->rowStarts           = (int *) 0;
4713
  tablePtr->cursorTimer         = (Tcl_TimerToken)0;
4714
  tablePtr->flashTimer          = (Tcl_TimerToken)0;
4715
  tablePtr->dataSource          = DATA_NONE;
4716
  tablePtr->activeBuf           = ckalloc(1);
4717
  *(tablePtr->activeBuf)        = '\0';
4718
  tablePtr->activeLayout        = NULL;
4719
 
4720
  /* misc tables */
4721
  tablePtr->tagTable    = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
4722
  Tcl_InitHashTable(tablePtr->tagTable, TCL_STRING_KEYS);
4723
  tablePtr->winTable    = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
4724
  Tcl_InitHashTable(tablePtr->winTable, TCL_STRING_KEYS);
4725
 
4726
  /* internal value cache */
4727
  tablePtr->cache       = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
4728
  Tcl_InitHashTable(tablePtr->cache, TCL_STRING_KEYS);
4729
 
4730
  /* style hash tables */
4731
  tablePtr->colWidths   = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
4732
  Tcl_InitHashTable(tablePtr->colWidths, TCL_ONE_WORD_KEYS);
4733
  tablePtr->rowHeights  = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
4734
  Tcl_InitHashTable(tablePtr->rowHeights, TCL_ONE_WORD_KEYS);
4735
 
4736
  /* style hash tables */
4737
  tablePtr->rowStyles   = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
4738
  Tcl_InitHashTable(tablePtr->rowStyles, TCL_ONE_WORD_KEYS);
4739
  tablePtr->colStyles   = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
4740
  Tcl_InitHashTable(tablePtr->colStyles, TCL_ONE_WORD_KEYS);
4741
  tablePtr->cellStyles  = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
4742
  Tcl_InitHashTable(tablePtr->cellStyles, TCL_STRING_KEYS);
4743
 
4744
  /* special style hash tables */
4745
  tablePtr->flashCells  = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
4746
  Tcl_InitHashTable(tablePtr->flashCells, TCL_STRING_KEYS);
4747
  tablePtr->selCells    = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
4748
  Tcl_InitHashTable(tablePtr->selCells, TCL_STRING_KEYS);
4749
 
4750
  tablePtr->rows                = 0;
4751
  tablePtr->cols                = 0;
4752
  tablePtr->selectMode          = NULL;
4753
  tablePtr->selectTitles        = 0;
4754
  tablePtr->defRowHeight        = 0;
4755
  tablePtr->defColWidth         = 0;
4756
  tablePtr->arrayVar            = NULL;
4757
  tablePtr->borderWidth         = 0;
4758
  tablePtr->defaultTag.anchor   = TK_ANCHOR_CENTER;
4759
  tablePtr->defaultTag.bg       = NULL;
4760
  tablePtr->defaultTag.fg       = NULL;
4761
  tablePtr->defaultTag.tkfont   = NULL;
4762
  tablePtr->defaultTag.image    = NULL;
4763
  tablePtr->defaultTag.imageStr = NULL;
4764
  tablePtr->defaultTag.justify  = TK_JUSTIFY_LEFT;
4765
  tablePtr->defaultTag.multiline        = 1;
4766
  tablePtr->defaultTag.relief   = TK_RELIEF_FLAT;
4767
  tablePtr->defaultTag.showtext = 0;
4768
  tablePtr->defaultTag.state    = STATE_UNKNOWN;
4769
  tablePtr->defaultTag.wrap     = 0;
4770
  tablePtr->yScrollCmd          = NULL;
4771
  tablePtr->xScrollCmd          = NULL;
4772
  tablePtr->insertBg            = NULL;
4773
  tablePtr->cursor              = None;
4774
  tablePtr->bdcursor            = None;
4775
  tablePtr->titleRows           = 0;
4776
  tablePtr->titleCols           = 0;
4777
  tablePtr->drawMode            = DRAW_MODE_TK_COMPAT;
4778
  tablePtr->colStretch          = STRETCH_MODE_NONE;
4779
  tablePtr->rowStretch          = STRETCH_MODE_NONE;
4780
  tablePtr->maxWidth            = 0;
4781
  tablePtr->maxHeight           = 0;
4782
  tablePtr->charWidth           = 0;
4783
  tablePtr->charHeight          = 0;
4784
  tablePtr->colOffset           = 0;
4785
  tablePtr->rowOffset           = 0;
4786
  tablePtr->flashTime           = 2;
4787
  tablePtr->rowTagCmd           = NULL;
4788
  tablePtr->colTagCmd           = NULL;
4789
  tablePtr->highlightWidth      = 0;
4790
  tablePtr->highlightBgColorPtr = NULL;
4791
  tablePtr->highlightColorPtr   = NULL;
4792
  tablePtr->takeFocus           = NULL;
4793
  tablePtr->state               = STATE_NORMAL;
4794
  tablePtr->insertWidth         = 0;
4795
  tablePtr->insertBorderWidth   = 0;
4796
  tablePtr->insertOnTime        = 0;
4797
  tablePtr->insertOffTime       = 0;
4798
  tablePtr->invertSelected      = 0;
4799
  tablePtr->autoClear           = 0;
4800
  tablePtr->flashMode           = 0;
4801
  tablePtr->exportSelection     = 1;
4802
  tablePtr->rowSep              = NULL;
4803
  tablePtr->colSep              = NULL;
4804
  tablePtr->browseCmd           = NULL;
4805
  tablePtr->command             = NULL;
4806
  tablePtr->selCmd              = NULL;
4807
  tablePtr->valCmd              = NULL;
4808
  tablePtr->validate            = 0;
4809
  tablePtr->useCmd              = 1;
4810
  tablePtr->caching             = 0;
4811
  tablePtr->padX                = 0;
4812
  tablePtr->padY                = 0;
4813
  tablePtr->maxReqCols          = 0;
4814
  tablePtr->maxReqRows          = 0;
4815
  tablePtr->maxReqWidth         = 800;
4816
  tablePtr->maxReqHeight        = 600;
4817
 
4818
  /* selection handlers needed here */
4819
 
4820
  Tk_ClassOption(new, "Table", &argc, &argv);
4821
  Tk_CreateEventHandler(tablePtr->tkwin,
4822
                        PointerMotionMask|ExposureMask|StructureNotifyMask|FocusChangeMask|VisibilityChangeMask,
4823
                        TableEventProc, (ClientData) tablePtr);
4824
  Tk_CreateSelHandler(tablePtr->tkwin, XA_PRIMARY, XA_STRING,
4825
                      TableFetchSelection, (ClientData) tablePtr, XA_STRING);
4826
 
4827
  tablePtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(tablePtr->tkwin),
4828
                        TableWidgetCmd, (ClientData) tablePtr,
4829
                        (Tcl_CmdDeleteProc *) TableCmdDeletedProc);
4830
  if (TableConfigure(interp, tablePtr, argc - 2, argv + 2, 0, 1) != TCL_OK) {
4831
    Tk_DestroyWindow(new);
4832
    return TCL_ERROR;
4833
  }
4834
  TableInitTags(tablePtr);
4835
  /* This is needed to avoid bug where the DLL is unloaded before
4836
   * the table is properly destroyed */
4837
  Tcl_CreateExitHandler((Tcl_ExitProc *) TableCmdDeletedProc,
4838
                        (ClientData) tablePtr);
4839
  Tcl_SetResult(interp, Tk_PathName(tablePtr->tkwin), TCL_STATIC);
4840
  return TCL_OK;
4841
}
4842
 
4843
/* Function to call on loading the Table module */
4844
 
4845
EXPORT(int,Tktable_Init)(interp)
4846
    Tcl_Interp *interp;
4847
{
4848
  static char init_script[] =
4849
    "if {[catch {source \"" TCL_RUNTIME "\"}]} {\n"
4850
#include "tkTabletcl.h"
4851
    "}\n";
4852
  if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 0) == NULL ||
4853
      Tcl_PkgRequire(interp, "Tk", TK_VERSION, 0) == NULL ||
4854
      Tcl_PkgProvide(interp, "Tktable", TBL_VERSION) != TCL_OK) {
4855
    return TCL_ERROR;
4856
  }
4857
  Tcl_CreateCommand(interp, TBL_COMMAND, TableCmd,
4858
                    (ClientData) Tk_MainWindow(interp),
4859
                    (Tcl_CmdDeleteProc *) NULL);
4860
 
4861
  return Tcl_Eval(interp, init_script);
4862
}
4863
 
4864
EXPORT(int,Tktable_SafeInit)(interp)
4865
    Tcl_Interp *interp;
4866
{
4867
  return Tktable_Init(interp);
4868
}
4869
 
4870
#ifdef _WIN32
4871
/*
4872
 *----------------------------------------------------------------------
4873
 *
4874
 * DllEntryPoint --
4875
 *
4876
 *      This wrapper function is used by Windows to invoke the
4877
 *      initialization code for the DLL.  If we are compiling
4878
 *      with Visual C++, this routine will be renamed to DllMain.
4879
 *      routine.
4880
 *
4881
 * Results:
4882
 *      Returns TRUE;
4883
 *
4884
 * Side effects:
4885
 *      None.
4886
 *
4887
 *----------------------------------------------------------------------
4888
 */
4889
 
4890
BOOL APIENTRY
4891
DllEntryPoint(hInst, reason, reserved)
4892
    HINSTANCE hInst;            /* Library instance handle. */
4893
    DWORD reason;               /* Reason this function is being called. */
4894
    LPVOID reserved;            /* Not used. */
4895
{
4896
  return TRUE;
4897
}
4898
#endif

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.