1/*
2 * tkTableUtil.c --
3 *
4 *	This module contains utility functions for table widgets.
5 *
6 * Copyright (c) 2000-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: tkTableUtil.c,v 1.4 2002/10/16 07:31:48 hobbs Exp $
12 */
13
14#include "tkTable.h"
15
16static char *	Cmd_GetName _ANSI_ARGS_((const Cmd_Struct *cmds, int val));
17static int	Cmd_GetValue _ANSI_ARGS_((const Cmd_Struct *cmds,
18			const char *arg));
19static void	Cmd_GetError _ANSI_ARGS_((Tcl_Interp *interp,
20			const Cmd_Struct *cmds, const char *arg));
21
22/*
23 *--------------------------------------------------------------
24 *
25 * Table_ClearHashTable --
26 *	This procedure is invoked to clear a STRING_KEY hash table,
27 *	freeing the string entries and then deleting the hash table.
28 *	The hash table cannot be used after calling this, except to
29 *	be freed or reinitialized.
30 *
31 * Results:
32 *	Cached info will be lost.
33 *
34 * Side effects:
35 *	Can cause redraw.
36 *	See the user documentation.
37 *
38 *--------------------------------------------------------------
39 */
40void
41Table_ClearHashTable(Tcl_HashTable *hashTblPtr)
42{
43    Tcl_HashEntry *entryPtr;
44    Tcl_HashSearch search;
45    char *value;
46
47    for (entryPtr = Tcl_FirstHashEntry(hashTblPtr, &search);
48	 entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&search)) {
49	value = (char *) Tcl_GetHashValue(entryPtr);
50	if (value != NULL) ckfree(value);
51    }
52
53    Tcl_DeleteHashTable(hashTblPtr);
54}
55
56/*
57 *----------------------------------------------------------------------
58 *
59 * TableOptionBdSet --
60 *
61 *	This routine configures the borderwidth value for a tag.
62 *
63 * Results:
64 *	A standard Tcl result.
65 *
66 * Side effects:
67 *	It may adjust the tag struct values of bd[0..4] and borders.
68 *
69 *----------------------------------------------------------------------
70 */
71
72int
73TableOptionBdSet(clientData, interp, tkwin, value, widgRec, offset)
74    ClientData clientData;		/* Type of struct being set. */
75    Tcl_Interp *interp;			/* Used for reporting errors. */
76    Tk_Window tkwin;			/* Window containing table widget. */
77    CONST84 char *value;		/* Value of option. */
78    char *widgRec;			/* Pointer to record for item. */
79    int offset;				/* Offset into item. */
80{
81    char **borderStr;
82    int *bordersPtr, *bdPtr;
83    int type	= (int) clientData;
84    int result	= TCL_OK;
85    int argc;
86    CONST84 char **argv;
87
88    if ((type == BD_TABLE) && (value[0] == '\0')) {
89	/*
90	 * NULL strings aren't allowed for the table global -bd
91	 */
92	Tcl_AppendResult(interp, "borderwidth value may not be empty",
93		(char *) NULL);
94	return TCL_ERROR;
95    }
96
97    if ((type == BD_TABLE) || (type == BD_TABLE_TAG)) {
98	TableTag *tagPtr = (TableTag *) (widgRec + offset);
99	borderStr	= &(tagPtr->borderStr);
100	bordersPtr	= &(tagPtr->borders);
101	bdPtr		= tagPtr->bd;
102    } else if (type == BD_TABLE_WIN) {
103	TableEmbWindow *tagPtr = (TableEmbWindow *) widgRec;
104	borderStr	= &(tagPtr->borderStr);
105	bordersPtr	= &(tagPtr->borders);
106	bdPtr		= tagPtr->bd;
107    } else {
108	panic("invalid type given to TableOptionBdSet\n");
109	return TCL_ERROR; /* lint */
110    }
111
112    result = Tcl_SplitList(interp, value, &argc, &argv);
113    if (result == TCL_OK) {
114	int i, bd[4];
115
116	if (((type == BD_TABLE) && (argc == 0)) || (argc == 3) || (argc > 4)) {
117	    Tcl_AppendResult(interp,
118		    "1, 2 or 4 values must be specified for borderwidth",
119		    (char *) NULL);
120	    result = TCL_ERROR;
121	} else {
122	    /*
123	     * We use the shadow bd array first, in case we have an error
124	     * parsing arguments half way through.
125	     */
126	    for (i = 0; i < argc; i++) {
127		if (Tk_GetPixels(interp, tkwin, argv[i], &(bd[i])) != TCL_OK) {
128		    result = TCL_ERROR;
129		    break;
130		}
131	    }
132	    /*
133	     * If everything is OK, store the parsed and given values for
134	     * easy retrieval.
135	     */
136	    if (result == TCL_OK) {
137		for (i = 0; i < argc; i++) {
138		    bdPtr[i] = MAX(0, bd[i]);
139		}
140		if (*borderStr) {
141		    ckfree(*borderStr);
142		}
143		if (value) {
144		    *borderStr	= (char *) ckalloc(strlen(value) + 1);
145		    strcpy(*borderStr, value);
146		} else {
147		    *borderStr	= NULL;
148		}
149		*bordersPtr	= argc;
150	    }
151	}
152	ckfree ((char *) argv);
153    }
154
155    return result;
156}
157
158/*
159 *----------------------------------------------------------------------
160 *
161 * TableOptionBdGet --
162 *
163 * Results:
164 *	Value of the -bd option.
165 *
166 * Side effects:
167 *	None.
168 *
169 *----------------------------------------------------------------------
170 */
171
172char *
173TableOptionBdGet(clientData, tkwin, widgRec, offset, freeProcPtr)
174    ClientData clientData;		/* Type of struct being set. */
175    Tk_Window tkwin;			/* Window containing canvas widget. */
176    char *widgRec;			/* Pointer to record for item. */
177    int offset;				/* Offset into item. */
178    Tcl_FreeProc **freeProcPtr;		/* Pointer to variable to fill in with
179					 * information about how to reclaim
180					 * storage for return string. */
181{
182    register int type	= (int) clientData;
183
184    if (type == BD_TABLE) {
185	return ((TableTag *) (widgRec + offset))->borderStr;
186    } else if (type == BD_TABLE_TAG) {
187	return ((TableTag *) widgRec)->borderStr;
188    } else if (type == BD_TABLE_WIN) {
189	return ((TableEmbWindow *) widgRec)->borderStr;
190    } else {
191	panic("invalid type given to TableOptionBdSet\n");
192	return NULL; /* lint */
193    }
194}
195
196/*
197 *----------------------------------------------------------------------
198 *
199 * TableTagConfigureBd --
200 *	This routine configures the border values based on a tag.
201 *	The previous value of the bd string (oldValue) is assumed to
202 *	be a valid value for this tag.
203 *
204 * Results:
205 *	A standard Tcl result.
206 *
207 * Side effects:
208 *	It may adjust the value used by -bd.
209 *
210 *----------------------------------------------------------------------
211 */
212
213int
214TableTagConfigureBd(Table *tablePtr, TableTag *tagPtr,
215	char *oldValue, int nullOK)
216{
217    int i, argc, result = TCL_OK;
218    CONST84 char **argv;
219
220    /*
221     * First check to see if the value really changed.
222     */
223    if (strcmp(tagPtr->borderStr ? tagPtr->borderStr : "",
224	    oldValue ? oldValue : "") == 0) {
225	return TCL_OK;
226    }
227
228    tagPtr->borders = 0;
229    if (!nullOK && ((tagPtr->borderStr == NULL)
230	    || (*(tagPtr->borderStr) == '\0'))) {
231	/*
232	 * NULL strings aren't allowed for this tag
233	 */
234	result = TCL_ERROR;
235    } else if (tagPtr->borderStr) {
236	result = Tcl_SplitList(tablePtr->interp, tagPtr->borderStr,
237		&argc, &argv);
238	if (result == TCL_OK) {
239	    if ((!nullOK && (argc == 0)) || (argc == 3) || (argc > 4)) {
240		Tcl_SetResult(tablePtr->interp,
241			"1, 2 or 4 values must be specified to -borderwidth",
242			TCL_STATIC);
243		result = TCL_ERROR;
244	    } else {
245		for (i = 0; i < argc; i++) {
246		    if (Tk_GetPixels(tablePtr->interp, tablePtr->tkwin,
247			    argv[i], &(tagPtr->bd[i])) != TCL_OK) {
248			result = TCL_ERROR;
249			break;
250		    }
251		    tagPtr->bd[i] = MAX(0, tagPtr->bd[i]);
252		}
253		tagPtr->borders = argc;
254	    }
255	    ckfree ((char *) argv);
256	}
257    }
258
259    if (result != TCL_OK) {
260	if (tagPtr->borderStr) {
261	    ckfree ((char *) tagPtr->borderStr);
262	}
263	if (oldValue != NULL) {
264	    size_t length = strlen(oldValue) + 1;
265	    /*
266	     * We are making the assumption that oldValue is correct.
267	     * We have to reparse in case the bad new value had a couple
268	     * of correct args before failing on a bad pixel value.
269	     */
270	    Tcl_SplitList(tablePtr->interp, oldValue, &argc, &argv);
271	    for (i = 0; i < argc; i++) {
272		Tk_GetPixels(tablePtr->interp, tablePtr->tkwin,
273			argv[i], &(tagPtr->bd[i]));
274	    }
275	    ckfree ((char *) argv);
276	    tagPtr->borders	= argc;
277	    tagPtr->borderStr	= (char *) ckalloc(length);
278	    memcpy(tagPtr->borderStr, oldValue, length);
279	} else {
280	    tagPtr->borders	= 0;
281	    tagPtr->borderStr	= (char *) NULL;
282	}
283    }
284
285    return result;
286}
287
288/*
289 *----------------------------------------------------------------------
290 *
291 * Cmd_OptionSet --
292 *
293 *
294 * Results:
295 *	A standard Tcl result.
296 *
297 * Side effects:
298 *	None.
299 *
300 *----------------------------------------------------------------------
301 */
302
303int
304Cmd_OptionSet(ClientData clientData, Tcl_Interp *interp,
305	Tk_Window unused, CONST84 char *value, char *widgRec, int offset)
306{
307  Cmd_Struct *p = (Cmd_Struct *)clientData;
308  int mode = Cmd_GetValue(p,value);
309  if (!mode) {
310    Cmd_GetError(interp,p,value);
311    return TCL_ERROR;
312  }
313  *((int*)(widgRec+offset)) = mode;
314  return TCL_OK;
315}
316
317/*
318 *----------------------------------------------------------------------
319 *
320 * Cmd_OptionGet --
321 *
322 *
323 * Results:
324 *	Value of the option.
325 *
326 * Side effects:
327 *	None.
328 *
329 *----------------------------------------------------------------------
330 */
331
332char *
333Cmd_OptionGet(ClientData clientData, Tk_Window unused,
334	      char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)
335{
336  Cmd_Struct *p = (Cmd_Struct *)clientData;
337  int mode = *((int*)(widgRec+offset));
338  return Cmd_GetName(p,mode);
339}
340
341/*
342 * simple Cmd_Struct lookup functions
343 */
344
345char *
346Cmd_GetName(const Cmd_Struct *cmds, int val)
347{
348  for(;cmds->name && cmds->name[0];cmds++) {
349    if (cmds->value==val) return cmds->name;
350  }
351  return NULL;
352}
353
354int
355Cmd_GetValue(const Cmd_Struct *cmds, const char *arg)
356{
357  unsigned int len = strlen(arg);
358  for(;cmds->name && cmds->name[0];cmds++) {
359    if (!strncmp(cmds->name, arg, len)) return cmds->value;
360  }
361  return 0;
362}
363
364void
365Cmd_GetError(Tcl_Interp *interp, const Cmd_Struct *cmds, const char *arg)
366{
367  int i;
368  Tcl_AppendResult(interp, "bad option \"", arg, "\" must be ", (char *) 0);
369  for(i=0;cmds->name && cmds->name[0];cmds++,i++) {
370    Tcl_AppendResult(interp, (i?", ":""), cmds->name, (char *) 0);
371  }
372}
373