1/*
2 * tkTableTag.c --
3 *
4 *	This module implements tags for table widgets.
5 *
6 * Copyright (c) 1998-2002 Jeffrey Hobbs
7 *
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10 *
11 * RCS: @(#) $Id: tkTableTag.c,v 1.15 2008/11/14 22:46:57 hobbs Exp $
12 */
13
14#include "tkTable.h"
15
16static TableTag *TableTagGetEntry _ANSI_ARGS_((Table *tablePtr, char *name,
17	int objc, CONST char **argv));
18static unsigned int	TableTagGetPriority _ANSI_ARGS_((Table *tablePtr,
19	TableTag *tagPtr));
20static void	TableImageProc _ANSI_ARGS_((ClientData clientData, int x,
21	int y, int width, int height, int imageWidth, int imageHeight));
22static int	TableOptionReliefSet _ANSI_ARGS_((ClientData clientData,
23			Tcl_Interp *interp, Tk_Window tkwin,
24			CONST84 char *value, char *widgRec, int offset));
25static char *	TableOptionReliefGet _ANSI_ARGS_((ClientData clientData,
26			Tk_Window tkwin, char *widgRec, int offset,
27			Tcl_FreeProc **freeProcPtr));
28
29static CONST84 char *tagCmdNames[] = {
30    "celltag", "cget", "coltag", "configure", "delete", "exists",
31    "includes", "lower", "names", "raise", "rowtag", (char *) NULL
32};
33
34enum tagCmd {
35    TAG_CELLTAG, TAG_CGET, TAG_COLTAG, TAG_CONFIGURE, TAG_DELETE, TAG_EXISTS,
36    TAG_INCLUDES, TAG_LOWER, TAG_NAMES, TAG_RAISE, TAG_ROWTAG
37};
38
39static Cmd_Struct tagState_vals[]= {
40    {"unknown",	 STATE_UNKNOWN},
41    {"normal",	 STATE_NORMAL},
42    {"disabled", STATE_DISABLED},
43    {"",	 0 }
44};
45
46static Tk_CustomOption tagStateOpt =
47{ Cmd_OptionSet, Cmd_OptionGet, (ClientData) (&tagState_vals) };
48static Tk_CustomOption tagBdOpt =
49{ TableOptionBdSet, TableOptionBdGet, (ClientData) BD_TABLE_TAG };
50static Tk_CustomOption tagReliefOpt =
51{ TableOptionReliefSet, TableOptionReliefGet, (ClientData) NULL };
52
53/*
54 * The default specification for configuring tags
55 * Done like this to make the command line parsing easy
56 */
57
58static Tk_ConfigSpec tagConfig[] = {
59  {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", "center",
60   Tk_Offset(TableTag, anchor), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
61  {TK_CONFIG_BORDER, "-background", "background", "Background", NULL,
62   Tk_Offset(TableTag, bg), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
63  {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL, (char *)NULL, 0, 0},
64  {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL, (char *)NULL, 0, 0},
65  {TK_CONFIG_CUSTOM, "-borderwidth", "borderWidth", "BorderWidth", "",
66   0 /* no offset */,
67   TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK, &tagBdOpt },
68  {TK_CONFIG_STRING, "-ellipsis", "ellipsis", "Ellipsis", "",
69   Tk_Offset(TableTag, ellipsis), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
70  {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground", NULL,
71   Tk_Offset(TableTag, fg), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
72  {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL, (char *)NULL, 0, 0},
73  {TK_CONFIG_FONT, "-font", "font", "Font", NULL,
74   Tk_Offset(TableTag, tkfont), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
75  {TK_CONFIG_STRING, "-image", "image", "Image", NULL,
76   Tk_Offset(TableTag, imageStr),
77   TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
78  {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify", "left",
79   Tk_Offset(TableTag, justify), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK },
80  {TK_CONFIG_INT, "-multiline", "multiline", "Multiline", "-1",
81   Tk_Offset(TableTag, multiline), TK_CONFIG_DONT_SET_DEFAULT },
82  {TK_CONFIG_CUSTOM, "-relief", "relief", "Relief", "flat",
83   Tk_Offset(TableTag, relief), TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK,
84   &tagReliefOpt },
85  {TK_CONFIG_INT, "-showtext", "showText", "ShowText", "-1",
86   Tk_Offset(TableTag, showtext), TK_CONFIG_DONT_SET_DEFAULT },
87  {TK_CONFIG_CUSTOM, "-state", "state", "State", "unknown",
88   Tk_Offset(TableTag, state), TK_CONFIG_DONT_SET_DEFAULT, &tagStateOpt },
89  {TK_CONFIG_INT, "-wrap", "wrap", "Wrap", "-1",
90   Tk_Offset(TableTag, wrap), TK_CONFIG_DONT_SET_DEFAULT },
91  {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL, 0, 0}
92};
93
94/*
95 * The join tag structure is used to create a combined tag, so it
96 * keeps priority info.
97 */
98typedef struct {
99    TableTag	tag;		/* must be first */
100    unsigned int magic;
101    unsigned int pbg, pfg, pborders, prelief, ptkfont, panchor, pimage;
102    unsigned int pstate, pjustify, pmultiline, pwrap, pshowtext, pellipsis;
103} TableJoinTag;
104
105/*
106 *----------------------------------------------------------------------
107 *
108 * TableImageProc --
109 *	Called when an image associated with a tag is changed.
110 *
111 * Results:
112 *	None.
113 *
114 * Side effects:
115 *	Invalidates the whole table.
116 *	This should only invalidate affected cells, but that info
117 *	is not managed...
118 *
119 *----------------------------------------------------------------------
120 */
121static void
122TableImageProc(ClientData clientData, int x, int y, int width, int height,
123	       int imageWidth, int imageHeight)
124{
125    TableInvalidateAll((Table *)clientData, 0);
126}
127
128/*
129 *----------------------------------------------------------------------
130 *
131 * TableNewTag --
132 *	ckallocs space for a new tag structure and inits the structure.
133 *
134 * Results:
135 *	Returns a pointer to the new structure.  Must be freed later.
136 *
137 * Side effects:
138 *	None.
139 *
140 *----------------------------------------------------------------------
141 */
142TableTag *
143TableNewTag(Table *tablePtr)
144{
145    TableTag *tagPtr;
146
147    /*
148     * If tablePtr is NULL, make a regular tag, otherwise make a join tag.
149     */
150    if (tablePtr == NULL) {
151	tagPtr = (TableTag *) ckalloc(sizeof(TableTag));
152	memset((VOID *) tagPtr, 0, sizeof(TableTag));
153
154	/*
155	 * Set the values that aren't 0/NULL by default
156	 */
157	tagPtr->anchor		= (Tk_Anchor)-1;
158	tagPtr->justify		= (Tk_Justify)-1;
159	tagPtr->multiline	= -1;
160	tagPtr->relief		= -1;
161	tagPtr->showtext	= -1;
162	tagPtr->state		= STATE_UNKNOWN;
163	tagPtr->wrap		= -1;
164    } else {
165	TableJoinTag *jtagPtr = (TableJoinTag *) ckalloc(sizeof(TableJoinTag));
166	memset((VOID *) jtagPtr, 0, sizeof(TableJoinTag));
167	tagPtr = (TableTag *) jtagPtr;
168
169	tagPtr->anchor		= (Tk_Anchor)-1;
170	tagPtr->justify		= (Tk_Justify)-1;
171	tagPtr->multiline	= -1;
172	tagPtr->relief		= -1;
173	tagPtr->showtext	= -1;
174	tagPtr->state		= STATE_UNKNOWN;
175	tagPtr->wrap		= -1;
176	jtagPtr->magic		= 0x99ABCDEF;
177	jtagPtr->pbg		= -1;
178	jtagPtr->pfg		= -1;
179	jtagPtr->pborders	= -1;
180	jtagPtr->prelief	= -1;
181	jtagPtr->ptkfont	= -1;
182	jtagPtr->panchor	= -1;
183	jtagPtr->pimage		= -1;
184	jtagPtr->pstate		= -1;
185	jtagPtr->pjustify	= -1;
186	jtagPtr->pmultiline	= -1;
187	jtagPtr->pwrap		= -1;
188	jtagPtr->pshowtext	= -1;
189	jtagPtr->pellipsis	= -1;
190    }
191
192    return (TableTag *) tagPtr;
193}
194
195/*
196 *----------------------------------------------------------------------
197 *
198 * TableResetTag --
199 *	This routine resets a given tag to the table defaults.
200 *
201 * Results:
202 *	Tag will have values changed.
203 *
204 * Side effects:
205 *	None.
206 *
207 *----------------------------------------------------------------------
208 */
209void
210TableResetTag(Table *tablePtr, TableTag *tagPtr)
211{
212    TableJoinTag *jtagPtr = (TableJoinTag *) tagPtr;
213
214    if (jtagPtr->magic != 0x99ABCDEF) {
215	panic("bad mojo in TableResetTag");
216    }
217
218    memset((VOID *) jtagPtr, 0, sizeof(TableJoinTag));
219
220    tagPtr->anchor	= (Tk_Anchor)-1;
221    tagPtr->justify	= (Tk_Justify)-1;
222    tagPtr->multiline	= -1;
223    tagPtr->relief	= -1;
224    tagPtr->showtext	= -1;
225    tagPtr->state	= STATE_UNKNOWN;
226    tagPtr->wrap	= -1;
227    jtagPtr->magic	= 0x99ABCDEF;
228    jtagPtr->pbg	= -1;
229    jtagPtr->pfg	= -1;
230    jtagPtr->pborders	= -1;
231    jtagPtr->prelief	= -1;
232    jtagPtr->ptkfont	= -1;
233    jtagPtr->panchor	= -1;
234    jtagPtr->pimage	= -1;
235    jtagPtr->pstate	= -1;
236    jtagPtr->pjustify	= -1;
237    jtagPtr->pmultiline	= -1;
238    jtagPtr->pwrap	= -1;
239    jtagPtr->pshowtext	= -1;
240    jtagPtr->pellipsis	= -1;
241
242    /*
243     * Merge in the default tag.
244     */
245    memcpy((VOID *) jtagPtr, (VOID *) &(tablePtr->defaultTag),
246	    sizeof(TableTag));
247}
248
249/*
250 *----------------------------------------------------------------------
251 *
252 * TableMergeTag --
253 *	This routine merges two tags by adding any fields from the addTag
254 *	that are set to the baseTag.
255 *
256 * Results:
257 *	baseTag will inherit all set characteristics of addTag
258 *	(addTag thus has the priority).
259 *
260 * Side effects:
261 *	None.
262 *
263 *----------------------------------------------------------------------
264 */
265void
266TableMergeTag(Table *tablePtr, TableTag *baseTag, TableTag *addTag)
267{
268    TableJoinTag *jtagPtr = (TableJoinTag *) baseTag;
269    unsigned int prio;
270
271    if (jtagPtr->magic != 0x99ABCDEF) {
272	panic("bad mojo in TableMergeTag");
273    }
274
275#ifndef NO_TAG_PRIORITIES
276    /*
277     * Find priority for the tag to merge
278     */
279    prio = TableTagGetPriority(tablePtr, addTag);
280
281    if ((addTag->anchor != -1) && (prio < jtagPtr->panchor)) {
282	baseTag->anchor		= addTag->anchor;
283	jtagPtr->panchor	= prio;
284    }
285    if ((addTag->bg != NULL) && (prio < jtagPtr->pbg)) {
286	baseTag->bg		= addTag->bg;
287	jtagPtr->pbg		= prio;
288    }
289    if ((addTag->fg != NULL) && (prio < jtagPtr->pfg)) {
290	baseTag->fg		= addTag->fg;
291	jtagPtr->pfg		= prio;
292    }
293    if ((addTag->ellipsis != NULL) && (prio < jtagPtr->pellipsis)) {
294	baseTag->ellipsis	= addTag->ellipsis;
295	jtagPtr->pellipsis	= prio;
296    }
297    if ((addTag->tkfont != NULL) && (prio < jtagPtr->ptkfont)) {
298	baseTag->tkfont		= addTag->tkfont;
299	jtagPtr->ptkfont	= prio;
300    }
301    if ((addTag->imageStr != NULL) && (prio < jtagPtr->pimage)) {
302	baseTag->imageStr	= addTag->imageStr;
303	baseTag->image		= addTag->image;
304	jtagPtr->pimage		= prio;
305    }
306    if ((addTag->multiline >= 0) && (prio < jtagPtr->pmultiline)) {
307	baseTag->multiline	= addTag->multiline;
308	jtagPtr->pmultiline	= prio;
309    }
310    if ((addTag->relief != -1) && (prio < jtagPtr->prelief)) {
311	baseTag->relief		= addTag->relief;
312	jtagPtr->prelief	= prio;
313    }
314    if ((addTag->showtext >= 0) && (prio < jtagPtr->pshowtext)) {
315	baseTag->showtext	= addTag->showtext;
316	jtagPtr->pshowtext	= prio;
317    }
318    if ((addTag->state != STATE_UNKNOWN) && (prio < jtagPtr->pstate)) {
319	baseTag->state		= addTag->state;
320	jtagPtr->pstate		= prio;
321    }
322    if ((addTag->justify != -1) && (prio < jtagPtr->pjustify)) {
323	baseTag->justify	= addTag->justify;
324	jtagPtr->pjustify	= prio;
325    }
326    if ((addTag->wrap >= 0) && (prio < jtagPtr->pwrap)) {
327	baseTag->wrap		= addTag->wrap;
328	jtagPtr->pwrap		= prio;
329    }
330    if ((addTag->borders) && (prio < jtagPtr->pborders)) {
331	baseTag->borderStr	= addTag->borderStr;
332	baseTag->borders	= addTag->borders;
333	baseTag->bd[0]		= addTag->bd[0];
334	baseTag->bd[1]		= addTag->bd[1];
335	baseTag->bd[2]		= addTag->bd[2];
336	baseTag->bd[3]		= addTag->bd[3];
337	jtagPtr->pborders	= prio;
338    }
339#else
340    if (addTag->anchor != -1)	baseTag->anchor = addTag->anchor;
341    if (addTag->bg != NULL)	baseTag->bg	= addTag->bg;
342    if (addTag->fg != NULL)	baseTag->fg	= addTag->fg;
343    if (addTag->ellipsis != NULL) baseTag->ellipsis = addTag->ellipsis;
344    if (addTag->tkfont != NULL)	baseTag->tkfont	= addTag->tkfont;
345    if (addTag->imageStr != NULL) {
346	baseTag->imageStr	= addTag->imageStr;
347	baseTag->image		= addTag->image;
348    }
349    if (addTag->multiline >= 0)	baseTag->multiline	= addTag->multiline;
350    if (addTag->relief != -1)	baseTag->relief		= addTag->relief;
351    if (addTag->showtext >= 0)	baseTag->showtext	= addTag->showtext;
352    if (addTag->state != STATE_UNKNOWN)	baseTag->state	= addTag->state;
353    if (addTag->justify != -1)	baseTag->justify	= addTag->justify;
354    if (addTag->wrap >= 0)	baseTag->wrap		= addTag->wrap;
355    if (addTag->borders) {
356	baseTag->borderStr	= addTag->borderStr;
357	baseTag->borders	= addTag->borders;
358	baseTag->bd[0]		= addTag->bd[0];
359	baseTag->bd[1]		= addTag->bd[1];
360	baseTag->bd[2]		= addTag->bd[2];
361	baseTag->bd[3]		= addTag->bd[3];
362    }
363#endif
364}
365
366/*
367 *----------------------------------------------------------------------
368 *
369 * TableInvertTag --
370 *	This routine swaps background and foreground for the selected tag.
371 *
372 * Results:
373 *	Inverts fg and bg of tag.
374 *
375 * Side effects:
376 *	None.
377 *
378 *----------------------------------------------------------------------
379 */
380void
381TableInvertTag(TableTag *baseTag)
382{
383    Tk_3DBorder tmpBg;
384
385    tmpBg	= baseTag->fg;
386    baseTag->fg	= baseTag->bg;
387    baseTag->bg	= tmpBg;
388}
389
390/*
391 *----------------------------------------------------------------------
392 *
393 * TableGetTagBorders --
394 *	This routine gets the border values based on a tag.
395 *
396 * Results:
397 *	It returns the values in the int*'s (if not NULL), and the
398 *	total number of defined borders as a result.
399 *
400 * Side effects:
401 *	None.
402 *
403 *----------------------------------------------------------------------
404 */
405int
406TableGetTagBorders(TableTag *tagPtr,
407	int *left, int *right, int *top, int *bottom)
408{
409    switch (tagPtr->borders) {
410	case 0:
411	    if (left)	{ *left		= 0; }
412	    if (right)	{ *right	= 0; }
413	    if (top)	{ *top		= 0; }
414	    if (bottom)	{ *bottom	= 0; }
415	    break;
416	case 1:
417	    if (left)	{ *left		= tagPtr->bd[0]; }
418	    if (right)	{ *right	= tagPtr->bd[0]; }
419	    if (top)	{ *top		= tagPtr->bd[0]; }
420	    if (bottom)	{ *bottom	= tagPtr->bd[0]; }
421	    break;
422	case 2:
423	    if (left)	{ *left		= tagPtr->bd[0]; }
424	    if (right)	{ *right	= tagPtr->bd[1]; }
425	    if (top)	{ *top		= 0; }
426	    if (bottom)	{ *bottom	= 0; }
427	    break;
428	case 4:
429	    if (left)	{ *left		= tagPtr->bd[0]; }
430	    if (right)	{ *right	= tagPtr->bd[1]; }
431	    if (top)	{ *top		= tagPtr->bd[2]; }
432	    if (bottom)	{ *bottom	= tagPtr->bd[3]; }
433	    break;
434	default:
435	    panic("invalid border value '%d'\n", tagPtr->borders);
436	    break;
437    }
438    return tagPtr->borders;
439}
440
441/*
442 *----------------------------------------------------------------------
443 *
444 * TableTagGetEntry --
445 *	Takes a name and optional args and creates a tag entry in the
446 *	table's tag table.
447 *
448 * Results:
449 *	A new tag entry will be created and returned.
450 *
451 * Side effects:
452 *	None.
453 *
454 *----------------------------------------------------------------------
455 */
456static TableTag *
457TableTagGetEntry(Table *tablePtr, char *name, int objc, CONST char **argv)
458{
459    Tcl_HashEntry *entryPtr;
460    TableTag *tagPtr = NULL;
461    int new;
462
463    entryPtr = Tcl_CreateHashEntry(tablePtr->tagTable, name, &new);
464    if (new) {
465	tagPtr = TableNewTag(NULL);
466	Tcl_SetHashValue(entryPtr, (ClientData) tagPtr);
467	if (tablePtr->tagPrioSize >= tablePtr->tagPrioMax) {
468	    int i;
469	    /*
470	     * Increase the priority list size in blocks of 10
471	     */
472	    tablePtr->tagPrioMax += 10;
473	    tablePtr->tagPrioNames = (char **) ckrealloc(
474		(char *) tablePtr->tagPrioNames,
475		sizeof(TableTag *) * tablePtr->tagPrioMax);
476	    tablePtr->tagPrios = (TableTag **) ckrealloc(
477		(char *) tablePtr->tagPrios,
478		sizeof(TableTag *) * tablePtr->tagPrioMax);
479	    for (i = tablePtr->tagPrioSize; i < tablePtr->tagPrioMax; i++) {
480		tablePtr->tagPrioNames[i] = (char *) NULL;
481		tablePtr->tagPrios[i] = (TableTag *) NULL;
482	    }
483	}
484	tablePtr->tagPrioNames[tablePtr->tagPrioSize] =
485	    (char *) Tcl_GetHashKey(tablePtr->tagTable, entryPtr);
486	tablePtr->tagPrios[tablePtr->tagPrioSize] = tagPtr;
487	tablePtr->tagPrioSize++;
488    } else {
489	tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
490    }
491    if (objc) {
492	Tk_ConfigureWidget(tablePtr->interp, tablePtr->tkwin, tagConfig,
493		objc, (CONST84 char **) argv, (char *)tagPtr,
494		TK_CONFIG_ARGV_ONLY);
495    }
496    return tagPtr;
497}
498
499/*
500 *----------------------------------------------------------------------
501 *
502 * TableTagGetPriority --
503 *	Get the priority value for a tag.
504 *
505 * Results:
506 *	returns the priority.
507 *
508 * Side effects:
509 *	None.
510 *
511 *----------------------------------------------------------------------
512 */
513static unsigned int
514TableTagGetPriority(Table *tablePtr, TableTag *tagPtr)
515{
516    unsigned int prio = 0;
517    while (tagPtr != tablePtr->tagPrios[prio]) { prio++; }
518    return prio;
519}
520
521/*
522 *----------------------------------------------------------------------
523 *
524 * TableInitTags --
525 *	Creates the static table tags.
526 *
527 * Results:
528 *	active, sel, title and flash are created as tags.
529 *
530 * Side effects:
531 *	None.
532 *
533 *----------------------------------------------------------------------
534 */
535void
536TableInitTags(Table *tablePtr)
537{
538    static CONST char *activeArgs[] = {"-bg", ACTIVE_BG, "-relief", "flat" };
539    static CONST char *selArgs[]    = {"-bg", SELECT_BG, "-fg", SELECT_FG,
540				       "-relief", "sunken" };
541    static CONST char *titleArgs[]  = {"-bg", DISABLED, "-fg", "white",
542				       "-relief", "flat",
543				       "-state", "disabled" };
544    static CONST char *flashArgs[]  = {"-bg", "red" };
545    /*
546     * The order of creation is important to priority.
547     */
548    TableTagGetEntry(tablePtr, "flash", ARSIZE(flashArgs), flashArgs);
549    TableTagGetEntry(tablePtr, "active", ARSIZE(activeArgs), activeArgs);
550    TableTagGetEntry(tablePtr, "sel", ARSIZE(selArgs), selArgs);
551    TableTagGetEntry(tablePtr, "title", ARSIZE(titleArgs), titleArgs);
552}
553
554/*
555 *----------------------------------------------------------------------
556 *
557 * FindRowColTag --
558 *	Finds a row/col tag based on the row/col styles and tagCommand.
559 *
560 * Results:
561 *	Returns tag associated with row/col cell, if any.
562 *
563 * Side effects:
564 *	Possible side effects from eval of tagCommand.
565 *	IMPORTANT: This plays with the interp result object,
566 *	so use of resultPtr in prior command may be invalid after
567 *	calling this function.
568 *
569 *----------------------------------------------------------------------
570 */
571TableTag *
572FindRowColTag(Table *tablePtr, int cell, int mode)
573{
574    Tcl_HashEntry *entryPtr;
575    TableTag *tagPtr = NULL;
576
577    entryPtr = Tcl_FindHashEntry((mode == ROW) ? tablePtr->rowStyles
578				 : tablePtr->colStyles, (char *) cell);
579    if (entryPtr == NULL) {
580	char *cmd = (mode == ROW) ? tablePtr->rowTagCmd : tablePtr->colTagCmd;
581	if (cmd) {
582	    register Tcl_Interp *interp = tablePtr->interp;
583	    char buf[INDEX_BUFSIZE];
584	    /*
585	     * Since no specific row/col tag exists, eval the given command
586	     * with row/col appended
587	     */
588	    sprintf(buf, " %d", cell);
589	    Tcl_Preserve((ClientData) interp);
590	    if (Tcl_VarEval(interp, cmd, buf, (char *)NULL) == TCL_OK) {
591		CONST char *name = Tcl_GetStringResult(interp);
592		if (name && *name) {
593		    /*
594		     * If a result was returned, check to see if it is
595		     * a valid tag.
596		     */
597		    entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, name);
598		}
599	    }
600	    Tcl_Release((ClientData) interp);
601	    Tcl_ResetResult(interp);
602	}
603    }
604    if (entryPtr != NULL) {
605	/*
606	 * This can be either the one in row|colStyles,
607	 * or that returned by eval'ing the row|colTagCmd
608	 */
609	tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
610    }
611    return tagPtr;
612}
613
614/*
615 *----------------------------------------------------------------------
616 *
617 * TableCleanupTag --
618 *	Releases the resources used by a tag before it is freed up.
619 *
620 * Results:
621 *	None.
622 *
623 * Side effects:
624 *	The tag is no longer valid.
625 *
626 *----------------------------------------------------------------------
627 */
628void
629TableCleanupTag(Table *tablePtr, TableTag *tagPtr)
630{
631    /*
632     * Free resources that the optionSpec doesn't specifically know about
633     */
634    if (tagPtr->image) {
635	Tk_FreeImage(tagPtr->image);
636    }
637
638    Tk_FreeOptions(tagConfig, (char *) tagPtr, tablePtr->display, 0);
639}
640
641/*
642 *--------------------------------------------------------------
643 *
644 * Table_TagCmd --
645 *	This procedure is invoked to process the tag method
646 *	that corresponds to a widget managed by this module.
647 *	See the user documentation for details on what it does.
648 *
649 * Results:
650 *	A standard Tcl result.
651 *
652 * Side effects:
653 *	See the user documentation.
654 *
655 *--------------------------------------------------------------
656 */
657int
658Table_TagCmd(ClientData clientData, register Tcl_Interp *interp,
659	    int objc, Tcl_Obj *CONST objv[])
660{
661    register Table *tablePtr = (Table *)clientData;
662    int result = TCL_OK, cmdIndex, i, newEntry, value, len;
663    int row, col, tagPrio, refresh = 0;
664    TableTag *tagPtr, *tag2Ptr;
665    Tcl_HashEntry *entryPtr, *scanPtr;
666    Tcl_HashTable *hashTblPtr;
667    Tcl_HashSearch search;
668    Tk_Image image;
669    Tcl_Obj *objPtr, *resultPtr;
670    char buf[INDEX_BUFSIZE], *keybuf, *tagname;
671
672    if (objc < 3) {
673	Tcl_WrongNumArgs(interp, 2, objv, "option ?arg arg ...?");
674	return TCL_ERROR;
675    }
676
677    result = Tcl_GetIndexFromObj(interp, objv[2], tagCmdNames,
678				 "tag option", 0, &cmdIndex);
679    if (result != TCL_OK) {
680	return result;
681    }
682    /*
683     * Before using this object, make sure there aren't any calls that
684     * could have changed the interp result, thus freeing the object.
685     */
686    resultPtr = Tcl_GetObjResult(interp);
687
688    switch ((enum tagCmd) cmdIndex) {
689	case TAG_CELLTAG:	/* add named tag to a (group of) cell(s) */
690	    if (objc < 4) {
691		Tcl_WrongNumArgs(interp, 3, objv, "tag ?arg arg ...?");
692		return TCL_ERROR;
693	    }
694	    tagname = Tcl_GetStringFromObj(objv[3], &len);
695	    if (len == 0) {
696		/*
697		 * An empty string was specified, so just delete the tag.
698		 */
699		tagPtr = NULL;
700	    } else {
701		/*
702		 * Get the pointer to the tag structure.  If it doesn't
703		 * exist, it will be created.
704		 */
705		tagPtr = TableTagGetEntry(tablePtr, tagname, 0, NULL);
706	    }
707
708	    if (objc == 4) {
709		/*
710		 * The user just wants the cells with this tag returned.
711		 * Handle specially tags named: active, flash, sel, title
712		 */
713
714		if ((tablePtr->flags & HAS_ACTIVE) &&
715			STREQ(tagname, "active")) {
716		    TableMakeArrayIndex(
717			tablePtr->activeRow+tablePtr->rowOffset,
718			tablePtr->activeCol+tablePtr->colOffset, buf);
719		    Tcl_SetStringObj(resultPtr, buf, -1);
720		} else if ((tablePtr->flashMode && STREQ(tagname, "flash"))
721			|| STREQ(tagname, "sel")) {
722		    hashTblPtr = (*tagname == 's') ?
723			tablePtr->selCells : tablePtr->flashCells;
724		    for (scanPtr = Tcl_FirstHashEntry(hashTblPtr, &search);
725			 scanPtr != NULL;
726			 scanPtr = Tcl_NextHashEntry(&search)) {
727			keybuf = (char *) Tcl_GetHashKey(hashTblPtr, scanPtr);
728			Tcl_ListObjAppendElement(NULL, resultPtr,
729				Tcl_NewStringObj(keybuf, -1));
730		    }
731		} else if (STREQ(tagname, "title") &&
732			(tablePtr->titleRows || tablePtr->titleCols)) {
733		    for (row = tablePtr->rowOffset;
734			 row < tablePtr->rowOffset+tablePtr->rows; row++) {
735			for (col = tablePtr->colOffset;
736			     col < tablePtr->colOffset+tablePtr->titleCols;
737			     col++) {
738			    TableMakeArrayIndex(row, col, buf);
739			    Tcl_ListObjAppendElement(NULL, resultPtr,
740				    Tcl_NewStringObj(buf, -1));
741			}
742		    }
743		    for (row = tablePtr->rowOffset;
744			 row < tablePtr->rowOffset+tablePtr->titleRows;
745			 row++) {
746			for (col = tablePtr->colOffset+tablePtr->titleCols;
747			     col < tablePtr->colOffset+tablePtr->cols; col++) {
748			    TableMakeArrayIndex(row, col, buf);
749			    Tcl_ListObjAppendElement(NULL, resultPtr,
750				    Tcl_NewStringObj(buf, -1));
751			}
752		    }
753		} else {
754		    /*
755		     * Check this tag pointer amongst all tagged cells
756		     */
757		    for (scanPtr = Tcl_FirstHashEntry(tablePtr->cellStyles,
758			    &search);
759			 scanPtr != NULL;
760			 scanPtr = Tcl_NextHashEntry(&search)) {
761			if ((TableTag *) Tcl_GetHashValue(scanPtr) == tagPtr) {
762			    keybuf = (char *) Tcl_GetHashKey(
763				tablePtr->cellStyles, scanPtr);
764			    Tcl_ListObjAppendElement(NULL, resultPtr,
765				    Tcl_NewStringObj(keybuf, -1));
766			}
767		    }
768		}
769		return TCL_OK;
770	    }
771
772	    /*
773	     * Loop through the arguments and fill in the hash table
774	     */
775	    for (i = 4; i < objc; i++) {
776		/*
777		 * Try and parse the index
778		 */
779		if (TableGetIndexObj(tablePtr, objv[i], &row, &col)
780			!= TCL_OK) {
781		    return TCL_ERROR;
782		}
783		/*
784		 * Get the hash key ready
785		 */
786		TableMakeArrayIndex(row, col, buf);
787
788		if (tagPtr == NULL) {
789		    /*
790		     * This is a deletion
791		     */
792		    entryPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf);
793		    if (entryPtr != NULL) {
794			Tcl_DeleteHashEntry(entryPtr);
795			refresh = 1;
796		    }
797		} else {
798		    /*
799		     * Add a key to the hash table and set it to point to the
800		     * Tag structure if it wasn't the same as an existing one
801		     */
802		    entryPtr = Tcl_CreateHashEntry(tablePtr->cellStyles,
803			    buf, &newEntry);
804		    if (newEntry || (tagPtr !=
805			    (TableTag *) Tcl_GetHashValue(entryPtr))) {
806			Tcl_SetHashValue(entryPtr, (ClientData) tagPtr);
807			refresh = 1;
808		    }
809		}
810		/*
811		 * Now invalidate this cell for redraw
812		 */
813		if (refresh) {
814		    TableRefresh(tablePtr, row-tablePtr->rowOffset,
815			    col-tablePtr->colOffset, CELL);
816		}
817	    }
818	    return TCL_OK;
819
820	case TAG_COLTAG:
821	case TAG_ROWTAG: {	    /* tag a row or a column */
822	    int forRows = (cmdIndex == TAG_ROWTAG);
823
824	    if (objc < 4) {
825		Tcl_WrongNumArgs(interp, 3, objv, "tag ?arg arg ..?");
826		return TCL_ERROR;
827	    }
828	    tagname = Tcl_GetStringFromObj(objv[3], &len);
829	    if (len == 0) {
830		/*
831		 * Empty string, so we want to delete this element
832		 */
833		tagPtr = NULL;
834	    } else {
835		/*
836		 * Get the pointer to the tag structure.  If it doesn't
837		 * exist, it will be created.
838		 */
839		tagPtr = TableTagGetEntry(tablePtr, tagname, 0, NULL);
840	    }
841
842	    /*
843	     * Choose the correct hash table based on args
844	     */
845	    hashTblPtr = forRows ? tablePtr->rowStyles : tablePtr->colStyles;
846
847	    if (objc == 4) {
848		/* the user just wants the tagged cells to be returned */
849		/* Special handling for tags: active, flash, sel, title */
850
851		if ((tablePtr->flags & HAS_ACTIVE) &&
852			strcmp(tagname, "active") == 0) {
853		    Tcl_SetIntObj(resultPtr,
854			    (forRows ?
855				    tablePtr->activeRow+tablePtr->rowOffset :
856				    tablePtr->activeCol+tablePtr->colOffset));
857		} else if ((tablePtr->flashMode && STREQ(tagname, "flash"))
858			|| STREQ(tagname, "sel")) {
859		    Tcl_HashTable *cacheTblPtr;
860
861		    cacheTblPtr = (Tcl_HashTable *)
862			ckalloc(sizeof(Tcl_HashTable));
863		    Tcl_InitHashTable(cacheTblPtr, TCL_ONE_WORD_KEYS);
864
865		    hashTblPtr = (*tagname == 's') ?
866			tablePtr->selCells : tablePtr->flashCells;
867		    for (scanPtr = Tcl_FirstHashEntry(hashTblPtr, &search);
868			 scanPtr != NULL;
869			 scanPtr = Tcl_NextHashEntry(&search)) {
870			TableParseArrayIndex(&row, &col,
871				Tcl_GetHashKey(hashTblPtr, scanPtr));
872			value = forRows ? row : col;
873			entryPtr = Tcl_CreateHashEntry(cacheTblPtr,
874				(char *)value, &newEntry);
875			if (newEntry) {
876			    Tcl_ListObjAppendElement(NULL, resultPtr,
877				    Tcl_NewIntObj(value));
878			}
879		    }
880
881		    Tcl_DeleteHashTable(cacheTblPtr);
882		    ckfree((char *) (cacheTblPtr));
883		} else if (STREQ(tagname, "title") &&
884			(forRows?tablePtr->titleRows:tablePtr->titleCols)) {
885		    if (forRows) {
886			for (row = tablePtr->rowOffset;
887			     row < tablePtr->rowOffset+tablePtr->titleRows;
888			     row++) {
889			    Tcl_ListObjAppendElement(NULL, resultPtr,
890				    Tcl_NewIntObj(row));
891			}
892		    } else {
893			for (col = tablePtr->colOffset;
894			     col < tablePtr->colOffset+tablePtr->titleCols;
895			     col++) {
896			    Tcl_ListObjAppendElement(NULL, resultPtr,
897				    Tcl_NewIntObj(col));
898			}
899		    }
900		} else {
901		    for (scanPtr = Tcl_FirstHashEntry(hashTblPtr, &search);
902			 scanPtr != NULL;
903			 scanPtr = Tcl_NextHashEntry(&search)) {
904			/* is this the tag pointer on this row */
905			if ((TableTag *) Tcl_GetHashValue(scanPtr) == tagPtr) {
906			    objPtr = Tcl_NewIntObj(
907				(int) Tcl_GetHashKey(hashTblPtr, scanPtr));
908			    Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
909			}
910		    }
911		}
912		return TCL_OK;
913	    }
914
915	    /*
916	     * Loop through the arguments and fill in the hash table
917	     */
918	    for (i = 4; i < objc; i++) {
919		/*
920		 * Try and parse the index
921		 */
922		if (Tcl_GetIntFromObj(interp, objv[i], &value) != TCL_OK) {
923		    return TCL_ERROR;
924		}
925		if (tagPtr == NULL) {
926		    /*
927		     * This is a deletion
928		     */
929		    entryPtr = Tcl_FindHashEntry(hashTblPtr, (char *)value);
930		    if (entryPtr != NULL) {
931			Tcl_DeleteHashEntry(entryPtr);
932			refresh = 1;
933		    }
934		} else {
935		    /*
936		     * Add a key to the hash table and set it to point to the
937		     * Tag structure if it wasn't the same as an existing one
938		     */
939		    entryPtr = Tcl_CreateHashEntry(hashTblPtr,
940			    (char *) value, &newEntry);
941		    if (newEntry || (tagPtr !=
942			    (TableTag *) Tcl_GetHashValue(entryPtr))) {
943			Tcl_SetHashValue(entryPtr, (ClientData) tagPtr);
944			refresh = 1;
945		    }
946		}
947		/* and invalidate the row or column affected */
948		if (refresh) {
949		    if (cmdIndex == TAG_ROWTAG) {
950			TableRefresh(tablePtr, value-tablePtr->rowOffset, 0,
951				ROW);
952		    } else {
953			TableRefresh(tablePtr, 0, value-tablePtr->colOffset,
954				COL);
955		    }
956		}
957	    }
958	    return TCL_OK;	/* COLTAG && ROWTAG */
959	}
960
961	case TAG_CGET:
962	    if (objc != 5) {
963		Tcl_WrongNumArgs(interp, 3, objv, "tagName option");
964		return TCL_ERROR;
965	    }
966	    tagname  = Tcl_GetString(objv[3]);
967	    entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname);
968	    if (entryPtr == NULL) {
969		goto invalidtag;
970	    } else {
971		tagPtr = (TableTag *) Tcl_GetHashValue (entryPtr);
972		result = Tk_ConfigureValue(interp, tablePtr->tkwin, tagConfig,
973			(char *) tagPtr, Tcl_GetString(objv[4]), 0);
974	    }
975	    return result;	/* CGET */
976
977	case TAG_CONFIGURE:
978	    if (objc < 4) {
979		Tcl_WrongNumArgs(interp, 3, objv, "tagName ?arg arg  ...?");
980		return TCL_ERROR;
981	    }
982
983	    /*
984	     * Get the pointer to the tag structure.  If it doesn't
985	     * exist, it will be created.
986	     */
987	    tagPtr = TableTagGetEntry(tablePtr, Tcl_GetString(objv[3]),
988		    0, NULL);
989
990	    /*
991	     * If there were less than 6 args, we return the configuration
992	     * (for all or just one option), even for new tags
993	     */
994	    if (objc < 6) {
995		result = Tk_ConfigureInfo(interp, tablePtr->tkwin, tagConfig,
996			(char *) tagPtr, (objc == 5) ?
997			Tcl_GetString(objv[4]) : NULL, 0);
998	    } else {
999		CONST84 char **argv;
1000
1001		/* Stringify */
1002		argv = (CONST84 char **) ckalloc((objc + 1) * sizeof(char *));
1003		for (i = 0; i < objc; i++)
1004		    argv[i] = Tcl_GetString(objv[i]);
1005		argv[objc] = NULL;
1006
1007		result = Tk_ConfigureWidget(interp, tablePtr->tkwin,
1008			tagConfig, objc-4, argv+4, (char *) tagPtr,
1009			TK_CONFIG_ARGV_ONLY);
1010		ckfree((char *) argv);
1011		if (result == TCL_ERROR) {
1012		    return TCL_ERROR;
1013		}
1014
1015		/*
1016		 * Handle change of image name
1017		 */
1018		if (tagPtr->imageStr) {
1019		    image = Tk_GetImage(interp, tablePtr->tkwin,
1020			    tagPtr->imageStr,
1021			    TableImageProc, (ClientData)tablePtr);
1022		    if (image == NULL) {
1023			result = TCL_ERROR;
1024		    }
1025		} else {
1026		    image = NULL;
1027		}
1028		if (tagPtr->image) {
1029		    Tk_FreeImage(tagPtr->image);
1030		}
1031		tagPtr->image = image;
1032
1033		/*
1034		 * We reconfigured, so invalidate the table to redraw
1035		 */
1036		TableInvalidateAll(tablePtr, 0);
1037	    }
1038	    return result;
1039
1040	case TAG_DELETE:
1041	    /* delete a tag */
1042	    if (objc < 4) {
1043		Tcl_WrongNumArgs(interp, 3, objv, "tagName ?tagName ...?");
1044		return TCL_ERROR;
1045	    }
1046	    /* run through the remaining arguments */
1047	    for (i = 3; i < objc; i++) {
1048		tagname  = Tcl_GetString(objv[i]);
1049		/* cannot delete the title tag */
1050		if (STREQ(tagname, "title") ||
1051			STREQ(tagname, "sel") ||
1052			STREQ(tagname, "flash") ||
1053			STREQ(tagname, "active")) {
1054		    Tcl_AppendStringsToObj(resultPtr, "cannot delete ",
1055			    tagname, " tag", (char *) NULL);
1056		    return TCL_ERROR;
1057		}
1058		entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname);
1059		if (entryPtr != NULL) {
1060		    /* get the tag pointer */
1061		    tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
1062
1063		    /* delete all references to this tag in rows */
1064		    scanPtr = Tcl_FirstHashEntry(tablePtr->rowStyles, &search);
1065		    for (; scanPtr != NULL;
1066			 scanPtr = Tcl_NextHashEntry(&search)) {
1067			if ((TableTag *)Tcl_GetHashValue(scanPtr) == tagPtr) {
1068			    Tcl_DeleteHashEntry(scanPtr);
1069			    refresh = 1;
1070			}
1071		    }
1072
1073		    /* delete all references to this tag in cols */
1074		    scanPtr = Tcl_FirstHashEntry(tablePtr->colStyles, &search);
1075		    for (; scanPtr != NULL;
1076			 scanPtr = Tcl_NextHashEntry(&search)) {
1077			if ((TableTag *)Tcl_GetHashValue(scanPtr) == tagPtr) {
1078			    Tcl_DeleteHashEntry(scanPtr);
1079			    refresh = 1;
1080			}
1081		    }
1082
1083		    /* delete all references to this tag in cells */
1084		    scanPtr = Tcl_FirstHashEntry(tablePtr->cellStyles,
1085			    &search);
1086		    for (; scanPtr != NULL;
1087			 scanPtr = Tcl_NextHashEntry(&search)) {
1088			if ((TableTag *)Tcl_GetHashValue(scanPtr) == tagPtr) {
1089			    Tcl_DeleteHashEntry(scanPtr);
1090			    refresh = 1;
1091			}
1092		    }
1093
1094		    /*
1095		     * Remove the tag from the prio list and collapse
1096		     * the rest of the tags.  We could check for shrinking
1097		     * the prio list as well.
1098		     */
1099		    for (i = 0; i < tablePtr->tagPrioSize; i++) {
1100			if (tablePtr->tagPrios[i] == tagPtr) break;
1101		    }
1102		    for ( ; i < tablePtr->tagPrioSize; i++) {
1103			tablePtr->tagPrioNames[i] =
1104			    tablePtr->tagPrioNames[i+1];
1105			tablePtr->tagPrios[i] = tablePtr->tagPrios[i+1];
1106		    }
1107		    tablePtr->tagPrioSize--;
1108
1109		    /* Release the tag structure */
1110		    TableCleanupTag(tablePtr, tagPtr);
1111		    ckfree((char *) tagPtr);
1112
1113		    /* And free the hash table entry */
1114		    Tcl_DeleteHashEntry(entryPtr);
1115		}
1116	    }
1117	    /* since we deleted a tag, redraw the screen */
1118	    if (refresh) {
1119		TableInvalidateAll(tablePtr, 0);
1120	    }
1121	    return result;
1122
1123	case TAG_EXISTS:
1124	    if (objc != 4) {
1125		Tcl_WrongNumArgs(interp, 3, objv, "tagName");
1126		return TCL_ERROR;
1127	    }
1128	    Tcl_SetBooleanObj(resultPtr,
1129		    (Tcl_FindHashEntry(tablePtr->tagTable,
1130			    Tcl_GetString(objv[3])) != NULL));
1131	    return TCL_OK;
1132
1133	case TAG_INCLUDES:
1134	    /* does a tag contain a index ? */
1135	    if (objc != 5) {
1136		Tcl_WrongNumArgs(interp, 3, objv, "tag index");
1137		return TCL_ERROR;
1138	    }
1139	    tagname  = Tcl_GetString(objv[3]);
1140	    /* check to see if the tag actually exists */
1141	    entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname);
1142	    if (entryPtr == NULL) {
1143		/* Unknown tag, just return 0 */
1144		Tcl_SetBooleanObj(resultPtr, 0);
1145		return TCL_OK;
1146	    }
1147	    /* parse index */
1148	    if (TableGetIndexObj(tablePtr, objv[4], &row, &col) != TCL_OK) {
1149		return TCL_ERROR;
1150	    }
1151	    /* create hash key */
1152	    TableMakeArrayIndex(row, col, buf);
1153
1154	    if (STREQ(tagname, "active")) {
1155		result = (tablePtr->activeRow+tablePtr->rowOffset==row &&
1156			tablePtr->activeCol+tablePtr->colOffset==col);
1157	    } else if (STREQ(tagname, "flash")) {
1158		result = (tablePtr->flashMode &&
1159			(Tcl_FindHashEntry(tablePtr->flashCells, buf)
1160				!= NULL));
1161	    } else if (STREQ(tagname, "sel")) {
1162		result = (Tcl_FindHashEntry(tablePtr->selCells, buf) != NULL);
1163	    } else if (STREQ(tagname, "title")) {
1164		result = (row < tablePtr->titleRows+tablePtr->rowOffset ||
1165			col < tablePtr->titleCols+tablePtr->colOffset);
1166	    } else {
1167		/* get the pointer to the tag structure */
1168		tagPtr = (TableTag *) Tcl_GetHashValue(entryPtr);
1169		scanPtr = Tcl_FindHashEntry(tablePtr->cellStyles, buf);
1170		/*
1171		 * Look to see if there is a cell, row, or col tag
1172		 * for this cell
1173		 */
1174		result = ((scanPtr &&
1175			(tagPtr == (TableTag *) Tcl_GetHashValue(scanPtr))) ||
1176			(tagPtr == FindRowColTag(tablePtr, row, ROW)) ||
1177			(tagPtr == FindRowColTag(tablePtr, col, COL)));
1178	    }
1179	    /*
1180	     * Because we may call FindRowColTag above, we can't use
1181	     * the resultPtr, but this is almost equivalent, and is SAFE
1182	     */
1183	    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result));
1184	    return TCL_OK;
1185
1186	case TAG_NAMES:
1187	    /*
1188	     * Print out the tag names in priority order
1189	     */
1190	    if (objc < 3 || objc > 4) {
1191		Tcl_WrongNumArgs(interp, 3, objv, "?pattern?");
1192		return TCL_ERROR;
1193	    }
1194	    tagname = (objc == 4) ? Tcl_GetString(objv[3]) : NULL;
1195	    for (i = 0; i < tablePtr->tagPrioSize; i++) {
1196		keybuf = tablePtr->tagPrioNames[i];
1197		if (objc == 3 || Tcl_StringMatch(keybuf, tagname)) {
1198		    objPtr = Tcl_NewStringObj(keybuf, -1);
1199		    Tcl_ListObjAppendElement(NULL, resultPtr, objPtr);
1200		}
1201	    }
1202	    return TCL_OK;
1203
1204	case TAG_LOWER:
1205	case TAG_RAISE:
1206	    /*
1207	     * Change priority of the named tag
1208	     */
1209	    if (objc != 4 && objc != 5) {
1210		Tcl_WrongNumArgs(interp, 3, objv, (cmdIndex == TAG_LOWER) ?
1211			"tagName ?belowThis?" : "tagName ?aboveThis?");
1212		return TCL_ERROR;
1213	    }
1214	    tagname  = Tcl_GetString(objv[3]);
1215	    /* check to see if the tag actually exists */
1216	    entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname);
1217	    if (entryPtr == NULL) {
1218		goto invalidtag;
1219	    }
1220	    tagPtr  = (TableTag *) Tcl_GetHashValue(entryPtr);
1221	    tagPrio = TableTagGetPriority(tablePtr, tagPtr);
1222	    keybuf  = tablePtr->tagPrioNames[tagPrio];
1223	    /*
1224	     * In the RAISE case, the priority is one higher (-1) because
1225	     * we want the named tag to move above the other in priority.
1226	     */
1227	    if (objc == 5) {
1228		tagname  = Tcl_GetString(objv[4]);
1229		entryPtr = Tcl_FindHashEntry(tablePtr->tagTable, tagname);
1230		if (entryPtr == NULL) {
1231		    goto invalidtag;
1232		}
1233		tag2Ptr  = (TableTag *) Tcl_GetHashValue(entryPtr);
1234		if (cmdIndex == TAG_LOWER) {
1235		    value = TableTagGetPriority(tablePtr, tag2Ptr);
1236		} else {
1237		    value = TableTagGetPriority(tablePtr, tag2Ptr) - 1;
1238		}
1239	    } else {
1240		if (cmdIndex == TAG_LOWER) {
1241		    /*
1242		     * Lower this tag's priority to the bottom.
1243		     */
1244		    value = tablePtr->tagPrioSize - 1;
1245		} else {
1246		    /*
1247		     * Raise this tag's priority to the top.
1248		     */
1249		    value = -1;
1250		}
1251	    }
1252	    if (value < tagPrio) {
1253		/*
1254		 * Move tag up in priority.
1255		 */
1256		for (i = tagPrio; i > value; i--) {
1257		    tablePtr->tagPrioNames[i] = tablePtr->tagPrioNames[i-1];
1258		    tablePtr->tagPrios[i]     = tablePtr->tagPrios[i-1];
1259		}
1260		i++;
1261		tablePtr->tagPrioNames[i] = keybuf;
1262		tablePtr->tagPrios[i]     = tagPtr;
1263		refresh = 1;
1264	    } else if (value > tagPrio) {
1265		/*
1266		 * Move tag down in priority.
1267		 */
1268		for (i = tagPrio; i < value; i++) {
1269		    tablePtr->tagPrioNames[i] = tablePtr->tagPrioNames[i+1];
1270		    tablePtr->tagPrios[i]     = tablePtr->tagPrios[i+1];
1271		}
1272		tablePtr->tagPrioNames[i] = keybuf;
1273		tablePtr->tagPrios[i]     = tagPtr;
1274		refresh = 1;
1275	    }
1276	    /* since we deleted a tag, redraw the screen */
1277	    if (refresh) {
1278		TableInvalidateAll(tablePtr, 0);
1279	    }
1280	    return TCL_OK;
1281
1282    }
1283    return TCL_OK;
1284
1285    invalidtag:
1286    /*
1287     * When jumping here, ensure the invalid 'tagname' is set already.
1288     */
1289    Tcl_AppendStringsToObj(resultPtr, "invalid tag name \"",
1290	    tagname, "\"", (char *) NULL);
1291    return TCL_ERROR;
1292}
1293
1294/*
1295 *----------------------------------------------------------------------
1296 *
1297 * TableOptionReliefSet --
1298 *
1299 *	This routine configures the borderwidth value for a tag.
1300 *
1301 * Results:
1302 *	A standard Tcl result.
1303 *
1304 * Side effects:
1305 *	It may adjust the tag struct values of relief[0..4] and borders.
1306 *
1307 *----------------------------------------------------------------------
1308 */
1309
1310static int
1311TableOptionReliefSet(clientData, interp, tkwin, value, widgRec, offset)
1312    ClientData clientData;		/* Type of struct being set. */
1313    Tcl_Interp *interp;			/* Used for reporting errors. */
1314    Tk_Window tkwin;			/* Window containing table widget. */
1315    CONST84 char *value;		/* Value of option. */
1316    char *widgRec;			/* Pointer to record for item. */
1317    int offset;				/* Offset into item. */
1318{
1319    TableTag *tagPtr = (TableTag *) widgRec;
1320
1321    if (*value == '\0') {
1322	tagPtr->relief = -1;
1323    } else {
1324	return Tk_GetRelief(interp, value, &(tagPtr->relief));
1325    }
1326    return TCL_OK;
1327}
1328
1329/*
1330 *----------------------------------------------------------------------
1331 *
1332 * TableOptionReliefGet --
1333 *
1334 * Results:
1335 *	Value of the tag's -relief option.
1336 *
1337 * Side effects:
1338 *	None.
1339 *
1340 *----------------------------------------------------------------------
1341 */
1342
1343static char *
1344TableOptionReliefGet(clientData, tkwin, widgRec, offset, freeProcPtr)
1345    ClientData clientData;		/* Type of struct being set. */
1346    Tk_Window tkwin;			/* Window containing canvas widget. */
1347    char *widgRec;			/* Pointer to record for item. */
1348    int offset;				/* Offset into item. */
1349    Tcl_FreeProc **freeProcPtr;		/* Pointer to variable to fill in with
1350					 * information about how to reclaim
1351					 * storage for return string. */
1352{
1353    return (char *) Tk_NameOfRelief(((TableTag *) widgRec)->relief);
1354}
1355