]> www.wagner.pp.ru Git - oss/ck.git/blob - ckButton.c
Ck console graphics toolkit
[oss/ck.git] / ckButton.c
1 /* 
2  * ckButton.c --
3  *
4  *      This module implements a collection of button-like
5  *      widgets for the Ck toolkit.  The widgets implemented
6  *      include labels, buttons, check buttons, and radio
7  *      buttons.
8  *
9  * Copyright (c) 1990-1994 The Regents of the University of California.
10  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
11  * Copyright (c) 1995 Christian Werner
12  *
13  * See the file "license.terms" for information on usage and redistribution
14  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15  */
16
17 #include "ckPort.h"
18 #include "ck.h"
19 #include "default.h"
20
21 /*
22  * A data structure of the following type is kept for each
23  * widget managed by this file:
24  */
25
26 typedef struct {
27     CkWindow *winPtr;           /* Window that embodies the button.  NULL
28                                  * means that the window has been destroyed. */
29     Tcl_Interp *interp;         /* Interpreter associated with button. */
30     Tcl_Command widgetCmd;      /* Token for button's widget command. */
31     int type;                   /* Type of widget:  restricts operations
32                                  * that may be performed on widget.  See
33                                  * below for possible values. */
34
35     /*
36      * Information about what's in the button.
37      */
38
39     char *text;                 /* Text to display in button (malloc'ed)
40                                  * or NULL. */
41     int textLength;             /* # of characters in text. */
42     char *textVarName;          /* Name of variable (malloc'ed) or NULL.
43                                  * If non-NULL, button displays the contents
44                                  * of this variable. */
45
46     /*
47      * Information used when displaying widget:
48      */
49
50     Ck_Uid state;               /* State of button for display purposes:
51                                  * normal, active, or disabled. */
52     int normalFg;               /* Foreground color in normal mode. */
53     int normalBg;               /* Background color in normal mode. */
54     int normalAttr;             /* Attributes in normal mode. */
55     int activeFg;               /* Foreground color in active mode. */
56     int activeBg;               /* Ditto, background color. */
57     int activeAttr;             /* Attributes in active mode. */
58     int disabledBg;             /* Background color when disabled. */
59     int disabledFg;             /* Foreground color when disabled. */
60     int disabledAttr;           /* Attributes when disabled. */
61     int underline;              /* Index of underlined character, < 0 if
62                                  * no underlining. */
63     int underlineFg;            /* Foreground for underlined character. */
64     int underlineAttr;          /* Attribute for underlined character. */
65     int selectFg;               /* Foreground color for selector. */
66     int width, height;          /* If > 0, these specify dimensions to request
67                                  * for window, in characters for text. */
68     Ck_Anchor anchor;           /* Where text should be displayed
69                                  * inside button region. */
70
71     /*
72      * For check and radio buttons, the fields below are used
73      * to manage the variable indicating the button's state.
74      */
75
76     char *selVarName;           /* Name of variable used to control selected
77                                  * state of button.  Malloc'ed (if
78                                  * not NULL). */
79     char *onValue;              /* Value to store in variable when
80                                  * this button is selected.  Malloc'ed (if
81                                  * not NULL). */
82     char *offValue;             /* Value to store in variable when this
83                                  * button isn't selected.  Malloc'ed
84                                  * (if not NULL).  Valid only for check
85                                  * buttons. */
86
87     /*
88      * Miscellaneous information:
89      */
90
91     char *command;              /* Command to execute when button is
92                                  * invoked; valid for buttons only.
93                                  * If not NULL, it's malloc-ed. */
94     char *takeFocus;            /* Tk 4.0 like. */
95     int flags;                  /* Various flags;  see below for
96                                  * definitions. */
97 } Button;
98
99 /*
100  * Possible "type" values for buttons.  These are the kinds of
101  * widgets supported by this file.  The ordering of the type
102  * numbers is significant:  greater means more features and is
103  * used in the code.
104  */
105
106 #define TYPE_LABEL              0
107 #define TYPE_BUTTON             1
108 #define TYPE_CHECK_BUTTON       2
109 #define TYPE_RADIO_BUTTON       3
110
111 /*
112  * Class names for buttons, indexed by one of the type values above.
113  */
114
115 static char *classNames[] = {"Label", "Button", "Checkbutton", "Radiobutton"};
116
117 /*
118  * Flag bits for buttons:
119  *
120  * REDRAW_PENDING:              Non-zero means a DoWhenIdle handler
121  *                              has already been queued to redraw
122  *                              this window.
123  * SELECTED:                    Non-zero means this button is selected,
124  *                              so special highlight should be drawn.
125  */
126
127 #define REDRAW_PENDING          1
128 #define SELECTED                2
129
130 /*
131  * Mask values used to selectively enable entries in the
132  * configuration specs:
133  */
134
135 #define LABEL_MASK              CK_CONFIG_USER_BIT
136 #define BUTTON_MASK             CK_CONFIG_USER_BIT << 1
137 #define CHECK_BUTTON_MASK       CK_CONFIG_USER_BIT << 2
138 #define RADIO_BUTTON_MASK       CK_CONFIG_USER_BIT << 3
139 #define ALL_MASK                (LABEL_MASK | BUTTON_MASK \
140         | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK)
141
142 static int configFlags[] = {LABEL_MASK, BUTTON_MASK,
143         CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
144 /*
145  * Information used for parsing configuration specs:
146  */
147
148 static Ck_ConfigSpec configSpecs[] = {
149     {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
150         "ActiveAttributes", DEF_BUTTON_ACTIVE_ATTR_COLOR,
151         Ck_Offset(Button, activeAttr),
152         BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_COLOR_ONLY},
153     {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
154         "ActiveAttributes", DEF_BUTTON_ACTIVE_ATTR_MONO,
155         Ck_Offset(Button, activeAttr),
156         BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_MONO_ONLY},
157     {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
158         DEF_BUTTON_ACTIVE_BG_COLOR, Ck_Offset(Button, activeBg),
159         BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_COLOR_ONLY},
160     {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
161         DEF_BUTTON_ACTIVE_BG_MONO, Ck_Offset(Button, activeBg),
162         BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_MONO_ONLY},
163     {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
164         DEF_BUTTON_ACTIVE_FG_COLOR, Ck_Offset(Button, activeFg), 
165         BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_COLOR_ONLY},
166     {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
167         DEF_BUTTON_ACTIVE_FG_MONO, Ck_Offset(Button, activeFg), 
168         BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_MONO_ONLY},
169     {CK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
170         DEF_BUTTON_ANCHOR, Ck_Offset(Button, anchor), ALL_MASK},
171     {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
172         DEF_BUTTON_ATTR, Ck_Offset(Button, normalAttr),
173         BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
174     {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
175         DEF_LABEL_ATTR, Ck_Offset(Button, normalAttr), LABEL_MASK},
176     {CK_CONFIG_COLOR, "-background", "background", "Background",
177         DEF_BUTTON_BG_COLOR, Ck_Offset(Button, normalBg),
178         ALL_MASK | CK_CONFIG_COLOR_ONLY},
179     {CK_CONFIG_COLOR, "-background", "background", "Background",
180         DEF_BUTTON_BG_MONO, Ck_Offset(Button, normalBg),
181         ALL_MASK | CK_CONFIG_MONO_ONLY},
182     {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
183         (char *) NULL, 0, ALL_MASK},
184     {CK_CONFIG_STRING, "-command", "command", "Command",
185         DEF_BUTTON_COMMAND, Ck_Offset(Button, command),
186         BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_NULL_OK},
187     {CK_CONFIG_ATTR, "-disabledattributes", "disabledAttributes",
188         "DisabledAttributes", DEF_BUTTON_DISABLED_ATTR,
189         Ck_Offset(Button, disabledAttr),
190         BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
191     {CK_CONFIG_COLOR, "-disabledbackground", "disabledBackground",
192         "DisabledBackground", DEF_BUTTON_DISABLED_BG_COLOR,
193         Ck_Offset(Button, disabledBg), BUTTON_MASK|CHECK_BUTTON_MASK
194         |RADIO_BUTTON_MASK|CK_CONFIG_COLOR_ONLY},
195     {CK_CONFIG_COLOR, "-disabledbackground", "disabledBackground",
196         "DisabledBackground", DEF_BUTTON_DISABLED_BG_MONO,
197         Ck_Offset(Button, disabledBg),
198         BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_MONO_ONLY},
199     {CK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
200         "DisabledForeground", DEF_BUTTON_DISABLED_FG_COLOR,
201         Ck_Offset(Button, disabledFg),
202         BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_COLOR_ONLY},
203     {CK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
204         "DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO,
205         Ck_Offset(Button, disabledFg),
206         BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_MONO_ONLY},
207     {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
208         (char *) NULL, 0, ALL_MASK},
209     {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
210         DEF_BUTTON_FG, Ck_Offset(Button, normalFg), ALL_MASK},
211     {CK_CONFIG_INT, "-height", "height", "Height",
212         DEF_BUTTON_HEIGHT, Ck_Offset(Button, height), ALL_MASK},
213     {CK_CONFIG_STRING, "-offvalue", "offValue", "Value",
214         DEF_BUTTON_OFF_VALUE, Ck_Offset(Button, offValue),
215         CHECK_BUTTON_MASK},
216     {CK_CONFIG_STRING, "-onvalue", "onValue", "Value",
217         DEF_BUTTON_ON_VALUE, Ck_Offset(Button, onValue),
218         CHECK_BUTTON_MASK},
219     {CK_CONFIG_COLOR, "-selectcolor", "selectColor", "Background",
220         DEF_BUTTON_SELECT_COLOR, Ck_Offset(Button, selectFg),
221         CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_COLOR_ONLY},
222     {CK_CONFIG_COLOR, "-selectcolor", "selectColor", "Background",
223         DEF_BUTTON_SELECT_MONO, Ck_Offset(Button, selectFg),
224         CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_MONO_ONLY},
225     {CK_CONFIG_UID, "-state", "state", "State",
226         DEF_BUTTON_STATE, Ck_Offset(Button, state),
227         BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
228     {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
229         DEF_BUTTON_TAKE_FOCUS, Ck_Offset(Button, takeFocus),
230         BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|CK_CONFIG_NULL_OK},
231     {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
232         DEF_LABEL_TAKE_FOCUS, Ck_Offset(Button, takeFocus),
233         LABEL_MASK|CK_CONFIG_NULL_OK},
234     {CK_CONFIG_STRING, "-text", "text", "Text",
235         DEF_BUTTON_TEXT, Ck_Offset(Button, text), ALL_MASK},
236     {CK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
237         DEF_BUTTON_TEXT_VARIABLE, Ck_Offset(Button, textVarName),
238         ALL_MASK|CK_CONFIG_NULL_OK},
239     {CK_CONFIG_INT, "-underline", "underline", "Underline",
240         DEF_BUTTON_UNDERLINE, Ck_Offset(Button, underline),
241         ALL_MASK},
242     {CK_CONFIG_ATTR, "-underlineattributes", "underlineAttributes",
243         "UnderlineAttributes", DEF_BUTTON_UNDERLINE_ATTR,
244         Ck_Offset(Button, underlineAttr), ALL_MASK},
245     {CK_CONFIG_COLOR, "-underlineforeground", "underlineForeground",
246         "UnderlineForeground", DEF_BUTTON_UNDERLINE_FG_COLOR,
247         Ck_Offset(Button, underlineFg), ALL_MASK|CK_CONFIG_COLOR_ONLY},
248     {CK_CONFIG_COLOR, "-underlineforeground", "underlineForeground",
249         "UnderlineForeground", DEF_BUTTON_UNDERLINE_FG_MONO,
250         Ck_Offset(Button, underlineFg), ALL_MASK|CK_CONFIG_MONO_ONLY},
251     {CK_CONFIG_STRING, "-value", "value", "Value",
252         DEF_BUTTON_VALUE, Ck_Offset(Button, onValue),
253         RADIO_BUTTON_MASK|CK_CONFIG_NULL_OK},
254     {CK_CONFIG_STRING, "-variable", "variable", "Variable",
255         DEF_RADIOBUTTON_VARIABLE, Ck_Offset(Button, selVarName),
256         RADIO_BUTTON_MASK},
257     {CK_CONFIG_STRING, "-variable", "variable", "Variable",
258         DEF_CHECKBUTTON_VARIABLE, Ck_Offset(Button, selVarName),
259         CHECK_BUTTON_MASK|CK_CONFIG_NULL_OK},
260     {CK_CONFIG_INT, "-width", "width", "Width",
261         DEF_BUTTON_WIDTH, Ck_Offset(Button, width), ALL_MASK},
262     {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
263         (char *) NULL, 0, 0}
264 };
265
266 /*
267  * String to print out in error messages, identifying options for
268  * widget commands for different types of labels or buttons:
269  */
270
271 static char *optionStrings[] = {
272     "cget or configure",
273     "activate, cget, configure, deactivate, flash, or invoke",
274     "activate, cget, configure, deactivate, deselect, flash, invoke, select, or toggle",
275     "activate, cget, configure, deactivate, deselect, flash, invoke, or select"
276 };
277
278 /*
279  * Forward declarations for procedures defined later in this file:
280  */
281
282 static void             ButtonCmdDeletedProc _ANSI_ARGS_((
283                             ClientData clientData));
284 static void             ButtonEventProc _ANSI_ARGS_((ClientData clientData,
285                             CkEvent *eventPtr));
286 static char *           ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
287                             Tcl_Interp *interp, char *name1, char *name2,
288                             int flags));
289 static char *           ButtonVarProc _ANSI_ARGS_((ClientData clientData,
290                             Tcl_Interp *interp, char *name1, char *name2,
291                             int flags));
292 static int              ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
293                             Tcl_Interp *interp, int argc, char **argv));
294 static void             ComputeButtonGeometry _ANSI_ARGS_((Button *butPtr));
295 static int              ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
296                             Button *butPtr, int argc, char **argv,
297                             int flags));
298 static void             DestroyButton _ANSI_ARGS_((ClientData clientData));
299 static void             DisplayButton _ANSI_ARGS_((ClientData clientData));
300 static int              InvokeButton  _ANSI_ARGS_((Button *butPtr));
301 \f
302 /*
303  *--------------------------------------------------------------
304  *
305  * Ck_ButtonCmd --
306  *
307  *      This procedure is invoked to process the "button", "label",
308  *      "radiobutton", and "checkbutton" Tcl commands.  See the
309  *      user documentation for details on what it does.
310  *
311  * Results:
312  *      A standard Tcl result.
313  *
314  * Side effects:
315  *      See the user documentation.
316  *
317  *--------------------------------------------------------------
318  */
319
320 int
321 Ck_ButtonCmd(clientData, interp, argc, argv)
322     ClientData clientData;      /* Main window associated with
323                                  * interpreter. */
324     Tcl_Interp *interp;         /* Current interpreter. */
325     int argc;                   /* Number of arguments. */
326     char **argv;                /* Argument strings. */
327 {
328     Button *butPtr;
329     int type;
330     CkWindow *winPtr = (CkWindow *) clientData;
331     CkWindow *new;
332
333     if (argc < 2) {
334         Tcl_AppendResult(interp, "wrong # args:  should be \"",
335                 argv[0], " pathName ?options?\"", (char *) NULL);
336         return TCL_ERROR;
337     }
338
339     switch (argv[0][0]) {
340         case 'l':
341             type = TYPE_LABEL;
342             break;
343         case 'b':
344             type = TYPE_BUTTON;
345             break;
346         case 'c':
347             type = TYPE_CHECK_BUTTON;
348             break;
349         case 'r':
350             type = TYPE_RADIO_BUTTON;
351             break;
352         default:
353             sprintf(interp->result,
354                     "unknown button-creation command \"%.50s\"", argv[0]);
355             return TCL_ERROR;
356     }
357
358     /*
359      * Create the new window.
360      */
361
362     new = Ck_CreateWindowFromPath(interp, winPtr, argv[1], 0);
363     if (new == NULL) {
364         return TCL_ERROR;
365     }
366
367     /*
368      * Initialize the data structure for the button.
369      */
370
371     butPtr = (Button *) ckalloc(sizeof (Button));
372     butPtr->winPtr = new;
373     butPtr->interp = interp;
374     butPtr->widgetCmd = Tcl_CreateCommand(interp,
375         butPtr->winPtr->pathName, ButtonWidgetCmd,
376             (ClientData) butPtr, ButtonCmdDeletedProc);
377     butPtr->type = type;
378     butPtr->text = NULL;
379     butPtr->textLength = 0;
380     butPtr->textVarName = NULL;
381     butPtr->state = ckNormalUid;
382     butPtr->normalFg = 0;
383     butPtr->normalBg = 0;
384     butPtr->normalAttr = 0;
385     butPtr->activeFg = 0;
386     butPtr->activeBg = 0;
387     butPtr->activeAttr = 0;
388     butPtr->disabledFg = 0;
389     butPtr->disabledBg = 0;
390     butPtr->disabledAttr = 0;
391     butPtr->underline = -1;
392     butPtr->underlineFg = 0;
393     butPtr->underlineAttr = 0;
394     butPtr->selectFg = 0;
395     butPtr->width = 0;
396     butPtr->height = 0;
397     butPtr->anchor = CK_ANCHOR_CENTER;
398     butPtr->selVarName = NULL;
399     butPtr->onValue = NULL;
400     butPtr->offValue = NULL;
401     butPtr->command = NULL;
402     butPtr->takeFocus = NULL;
403     butPtr->flags = 0;
404
405     Ck_SetClass(new, classNames[type]);
406     Ck_CreateEventHandler(butPtr->winPtr,
407             CK_EV_EXPOSE | CK_EV_MAP | CK_EV_DESTROY,
408             ButtonEventProc, (ClientData) butPtr);
409     if (ConfigureButton(interp, butPtr, argc-2, argv+2,
410             configFlags[type]) != TCL_OK) {
411         Ck_DestroyWindow(butPtr->winPtr);
412         return TCL_ERROR;
413     }
414
415     interp->result = butPtr->winPtr->pathName;
416     return TCL_OK;
417 }
418 \f
419 /*
420  *--------------------------------------------------------------
421  *
422  * ButtonWidgetCmd --
423  *
424  *      This procedure is invoked to process the Tcl command
425  *      that corresponds to a widget managed by this module.
426  *      See the user documentation for details on what it does.
427  *
428  * Results:
429  *      A standard Tcl result.
430  *
431  * Side effects:
432  *      See the user documentation.
433  *
434  *--------------------------------------------------------------
435  */
436
437 static int
438 ButtonWidgetCmd(clientData, interp, argc, argv)
439     ClientData clientData;      /* Information about button widget. */
440     Tcl_Interp *interp;         /* Current interpreter. */
441     int argc;                   /* Number of arguments. */
442     char **argv;                /* Argument strings. */
443 {
444     Button *butPtr = (Button *) clientData;
445     int result = TCL_OK;
446     int length;
447     char c;
448
449     if (argc < 2) {
450         sprintf(interp->result,
451                 "wrong # args: should be \"%.50s option [arg arg ...]\"",
452                 argv[0]);
453         return TCL_ERROR;
454     }
455     Ck_Preserve((ClientData) butPtr);
456     c = argv[1][0];
457     length = strlen(argv[1]);
458     if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)
459             && (butPtr->type != TYPE_LABEL)) {
460         if (argc > 2) {
461             sprintf(interp->result,
462                     "wrong # args: should be \"%.50s activate\"",
463                     argv[0]);
464             goto error;
465         }
466         if (butPtr->state != ckDisabledUid) {
467             butPtr->state = ckActiveUid;
468             goto redisplay;
469         }
470     } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
471         && (length >= 2)) {
472         if (argc != 3) {
473             Tcl_AppendResult(interp, "wrong # args: should be \"",
474                     argv[0], " cget option\"",
475                     (char *) NULL);
476             goto error;
477         }
478         result = Ck_ConfigureValue(interp, butPtr->winPtr, configSpecs,
479                 (char *) butPtr, argv[2], configFlags[butPtr->type]);
480     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
481         if (argc == 2) {
482             result = Ck_ConfigureInfo(interp, butPtr->winPtr, configSpecs,
483                     (char *) butPtr, (char *) NULL, configFlags[butPtr->type]);
484         } else if (argc == 3) {
485             result = Ck_ConfigureInfo(interp, butPtr->winPtr, configSpecs,
486                     (char *) butPtr, argv[2],
487                     configFlags[butPtr->type]);
488         } else {
489             result = ConfigureButton(interp, butPtr, argc-2, argv+2,
490                     configFlags[butPtr->type] | CK_CONFIG_ARGV_ONLY);
491         }
492     } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)
493             && (length > 2) && (butPtr->type != TYPE_LABEL)) {
494         if (argc > 2) {
495             sprintf(interp->result,
496                     "wrong # args: should be \"%.50s deactivate\"",
497                     argv[0]);
498             goto error;
499         }
500         if (butPtr->state != ckDisabledUid) {
501             butPtr->state = ckNormalUid;
502             goto redisplay;
503         }
504     } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
505             && (length > 2) && (butPtr->type >= TYPE_CHECK_BUTTON)) {
506         if (argc > 2) {
507             sprintf(interp->result,
508                     "wrong # args: should be \"%.50s deselect\"",
509                     argv[0]);
510             goto error;
511         }
512         if (butPtr->type == TYPE_CHECK_BUTTON) {
513             Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
514                     TCL_GLOBAL_ONLY);
515         } else if (butPtr->flags & SELECTED) {
516             Tcl_SetVar(interp, butPtr->selVarName, "", TCL_GLOBAL_ONLY);
517         }
518     } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
519             && (butPtr->type > TYPE_LABEL)) {
520         if (argc > 2) {
521             sprintf(interp->result,
522                     "wrong # args: should be \"%.50s invoke\"",
523                     argv[0]);
524             goto error;
525         }
526         if (butPtr->state != ckDisabledUid) {
527             result = InvokeButton(butPtr);
528         }
529     } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
530             && (butPtr->type >= TYPE_CHECK_BUTTON)) {
531         if (argc > 2) {
532             sprintf(interp->result,
533                     "wrong # args: should be \"%.50s select\"",
534                     argv[0]);
535             goto error;
536         }
537         Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY);
538     } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
539             && (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
540         if (argc > 2) {
541             sprintf(interp->result,
542                     "wrong # args: should be \"%.50s select\"",
543                     argv[0]);
544             goto error;
545         }
546         if (butPtr->flags & SELECTED) {
547             Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue, TCL_GLOBAL_ONLY);
548         } else {
549             Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY);
550         }
551     } else {
552         sprintf(interp->result,
553                 "bad option \"%.50s\":  must be %s", argv[1],
554                 optionStrings[butPtr->type]);
555         goto error;
556     }
557     Ck_Release((ClientData) butPtr);
558     return result;
559
560     redisplay:
561     if ((butPtr->winPtr->flags & CK_MAPPED) &&
562         !(butPtr->flags & REDRAW_PENDING)) {
563         Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
564         butPtr->flags |= REDRAW_PENDING;
565     }
566     Ck_Release((ClientData) butPtr);
567     return TCL_OK;
568
569     error:
570     Ck_Release((ClientData) butPtr);
571     return TCL_ERROR;
572 }
573 \f
574 /*
575  *----------------------------------------------------------------------
576  *
577  * DestroyButton --
578  *
579  *      This procedure is invoked by Ck_EventuallyFree or Ck_Release
580  *      to clean up the internal structure of a button at a safe time
581  *      (when no-one is using it anymore).
582  *
583  * Results:
584  *      None.
585  *
586  * Side effects:
587  *      Everything associated with the widget is freed up.
588  *
589  *----------------------------------------------------------------------
590  */
591
592 static void
593 DestroyButton(clientData)
594     ClientData clientData;              /* Info about entry widget. */
595 {
596     Button *butPtr = (Button *) clientData;
597
598     /*
599      * Free up all the stuff that requires special handling, then
600      * let Tk_FreeOptions handle all the standard option-related
601      * stuff.
602      */
603
604     if (butPtr->textVarName != NULL) {
605         Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
606                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
607                 ButtonTextVarProc, (ClientData) butPtr);
608     }
609     if (butPtr->selVarName != NULL) {
610         Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
611                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
612                 ButtonVarProc, (ClientData) butPtr);
613     }
614     Ck_FreeOptions(configSpecs, (char *) butPtr, configFlags[butPtr->type]);
615     ckfree((char *) butPtr);
616 }
617 \f
618 /*
619  *----------------------------------------------------------------------
620  *
621  * ButtonCmdDeletedProc --
622  *
623  *      This procedure is invoked when a widget command is deleted.  If
624  *      the widget isn't already in the process of being destroyed,
625  *      this command destroys it.
626  *
627  * Results:
628  *      None.
629  *
630  * Side effects:
631  *      The widget is destroyed.
632  *
633  *----------------------------------------------------------------------
634  */
635
636 static void
637 ButtonCmdDeletedProc(clientData)
638     ClientData clientData;      /* Pointer to widget record for widget. */
639 {
640     Button *butPtr = (Button *) clientData;
641     CkWindow *winPtr = butPtr->winPtr;
642
643     /*
644      * This procedure could be invoked either because the window was
645      * destroyed and the command was then deleted (in which case winPtr
646      * is NULL) or because the command was deleted, and then this procedure
647      * destroys the widget.
648      */
649
650     if (winPtr != NULL) {
651         butPtr->winPtr = NULL;
652         Ck_DestroyWindow(winPtr);
653     }
654 }
655 \f
656 /*
657  *----------------------------------------------------------------------
658  *
659  * ConfigureButton --
660  *
661  *      This procedure is called to process an argv/argc list, plus
662  *      the Tk option database, in order to configure (or
663  *      reconfigure) a button widget.
664  *
665  * Results:
666  *      The return value is a standard Tcl result.  If TCL_ERROR is
667  *      returned, then interp->result contains an error message.
668  *
669  * Side effects:
670  *      Configuration information, such as text string, colors, font,
671  *      etc. get set for butPtr;  old resources get freed, if there
672  *      were any.  The button is redisplayed.
673  *
674  *----------------------------------------------------------------------
675  */
676
677 static int
678 ConfigureButton(interp, butPtr, argc, argv, flags)
679     Tcl_Interp *interp;         /* Used for error reporting. */
680     Button *butPtr;             /* Information about widget;  may or may
681                                  * not already have values for some fields. */
682     int argc;                   /* Number of valid entries in argv. */
683     char **argv;                /* Arguments. */
684     int flags;                  /* Flags to pass to Tk_ConfigureWidget. */
685 {
686     /*
687      * Eliminate any existing trace on variables monitored by the button.
688      */
689
690     if (butPtr->textVarName != NULL) {
691         Tcl_UntraceVar(interp, butPtr->textVarName, 
692                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
693                 ButtonTextVarProc, (ClientData) butPtr);
694     }
695     if (butPtr->selVarName != NULL) {
696         Tcl_UntraceVar(interp, butPtr->selVarName, 
697                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
698                 ButtonVarProc, (ClientData) butPtr);
699     }
700
701     if (Ck_ConfigureWidget(interp, butPtr->winPtr, configSpecs,
702             argc, argv, (char *) butPtr, flags) != TCL_OK) {
703         return TCL_ERROR;
704     }
705
706     /*
707      * A few options need special processing.
708      */
709
710     if (butPtr->state != ckActiveUid && butPtr->state != ckDisabledUid)
711         butPtr->state = ckNormalUid;
712
713     if (butPtr->type >= TYPE_CHECK_BUTTON) {
714         char *value;
715
716         if (butPtr->selVarName == NULL) {
717             butPtr->selVarName = (char *) ckalloc(
718                 strlen((char *) butPtr->winPtr->nameUid) + 1);
719             strcpy(butPtr->selVarName, (char *) butPtr->winPtr->nameUid);
720         }
721         if (butPtr->onValue == NULL) {
722             butPtr->onValue = (char *) ckalloc(
723                 strlen((char *) butPtr->winPtr->nameUid) + 1);
724             strcpy(butPtr->onValue, (char *) butPtr->winPtr->nameUid);
725         }
726
727         /*
728          * Select the button if the associated variable has the
729          * appropriate value, initialize the variable if it doesn't
730          * exist, then set a trace on the variable to monitor future
731          * changes to its value.
732          */
733
734         value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
735         butPtr->flags &= ~SELECTED;
736         if (value != NULL) {
737             if (strcmp(value, butPtr->onValue) == 0) {
738                 butPtr->flags |= SELECTED;
739             }
740         } else {
741             Tcl_SetVar(interp, butPtr->selVarName,
742                 (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
743                 TCL_GLOBAL_ONLY);
744         }
745         Tcl_TraceVar(interp, butPtr->selVarName,
746             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
747             ButtonVarProc, (ClientData) butPtr);
748     }
749
750     /*
751      * If the button is to display the value of a variable, then set up
752      * a trace on the variable's value, create the variable if it doesn't
753      * exist, and fetch its current value.
754      */
755
756     if (butPtr->textVarName != NULL) {
757         char *value;
758
759         value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
760         if (value == NULL) {
761             Tcl_SetVar(interp, butPtr->textVarName,
762                     butPtr->text != NULL ? butPtr->text : "",
763                     TCL_GLOBAL_ONLY);
764         } else {
765             if (butPtr->text != NULL) {
766                 ckfree(butPtr->text);
767             }
768             butPtr->text = ckalloc((unsigned) (strlen(value) + 1));
769             strcpy(butPtr->text, value);
770         }
771         Tcl_TraceVar(interp, butPtr->textVarName,
772                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
773                 ButtonTextVarProc, (ClientData) butPtr);
774     }
775
776     ComputeButtonGeometry(butPtr);
777
778     /*
779      * Lastly, arrange for the button to be redisplayed.
780      */
781
782     if ((butPtr->winPtr->flags & CK_MAPPED)
783         && !(butPtr->flags & REDRAW_PENDING)) {
784         Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
785         butPtr->flags |= REDRAW_PENDING;
786     }
787
788     return TCL_OK;
789 }
790 \f
791 /*
792  *----------------------------------------------------------------------
793  *
794  * DisplayButton --
795  *
796  *      This procedure is invoked to display a button widget.
797  *
798  * Results:
799  *      None.
800  *
801  * Side effects:
802  *      Commands are output to display the button in its
803  *      current mode.
804  *
805  *----------------------------------------------------------------------
806  */
807
808 static void
809 DisplayButton(clientData)
810     ClientData clientData;      /* Information about widget. */
811 {
812     Button *butPtr = (Button *) clientData;
813     int x, y, fg, bg, attr, textWidth, charWidth;
814     CkWindow *winPtr = butPtr->winPtr;
815
816     butPtr->flags &= ~REDRAW_PENDING;
817     if ((butPtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED)) {
818         return;
819     }
820
821     if (butPtr->state == ckDisabledUid) {
822         fg = butPtr->disabledFg;
823         bg = butPtr->disabledBg;
824         attr = butPtr->disabledAttr;
825     } else if (butPtr->state == ckActiveUid) {
826         fg = butPtr->activeFg;
827         bg = butPtr->activeBg;
828         attr = butPtr->activeAttr;
829     } else {
830         fg = butPtr->normalFg;
831         bg = butPtr->normalBg;
832         attr = butPtr->normalAttr;
833     }
834
835     /*
836      * Display text for button.
837      */
838
839     if (butPtr->text != NULL)
840         CkMeasureChars(winPtr->mainPtr, butPtr->text, butPtr->textLength,
841             0, winPtr->width, 0, CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS,
842             &textWidth, &charWidth);
843     else
844         textWidth = 0;
845
846     switch (butPtr->anchor) {
847         case CK_ANCHOR_NW: case CK_ANCHOR_W: case CK_ANCHOR_SW:
848             x = butPtr->type >= TYPE_CHECK_BUTTON ? 4 : 0;
849             break;
850         case CK_ANCHOR_N: case CK_ANCHOR_CENTER: case CK_ANCHOR_S:
851             x = (winPtr->width - textWidth) / 2;
852             if (butPtr->type >= TYPE_CHECK_BUTTON)
853                 x += 2;
854             break;
855         default:
856             x = winPtr->width - textWidth;
857             if (butPtr->type >= TYPE_CHECK_BUTTON && x < 4)
858                 x = 4;
859             break;
860     }
861     if (x + textWidth > winPtr->width)
862         textWidth = winPtr->width - x;
863
864     switch (butPtr->anchor) {
865         case CK_ANCHOR_NW: case CK_ANCHOR_N: case CK_ANCHOR_NE:
866             y = 0;
867             break;
868         case CK_ANCHOR_W: case CK_ANCHOR_CENTER: case CK_ANCHOR_E:
869             y = (winPtr->height - 1) / 2;
870             break;
871         default:
872             y = winPtr->height - 1;
873             if (y < 0)
874                 y = 0;
875             break;
876     }
877
878     Ck_SetWindowAttr(winPtr, fg, bg, attr);
879     Ck_ClearToBot(winPtr, 0, 0);
880     if (butPtr->text != NULL) {
881         CkDisplayChars(winPtr->mainPtr,
882             winPtr->window, butPtr->text, charWidth, x, y,
883             0, CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS);
884         if (butPtr->underline >= 0 && butPtr->state == ckNormalUid) {
885             Ck_SetWindowAttr(winPtr, butPtr->underlineFg, bg,
886                 butPtr->underlineAttr);
887             CkUnderlineChars(winPtr->mainPtr,
888                 winPtr->window, butPtr->text, charWidth, x, y,
889                 0, CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS,
890                 butPtr->underline, butPtr->underline);
891             Ck_SetWindowAttr(winPtr, fg, bg, attr);
892         }
893     }
894     if (butPtr->type >= TYPE_CHECK_BUTTON) {
895         int gchar;
896
897         mvwaddstr(winPtr->window, y, 0, butPtr->type == TYPE_CHECK_BUTTON ?
898             "[ ]" : "( )");
899         Ck_SetWindowAttr(winPtr, butPtr->selectFg, bg, attr);
900         if (!(butPtr->flags & SELECTED)) {
901             mvwaddch(winPtr->window, y, 1, (unsigned char) ' ');
902         } else if (butPtr->type == TYPE_CHECK_BUTTON) {
903             Ck_GetGChar(butPtr->interp, "diamond", &gchar);
904             mvwaddch(winPtr->window, y, 1, gchar);
905         } else if (butPtr->type == TYPE_RADIO_BUTTON) {
906             Ck_GetGChar(butPtr->interp, "bullet", &gchar);
907             mvwaddch(winPtr->window, y, 1, gchar);
908         }
909     }
910     Ck_SetWindowAttr(winPtr, fg, bg, attr);
911     wmove(winPtr->window, y, (butPtr->type >= TYPE_CHECK_BUTTON) ? 1 : x);
912     Ck_EventuallyRefresh(winPtr);
913 }
914 \f
915 /*
916  *--------------------------------------------------------------
917  *
918  * ButtonEventProc --
919  *
920  *      This procedure is invoked by the dispatcher for various
921  *      events on buttons.
922  *
923  * Results:
924  *      None.
925  *
926  * Side effects:
927  *      When the window gets deleted, internal structures get
928  *      cleaned up.  When it gets exposed, it is redisplayed.
929  *
930  *--------------------------------------------------------------
931  */
932
933 static void
934 ButtonEventProc(clientData, eventPtr)
935     ClientData clientData;      /* Information about window. */
936     CkEvent *eventPtr;          /* Information about event. */
937 {
938     Button *butPtr = (Button *) clientData;
939
940     if (eventPtr->type == CK_EV_EXPOSE || eventPtr->type == CK_EV_MAP) {
941         if ((butPtr->winPtr != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
942             Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
943             butPtr->flags |= REDRAW_PENDING;
944         }
945     } else if (eventPtr->type == CK_EV_DESTROY) {
946         if (butPtr->winPtr != NULL) {
947             butPtr->winPtr = NULL;
948             Tcl_DeleteCommand(butPtr->interp,
949                     Tcl_GetCommandName(butPtr->interp, butPtr->widgetCmd));
950         }
951         if (butPtr->flags & REDRAW_PENDING) {
952             Tk_CancelIdleCall(DisplayButton, (ClientData) butPtr);
953         }
954         Ck_EventuallyFree((ClientData) butPtr, (Ck_FreeProc *) DestroyButton);
955     }
956 }
957 \f
958 /*
959  *----------------------------------------------------------------------
960  *
961  * ComputeButtonGeometry --
962  *
963  *      After changes in a button's text or bitmap, this procedure
964  *      recomputes the button's geometry and passes this information
965  *      along to the geometry manager for the window.
966  *
967  * Results:
968  *      None.
969  *
970  * Side effects:
971  *      The button's window may change size.
972  *
973  *----------------------------------------------------------------------
974  */
975
976 static void
977 ComputeButtonGeometry(butPtr)
978     Button *butPtr;     /* Button whose geometry may have changed. */
979 {
980     int width, height, dummy;
981     CkWindow *winPtr = butPtr->winPtr;
982
983     butPtr->textLength = butPtr->text == NULL ? 0 : strlen(butPtr->text);
984     if (butPtr->height > 0)
985         height = butPtr->height;
986     else
987         height = 1;
988     if (butPtr->width > 0)
989         width = butPtr->width;
990     else
991         CkMeasureChars(winPtr->mainPtr,
992             butPtr->text == NULL ? "" : butPtr->text,
993             butPtr->textLength, 0, 100000, 0,
994             CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS,
995             &width, &dummy);
996
997     /*
998      * When issuing the geometry request, add extra space for the selector,
999      * if any.
1000      */
1001
1002     if (butPtr->type >= TYPE_CHECK_BUTTON)
1003         width += 4;
1004
1005     Ck_GeometryRequest(butPtr->winPtr, width, height);
1006 }
1007 \f
1008 /*
1009  *----------------------------------------------------------------------
1010  *
1011  * InvokeButton --
1012  *
1013  *      This procedure is called to carry out the actions associated
1014  *      with a button, such as invoking a Tcl command or setting a
1015  *      variable.  This procedure is invoked, for example, when the
1016  *      button is invoked via the mouse.
1017  *
1018  * Results:
1019  *      A standard Tcl return value.  Information is also left in
1020  *      interp->result.
1021  *
1022  * Side effects:
1023  *      Depends on the button and its associated command.
1024  *
1025  *----------------------------------------------------------------------
1026  */
1027
1028 static int
1029 InvokeButton(butPtr)
1030     Button *butPtr;             /* Information about button. */
1031 {
1032     if (butPtr->type == TYPE_CHECK_BUTTON) {
1033         if (butPtr->flags & SELECTED) {
1034             Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue,
1035                     TCL_GLOBAL_ONLY);
1036         } else {
1037             Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
1038                     TCL_GLOBAL_ONLY);
1039         }
1040     } else if (butPtr->type == TYPE_RADIO_BUTTON) {
1041         Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
1042                 TCL_GLOBAL_ONLY);
1043     }
1044     if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
1045         return CkCopyAndGlobalEval(butPtr->interp, butPtr->command);
1046     }
1047     return TCL_OK;
1048 }
1049 \f
1050 /*
1051  *--------------------------------------------------------------
1052  *
1053  * ButtonVarProc --
1054  *
1055  *      This procedure is invoked when someone changes the
1056  *      state variable associated with a radio button.  Depending
1057  *      on the new value of the button's variable, the button
1058  *      may be selected or deselected.
1059  *
1060  * Results:
1061  *      NULL is always returned.
1062  *
1063  * Side effects:
1064  *      The button may become selected or deselected.
1065  *
1066  *--------------------------------------------------------------
1067  */
1068
1069 static char *
1070 ButtonVarProc(clientData, interp, name1, name2, flags)
1071     ClientData clientData;      /* Information about button. */
1072     Tcl_Interp *interp;         /* Interpreter containing variable. */
1073     char *name1;                /* Name of variable. */
1074     char *name2;                /* Second part of variable name. */
1075     int flags;                  /* Information about what happened. */
1076 {
1077     Button *butPtr = (Button *) clientData;
1078     char *value;
1079
1080     /*
1081      * If the variable is being unset, then just re-establish the
1082      * trace unless the whole interpreter is going away.
1083      */
1084
1085     if (flags & TCL_TRACE_UNSETS) {
1086         butPtr->flags &= ~SELECTED;
1087         if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1088             Tcl_TraceVar2(interp, name1, name2,
1089                     TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1090                     ButtonVarProc, clientData);
1091         }
1092         goto redisplay;
1093     }
1094
1095     /*
1096      * Use the value of the variable to update the selected status of
1097      * the button.
1098      */
1099
1100     value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
1101     if (strcmp(value, butPtr->onValue) == 0) {
1102         if (butPtr->flags & SELECTED) {
1103             return (char *) NULL;
1104         }
1105         butPtr->flags |= SELECTED;
1106     } else if (butPtr->flags & SELECTED) {
1107         butPtr->flags &= ~SELECTED;
1108     } else {
1109         return (char *) NULL;
1110     }
1111
1112  redisplay:
1113     if ((butPtr->winPtr != NULL) && (butPtr->winPtr->flags & CK_MAPPED)
1114             && !(butPtr->flags & REDRAW_PENDING)) {
1115         Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
1116         butPtr->flags |= REDRAW_PENDING;
1117     }
1118     return (char *) NULL;
1119 }
1120 \f
1121 /*
1122  *--------------------------------------------------------------
1123  *
1124  * ButtonTextVarProc --
1125  *
1126  *      This procedure is invoked when someone changes the variable
1127  *      whose contents are to be displayed in a button.
1128  *
1129  * Results:
1130  *      NULL is always returned.
1131  *
1132  * Side effects:
1133  *      The text displayed in the button will change to match the
1134  *      variable.
1135  *
1136  *--------------------------------------------------------------
1137  */
1138
1139 static char *
1140 ButtonTextVarProc(clientData, interp, name1, name2, flags)
1141     ClientData clientData;      /* Information about button. */
1142     Tcl_Interp *interp;         /* Interpreter containing variable. */
1143     char *name1;                /* Name of variable. */
1144     char *name2;                /* Second part of variable name. */
1145     int flags;                  /* Information about what happened. */
1146 {
1147     Button *butPtr = (Button *) clientData;
1148     char *value;
1149
1150     /*
1151      * If the variable is unset, then immediately recreate it unless
1152      * the whole interpreter is going away.
1153      */
1154
1155     if (flags & TCL_TRACE_UNSETS) {
1156         if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1157             Tcl_SetVar2(interp, name1, name2,
1158                     butPtr->text != NULL ? butPtr->text : "",
1159                     flags & TCL_GLOBAL_ONLY);
1160             Tcl_TraceVar2(interp, name1, name2,
1161                     TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1162                     ButtonTextVarProc, clientData);
1163         }
1164         return (char *) NULL;
1165     }
1166
1167     value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
1168     if (value == NULL) {
1169         value = "";
1170     }
1171     if (butPtr->text != NULL) {
1172         ckfree(butPtr->text);
1173     }
1174     butPtr->text = ckalloc((unsigned) (strlen(value) + 1));
1175     strcpy(butPtr->text, value);
1176     ComputeButtonGeometry(butPtr);
1177
1178     if ((butPtr->winPtr != NULL) && (butPtr->winPtr->flags & CK_MAPPED)
1179             && !(butPtr->flags & REDRAW_PENDING)) {
1180         Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
1181         butPtr->flags |= REDRAW_PENDING;
1182     }
1183     return (char *) NULL;
1184 }