4 * This file contains the Ck_ConfigureWidget procedure.
6 * Copyright (c) 1990-1994 The Regents of the University of California.
7 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
8 * Copyright (c) 1995 Christian Werner
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
18 * Values for "flags" field of Ck_ConfigSpec structures. Be sure
19 * to coordinate these values with those defined in ck.h
20 * (CK_CONFIG_*). There must not be overlap!
22 * INIT - Non-zero means (char *) things have been
23 * converted to Ck_Uid's.
29 * Forward declarations for procedures defined later in this file:
32 static int DoConfig _ANSI_ARGS_((Tcl_Interp *interp,
33 CkWindow *winPtr, Ck_ConfigSpec *specPtr,
34 Ck_Uid value, int valueIsUid, char *widgRec));
35 static Ck_ConfigSpec * FindConfigSpec _ANSI_ARGS_ ((Tcl_Interp *interp,
36 Ck_ConfigSpec *specs, char *argvName,
37 int needFlags, int hateFlags));
38 static char * FormatConfigInfo _ANSI_ARGS_ ((Tcl_Interp *interp,
39 CkWindow *winPtr, Ck_ConfigSpec *specPtr,
41 static char * FormatConfigValue _ANSI_ARGS_((Tcl_Interp *interp,
42 CkWindow *tkwin, Ck_ConfigSpec *specPtr,
43 char *widgRec, char *buffer,
44 Tcl_FreeProc **freeProcPtr));
47 *--------------------------------------------------------------
49 * Ck_ConfigureWidget --
51 * Process command-line options to fill in fields of a
52 * widget record with resources and other parameters.
55 * A standard Tcl return value. In case of an error,
56 * interp->result will hold an error message.
59 * The fields of widgRec get filled in with information
60 * from argc/argv. Old information in widgRec's fields
63 *--------------------------------------------------------------
67 Ck_ConfigureWidget(interp, winPtr, specs, argc, argv, widgRec, flags)
68 Tcl_Interp *interp; /* Interpreter for error reporting. */
69 CkWindow *winPtr; /* Window containing widget. */
70 Ck_ConfigSpec *specs; /* Describes legal options. */
71 int argc; /* Number of elements in argv. */
72 char **argv; /* Command-line options. */
73 char *widgRec; /* Record whose fields are to be
74 * modified. Values must be properly
76 int flags; /* Used to specify additional flags
77 * that must be present in config specs
78 * for them to be considered. Also,
79 * may have CK_CONFIG_ARGV_ONLY set. */
81 Ck_ConfigSpec *specPtr;
82 Ck_Uid value; /* Value of option from database. */
83 int needFlags; /* Specs must contain this set of flags
84 * or else they are not considered. */
85 int hateFlags; /* If a spec contains any bits here, it's
88 needFlags = flags & ~(CK_CONFIG_USER_BIT - 1);
89 if (!(winPtr->mainPtr->flags & CK_HAS_COLOR)) {
90 hateFlags = CK_CONFIG_COLOR_ONLY;
92 hateFlags = CK_CONFIG_MONO_ONLY;
96 * Pass one: scan through all the option specs, replacing strings
97 * with Ck_Uids (if this hasn't been done already) and clearing
98 * the CK_CONFIG_OPTION_SPECIFIED flags.
101 for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
102 if (!(specPtr->specFlags & INIT) && (specPtr->argvName != NULL)) {
103 if (specPtr->dbName != NULL) {
104 specPtr->dbName = Ck_GetUid(specPtr->dbName);
106 if (specPtr->dbClass != NULL) {
107 specPtr->dbClass = Ck_GetUid(specPtr->dbClass);
109 if (specPtr->defValue != NULL) {
110 specPtr->defValue = Ck_GetUid(specPtr->defValue);
113 specPtr->specFlags = (specPtr->specFlags & ~CK_CONFIG_OPTION_SPECIFIED)
118 * Pass two: scan through all of the arguments, processing those
119 * that match entries in the specs.
122 for ( ; argc > 0; argc -= 2, argv += 2) {
123 specPtr = FindConfigSpec(interp, specs, *argv, needFlags, hateFlags);
124 if (specPtr == NULL) {
133 Tcl_AppendResult(interp, "value for \"", *argv,
134 "\" missing", (char *) NULL);
137 if (DoConfig(interp, winPtr, specPtr, argv[1], 0, widgRec) != TCL_OK) {
140 sprintf(msg, "\n (processing \"%.40s\" option)",
142 Tcl_AddErrorInfo(interp, msg);
145 specPtr->specFlags |= CK_CONFIG_OPTION_SPECIFIED;
149 * Pass three: scan through all of the specs again; if no
150 * command-line argument matched a spec, then check for info
151 * in the option database. If there was nothing in the
152 * database, then use the default.
155 if (!(flags & CK_CONFIG_ARGV_ONLY)) {
156 for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
157 if ((specPtr->specFlags & CK_CONFIG_OPTION_SPECIFIED)
158 || (specPtr->argvName == NULL)
159 || (specPtr->type == CK_CONFIG_SYNONYM)) {
162 if (((specPtr->specFlags & needFlags) != needFlags)
163 || (specPtr->specFlags & hateFlags)) {
167 if (specPtr->dbName != NULL) {
168 value = Ck_GetOption(winPtr, specPtr->dbName,
172 if (DoConfig(interp, winPtr, specPtr, value, 1, widgRec) !=
176 sprintf(msg, "\n (%s \"%.50s\" in widget \"%.50s\")",
177 "database entry for",
178 specPtr->dbName, winPtr->pathName);
179 Tcl_AddErrorInfo(interp, msg);
183 value = specPtr->defValue;
184 if ((value != NULL) && !(specPtr->specFlags
185 & CK_CONFIG_DONT_SET_DEFAULT)) {
186 if (DoConfig(interp, winPtr, specPtr, value, 1, widgRec) !=
191 "\n (%s \"%.50s\" in widget \"%.50s\")",
193 specPtr->dbName, winPtr->pathName);
194 Tcl_AddErrorInfo(interp, msg);
205 *--------------------------------------------------------------
209 * Search through a table of configuration specs, looking for
210 * one that matches a given argvName.
213 * The return value is a pointer to the matching entry, or NULL
214 * if nothing matched. In that case an error message is left
220 *--------------------------------------------------------------
223 static Ck_ConfigSpec *
224 FindConfigSpec(interp, specs, argvName, needFlags, hateFlags)
225 Tcl_Interp *interp; /* Used for reporting errors. */
226 Ck_ConfigSpec *specs; /* Pointer to table of configuration
227 * specifications for a widget. */
228 char *argvName; /* Name (suitable for use in a "config"
229 * command) identifying particular option. */
230 int needFlags; /* Flags that must be present in matching
232 int hateFlags; /* Flags that must NOT be present in
235 Ck_ConfigSpec *specPtr;
236 char c; /* First character of current argument. */
237 Ck_ConfigSpec *matchPtr; /* Matching spec, or NULL. */
241 length = strlen(argvName);
243 for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
244 if (specPtr->argvName == NULL) {
247 if ((specPtr->argvName[1] != c)
248 || (strncmp(specPtr->argvName, argvName, length) != 0)) {
251 if (((specPtr->specFlags & needFlags) != needFlags)
252 || (specPtr->specFlags & hateFlags)) {
255 if (specPtr->argvName[length] == 0) {
259 if (matchPtr != NULL) {
260 Tcl_AppendResult(interp, "ambiguous option \"", argvName,
261 "\"", (char *) NULL);
262 return (Ck_ConfigSpec *) NULL;
267 if (matchPtr == NULL) {
268 Tcl_AppendResult(interp, "unknown option \"", argvName,
269 "\"", (char *) NULL);
270 return (Ck_ConfigSpec *) NULL;
274 * Found a matching entry. If it's a synonym, then find the
275 * entry that it's a synonym for.
280 if (specPtr->type == CK_CONFIG_SYNONYM) {
281 for (specPtr = specs; ; specPtr++) {
282 if (specPtr->type == CK_CONFIG_END) {
283 Tcl_AppendResult(interp,
284 "couldn't find synonym for option \"",
285 argvName, "\"", (char *) NULL);
286 return (Ck_ConfigSpec *) NULL;
288 if ((specPtr->dbName == matchPtr->dbName)
289 && (specPtr->type != CK_CONFIG_SYNONYM)
290 && ((specPtr->specFlags & needFlags) == needFlags)
291 && !(specPtr->specFlags & hateFlags)) {
300 *--------------------------------------------------------------
304 * This procedure applies a single configuration option
305 * to a widget record.
308 * A standard Tcl return value.
311 * WidgRec is modified as indicated by specPtr and value.
312 * The old value is recycled, if that is appropriate for
315 *--------------------------------------------------------------
319 DoConfig(interp, winPtr, specPtr, value, valueIsUid, widgRec)
320 Tcl_Interp *interp; /* Interpreter for error reporting. */
321 CkWindow *winPtr; /* Window containing widget. */
322 Ck_ConfigSpec *specPtr; /* Specifier to apply. */
323 char *value; /* Value to use to fill in widgRec. */
324 int valueIsUid; /* Non-zero means value is a Tk_Uid;
325 * zero means it's an ordinary string. */
326 char *widgRec; /* Record whose fields are to be
327 * modified. Values must be properly
335 if ((*value == 0) && (specPtr->specFlags & CK_CONFIG_NULL_OK)) {
340 ptr = widgRec + specPtr->offset;
341 switch (specPtr->type) {
342 case CK_CONFIG_BOOLEAN:
343 if (Tcl_GetBoolean(interp, value, (int *) ptr) != TCL_OK) {
348 if (Tcl_GetInt(interp, value, (int *) ptr) != TCL_OK) {
352 case CK_CONFIG_DOUBLE:
353 if (Tcl_GetDouble(interp, value, (double *) ptr) != TCL_OK) {
357 case CK_CONFIG_STRING: {
363 new = (char *) ckalloc((unsigned) (strlen(value) + 1));
366 old = *((char **) ptr);
370 *((char **) ptr) = new;
375 *((Ck_Uid *) ptr) = NULL;
377 uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
378 *((Ck_Uid *) ptr) = uid;
381 case CK_CONFIG_COLOR: {
384 uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
385 if (Ck_GetColor(interp, (char *) value, &color) != TCL_OK)
387 *((int *) ptr) = color;
390 case CK_CONFIG_BORDER: {
396 uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
397 new = Ck_GetBorder(interp, uid);
402 old = *((CkBorder **) ptr);
406 *((CkBorder **) ptr) = new;
409 case CK_CONFIG_JUSTIFY:
410 uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
411 if (Ck_GetJustify(interp, uid, (Ck_Justify *) ptr) != TCL_OK) {
415 case CK_CONFIG_ANCHOR:
416 uid = valueIsUid ? (Ck_Uid) value : Ck_GetUid(value);
417 if (Ck_GetAnchor(interp, uid, (Ck_Anchor *) ptr) != TCL_OK) {
421 case CK_CONFIG_COORD:
422 if (Ck_GetCoord(interp, winPtr, value, (int *) ptr) != TCL_OK)
426 if (Ck_GetAttr(interp, value, (int *) ptr) != TCL_OK)
429 case CK_CONFIG_WINDOW: {
435 winPtr2 = Ck_NameToWindow(interp, value, winPtr);
436 if (winPtr2 == NULL) {
440 *((CkWindow **) ptr) = winPtr2;
443 case CK_CONFIG_CUSTOM:
444 if ((*specPtr->customPtr->parseProc)(
445 specPtr->customPtr->clientData, interp, winPtr,
446 value, widgRec, specPtr->offset) != TCL_OK) {
451 sprintf(interp->result, "bad config table: unknown type %d",
457 } while ((specPtr->argvName == NULL) && (specPtr->type != CK_CONFIG_END));
462 *--------------------------------------------------------------
464 * Ck_ConfigureInfo --
466 * Return information about the configuration options
467 * for a window, and their current values.
470 * Always returns TCL_OK. Interp->result will be modified
471 * hold a description of either a single configuration option
472 * available for "widgRec" via "specs", or all the configuration
473 * options available. In the "all" case, the result will
474 * available for "widgRec" via "specs". The result will
475 * be a list, each of whose entries describes one option.
476 * Each entry will itself be a list containing the option's
477 * name for use on command lines, database name, database
478 * class, default value, and current value (empty string
479 * if none). For options that are synonyms, the list will
480 * contain only two values: name and synonym name. If the
481 * "name" argument is non-NULL, then the only information
482 * returned is that for the named argument (i.e. the corresponding
483 * entry in the overall list is returned).
488 *--------------------------------------------------------------
492 Ck_ConfigureInfo(interp, winPtr, specs, widgRec, argvName, flags)
493 Tcl_Interp *interp; /* Interpreter for error reporting. */
494 CkWindow *winPtr; /* Window corresponding to widgRec. */
495 Ck_ConfigSpec *specs; /* Describes legal options. */
496 char *widgRec; /* Record whose fields contain current
497 * values for options. */
498 char *argvName; /* If non-NULL, indicates a single option
499 * whose info is to be returned. Otherwise
500 * info is returned for all options. */
501 int flags; /* Used to specify additional flags
502 * that must be present in config specs
503 * for them to be considered. */
505 Ck_ConfigSpec *specPtr;
506 int needFlags, hateFlags;
510 needFlags = flags & ~(CK_CONFIG_USER_BIT - 1);
511 if (!(winPtr->mainPtr->flags & CK_HAS_COLOR)) {
512 hateFlags = CK_CONFIG_COLOR_ONLY;
514 hateFlags = CK_CONFIG_MONO_ONLY;
518 * If information is only wanted for a single configuration
519 * spec, then handle that one spec specially.
522 Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
523 if (argvName != NULL) {
524 specPtr = FindConfigSpec(interp, specs, argvName, needFlags,
526 if (specPtr == NULL) {
529 interp->result = FormatConfigInfo(interp, winPtr, specPtr, widgRec);
530 interp->freeProc = (Tcl_FreeProc *) free;
535 * Loop through all the specs, creating a big list with all
539 for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
540 if ((argvName != NULL) && (specPtr->argvName != argvName)) {
543 if (((specPtr->specFlags & needFlags) != needFlags)
544 || (specPtr->specFlags & hateFlags)) {
547 if (specPtr->argvName == NULL) {
550 list = FormatConfigInfo(interp, winPtr, specPtr, widgRec);
551 Tcl_AppendResult(interp, leader, list, "}", (char *) NULL);
558 *----------------------------------------------------------------------
560 * Ck_ConfigureValue --
562 * This procedure returns the current value of a configuration
563 * option for a widget.
566 * The return value is a standard Tcl completion code (TCL_OK or
567 * TCL_ERROR). Interp->result will be set to hold either the value
568 * of the option given by argvName (if TCL_OK is returned) or
569 * an error message (if TCL_ERROR is returned).
574 *----------------------------------------------------------------------
578 Ck_ConfigureValue(interp, winPtr, specs, widgRec, argvName, flags)
579 Tcl_Interp *interp; /* Interpreter for error reporting. */
580 CkWindow *winPtr; /* Window corresponding to widgRec. */
581 Ck_ConfigSpec *specs; /* Describes legal options. */
582 char *widgRec; /* Record whose fields contain current
583 * values for options. */
584 char *argvName; /* Gives the command-line name for the
585 * option whose value is to be returned. */
586 int flags; /* Used to specify additional flags
587 * that must be present in config specs
588 * for them to be considered. */
590 Ck_ConfigSpec *specPtr;
591 int needFlags, hateFlags;
593 needFlags = flags & ~(CK_CONFIG_USER_BIT - 1);
594 if (winPtr->mainPtr->flags & CK_HAS_COLOR)
595 hateFlags = CK_CONFIG_MONO_ONLY;
597 hateFlags = CK_CONFIG_COLOR_ONLY;
598 specPtr = FindConfigSpec(interp, specs, argvName, needFlags, hateFlags);
599 if (specPtr == NULL) {
602 interp->result = FormatConfigValue(interp, winPtr, specPtr, widgRec,
603 interp->result, &interp->freeProc);
608 *--------------------------------------------------------------
610 * FormatConfigInfo --
612 * Create a valid Tcl list holding the configuration information
613 * for a single configuration option.
616 * A Tcl list, dynamically allocated. The caller is expected to
617 * arrange for this list to be freed eventually.
620 * Memory is allocated.
622 *--------------------------------------------------------------
626 FormatConfigInfo(interp, winPtr, specPtr, widgRec)
627 Tcl_Interp *interp; /* Interpreter to use for things
628 * like floating-point precision. */
629 CkWindow *winPtr; /* Window corresponding to widget. */
630 Ck_ConfigSpec *specPtr; /* Pointer to information describing
632 char *widgRec; /* Pointer to record holding current
633 * values of info for widget. */
635 char *argv[6], *result;
637 Tcl_FreeProc *freeProc = (Tcl_FreeProc *) NULL;
639 argv[0] = specPtr->argvName;
640 argv[1] = specPtr->dbName;
641 argv[2] = specPtr->dbClass;
642 argv[3] = specPtr->defValue;
643 if (specPtr->type == CK_CONFIG_SYNONYM) {
644 return Tcl_Merge(2, argv);
646 argv[4] = FormatConfigValue(interp, winPtr, specPtr, widgRec, buffer,
648 if (argv[1] == NULL) {
651 if (argv[2] == NULL) {
654 if (argv[3] == NULL) {
657 if (argv[4] == NULL) {
660 result = Tcl_Merge(5, argv);
661 if (freeProc != NULL) {
662 if (freeProc == (Tcl_FreeProc *) free) {
665 (*freeProc)(argv[4]);
672 *----------------------------------------------------------------------
674 * FormatConfigValue --
676 * This procedure formats the current value of a configuration
680 * The return value is the formatted value of the option given
681 * by specPtr and widgRec. If the value is static, so that it
682 * need not be freed, *freeProcPtr will be set to NULL; otherwise
683 * *freeProcPtr will be set to the address of a procedure to
684 * free the result, and the caller must invoke this procedure
685 * when it is finished with the result.
690 *----------------------------------------------------------------------
694 FormatConfigValue(interp, winPtr, specPtr, widgRec, buffer, freeProcPtr)
695 Tcl_Interp *interp; /* Interpreter for use in real conversions. */
696 CkWindow *winPtr; /* Window corresponding to widget. */
697 Ck_ConfigSpec *specPtr; /* Pointer to information describing option.
698 * Must not point to a synonym option. */
699 char *widgRec; /* Pointer to record holding current
700 * values of info for widget. */
701 char *buffer; /* Static buffer to use for small values.
702 * Must have at least 200 bytes of storage. */
703 Tcl_FreeProc **freeProcPtr; /* Pointer to word to fill in with address
704 * of procedure to free the result, or NULL
705 * if result is static. */
710 ptr = widgRec + specPtr->offset;
712 switch (specPtr->type) {
713 case CK_CONFIG_BOOLEAN:
714 if (*((int *) ptr) == 0) {
721 case CK_CONFIG_COORD:
722 sprintf(buffer, "%d", *((int *) ptr));
725 case CK_CONFIG_DOUBLE:
726 Tcl_PrintDouble(interp, *((double *) ptr), buffer);
729 case CK_CONFIG_STRING:
730 result = (*(char **) ptr);
734 case CK_CONFIG_UID: {
735 Ck_Uid uid = *((Ck_Uid *) ptr);
741 case CK_CONFIG_COLOR: {
742 result = Ck_NameOfColor(*((int *) ptr));
745 case CK_CONFIG_BORDER: {
746 CkBorder *borderPtr = *((CkBorder **) ptr);
747 if (borderPtr != NULL) {
748 result = Ck_NameOfBorder(borderPtr);
752 case CK_CONFIG_JUSTIFY:
753 result = Ck_NameOfJustify(*((Ck_Justify *) ptr));
755 case CK_CONFIG_ANCHOR:
756 result = Ck_NameOfAnchor(*((Ck_Anchor *) ptr));
759 result = Ck_NameOfAttr(*(int *) ptr);
760 *freeProcPtr = (Tcl_FreeProc *) free;
762 case CK_CONFIG_WINDOW: {
765 winPtr2 = *((CkWindow **) ptr);
766 if (winPtr2 != NULL) {
767 result = winPtr2->pathName;
771 case CK_CONFIG_CUSTOM:
772 result = (*specPtr->customPtr->printProc)(
773 specPtr->customPtr->clientData, winPtr, widgRec,
774 specPtr->offset, freeProcPtr);
777 result = "?? unknown type ??";
783 *----------------------------------------------------------------------
787 * Free up all resources associated with configuration options.
793 * Any resource in widgRec that is controlled by a configuration
794 * option is freed in the appropriate fashion.
796 *----------------------------------------------------------------------
800 Ck_FreeOptions(specs, widgRec, needFlags)
801 Ck_ConfigSpec *specs; /* Describes legal options. */
802 char *widgRec; /* Record whose fields contain current
803 * values for options. */
804 int needFlags; /* Used to specify additional flags
805 * that must be present in config specs
806 * for them to be considered. */
808 Ck_ConfigSpec *specPtr;
811 for (specPtr = specs; specPtr->type != CK_CONFIG_END; specPtr++) {
812 if ((specPtr->specFlags & needFlags) != needFlags) {
815 ptr = widgRec + specPtr->offset;
816 switch (specPtr->type) {
817 case CK_CONFIG_STRING:
818 if (*((char **) ptr) != NULL) {
819 ckfree(*((char **) ptr));
820 *((char **) ptr) = NULL;
823 case CK_CONFIG_BORDER:
824 if (*((CkBorder **) ptr) != NULL) {
825 Ck_FreeBorder(*((CkBorder **) ptr));
826 *((CkBorder **) ptr) = NULL;