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
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
13 * See the file "license.terms" for information on usage and redistribution
14 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
22 * A data structure of the following type is kept for each
23 * widget managed by this file:
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. */
36 * Information about what's in the button.
39 char *text; /* Text to display in button (malloc'ed)
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. */
47 * Information used when displaying widget:
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
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. */
72 * For check and radio buttons, the fields below are used
73 * to manage the variable indicating the button's state.
76 char *selVarName; /* Name of variable used to control selected
77 * state of button. Malloc'ed (if
79 char *onValue; /* Value to store in variable when
80 * this button is selected. Malloc'ed (if
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
88 * Miscellaneous information:
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
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
107 #define TYPE_BUTTON 1
108 #define TYPE_CHECK_BUTTON 2
109 #define TYPE_RADIO_BUTTON 3
112 * Class names for buttons, indexed by one of the type values above.
115 static char *classNames[] = {"Label", "Button", "Checkbutton", "Radiobutton"};
118 * Flag bits for buttons:
120 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
121 * has already been queued to redraw
123 * SELECTED: Non-zero means this button is selected,
124 * so special highlight should be drawn.
127 #define REDRAW_PENDING 1
131 * Mask values used to selectively enable entries in the
132 * configuration specs:
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)
142 static int configFlags[] = {LABEL_MASK, BUTTON_MASK,
143 CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
145 * Information used for parsing configuration specs:
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),
216 {CK_CONFIG_STRING, "-onvalue", "onValue", "Value",
217 DEF_BUTTON_ON_VALUE, Ck_Offset(Button, onValue),
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),
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),
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,
267 * String to print out in error messages, identifying options for
268 * widget commands for different types of labels or buttons:
271 static char *optionStrings[] = {
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"
279 * Forward declarations for procedures defined later in this file:
282 static void ButtonCmdDeletedProc _ANSI_ARGS_((
283 ClientData clientData));
284 static void ButtonEventProc _ANSI_ARGS_((ClientData clientData,
286 static char * ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
287 Tcl_Interp *interp, char *name1, char *name2,
289 static char * ButtonVarProc _ANSI_ARGS_((ClientData clientData,
290 Tcl_Interp *interp, char *name1, char *name2,
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,
298 static void DestroyButton _ANSI_ARGS_((ClientData clientData));
299 static void DisplayButton _ANSI_ARGS_((ClientData clientData));
300 static int InvokeButton _ANSI_ARGS_((Button *butPtr));
303 *--------------------------------------------------------------
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.
312 * A standard Tcl result.
315 * See the user documentation.
317 *--------------------------------------------------------------
321 Ck_ButtonCmd(clientData, interp, argc, argv)
322 ClientData clientData; /* Main window associated with
324 Tcl_Interp *interp; /* Current interpreter. */
325 int argc; /* Number of arguments. */
326 char **argv; /* Argument strings. */
330 CkWindow *winPtr = (CkWindow *) clientData;
334 Tcl_AppendResult(interp, "wrong # args: should be \"",
335 argv[0], " pathName ?options?\"", (char *) NULL);
339 switch (argv[0][0]) {
347 type = TYPE_CHECK_BUTTON;
350 type = TYPE_RADIO_BUTTON;
353 sprintf(interp->result,
354 "unknown button-creation command \"%.50s\"", argv[0]);
359 * Create the new window.
362 new = Ck_CreateWindowFromPath(interp, winPtr, argv[1], 0);
368 * Initialize the data structure for the button.
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);
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;
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;
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);
415 interp->result = butPtr->winPtr->pathName;
420 *--------------------------------------------------------------
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.
429 * A standard Tcl result.
432 * See the user documentation.
434 *--------------------------------------------------------------
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. */
444 Button *butPtr = (Button *) clientData;
450 sprintf(interp->result,
451 "wrong # args: should be \"%.50s option [arg arg ...]\"",
455 Ck_Preserve((ClientData) butPtr);
457 length = strlen(argv[1]);
458 if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)
459 && (butPtr->type != TYPE_LABEL)) {
461 sprintf(interp->result,
462 "wrong # args: should be \"%.50s activate\"",
466 if (butPtr->state != ckDisabledUid) {
467 butPtr->state = ckActiveUid;
470 } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
473 Tcl_AppendResult(interp, "wrong # args: should be \"",
474 argv[0], " cget option\"",
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)) {
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]);
489 result = ConfigureButton(interp, butPtr, argc-2, argv+2,
490 configFlags[butPtr->type] | CK_CONFIG_ARGV_ONLY);
492 } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)
493 && (length > 2) && (butPtr->type != TYPE_LABEL)) {
495 sprintf(interp->result,
496 "wrong # args: should be \"%.50s deactivate\"",
500 if (butPtr->state != ckDisabledUid) {
501 butPtr->state = ckNormalUid;
504 } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
505 && (length > 2) && (butPtr->type >= TYPE_CHECK_BUTTON)) {
507 sprintf(interp->result,
508 "wrong # args: should be \"%.50s deselect\"",
512 if (butPtr->type == TYPE_CHECK_BUTTON) {
513 Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
515 } else if (butPtr->flags & SELECTED) {
516 Tcl_SetVar(interp, butPtr->selVarName, "", TCL_GLOBAL_ONLY);
518 } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
519 && (butPtr->type > TYPE_LABEL)) {
521 sprintf(interp->result,
522 "wrong # args: should be \"%.50s invoke\"",
526 if (butPtr->state != ckDisabledUid) {
527 result = InvokeButton(butPtr);
529 } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
530 && (butPtr->type >= TYPE_CHECK_BUTTON)) {
532 sprintf(interp->result,
533 "wrong # args: should be \"%.50s select\"",
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)) {
541 sprintf(interp->result,
542 "wrong # args: should be \"%.50s select\"",
546 if (butPtr->flags & SELECTED) {
547 Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue, TCL_GLOBAL_ONLY);
549 Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY);
552 sprintf(interp->result,
553 "bad option \"%.50s\": must be %s", argv[1],
554 optionStrings[butPtr->type]);
557 Ck_Release((ClientData) butPtr);
561 if ((butPtr->winPtr->flags & CK_MAPPED) &&
562 !(butPtr->flags & REDRAW_PENDING)) {
563 Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
564 butPtr->flags |= REDRAW_PENDING;
566 Ck_Release((ClientData) butPtr);
570 Ck_Release((ClientData) butPtr);
575 *----------------------------------------------------------------------
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).
587 * Everything associated with the widget is freed up.
589 *----------------------------------------------------------------------
593 DestroyButton(clientData)
594 ClientData clientData; /* Info about entry widget. */
596 Button *butPtr = (Button *) clientData;
599 * Free up all the stuff that requires special handling, then
600 * let Tk_FreeOptions handle all the standard option-related
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);
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);
614 Ck_FreeOptions(configSpecs, (char *) butPtr, configFlags[butPtr->type]);
615 ckfree((char *) butPtr);
619 *----------------------------------------------------------------------
621 * ButtonCmdDeletedProc --
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.
631 * The widget is destroyed.
633 *----------------------------------------------------------------------
637 ButtonCmdDeletedProc(clientData)
638 ClientData clientData; /* Pointer to widget record for widget. */
640 Button *butPtr = (Button *) clientData;
641 CkWindow *winPtr = butPtr->winPtr;
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.
650 if (winPtr != NULL) {
651 butPtr->winPtr = NULL;
652 Ck_DestroyWindow(winPtr);
657 *----------------------------------------------------------------------
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.
666 * The return value is a standard Tcl result. If TCL_ERROR is
667 * returned, then interp->result contains an error message.
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.
674 *----------------------------------------------------------------------
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. */
687 * Eliminate any existing trace on variables monitored by the button.
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);
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);
701 if (Ck_ConfigureWidget(interp, butPtr->winPtr, configSpecs,
702 argc, argv, (char *) butPtr, flags) != TCL_OK) {
707 * A few options need special processing.
710 if (butPtr->state != ckActiveUid && butPtr->state != ckDisabledUid)
711 butPtr->state = ckNormalUid;
713 if (butPtr->type >= TYPE_CHECK_BUTTON) {
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);
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);
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.
734 value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
735 butPtr->flags &= ~SELECTED;
737 if (strcmp(value, butPtr->onValue) == 0) {
738 butPtr->flags |= SELECTED;
741 Tcl_SetVar(interp, butPtr->selVarName,
742 (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
745 Tcl_TraceVar(interp, butPtr->selVarName,
746 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
747 ButtonVarProc, (ClientData) butPtr);
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.
756 if (butPtr->textVarName != NULL) {
759 value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
761 Tcl_SetVar(interp, butPtr->textVarName,
762 butPtr->text != NULL ? butPtr->text : "",
765 if (butPtr->text != NULL) {
766 ckfree(butPtr->text);
768 butPtr->text = ckalloc((unsigned) (strlen(value) + 1));
769 strcpy(butPtr->text, value);
771 Tcl_TraceVar(interp, butPtr->textVarName,
772 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
773 ButtonTextVarProc, (ClientData) butPtr);
776 ComputeButtonGeometry(butPtr);
779 * Lastly, arrange for the button to be redisplayed.
782 if ((butPtr->winPtr->flags & CK_MAPPED)
783 && !(butPtr->flags & REDRAW_PENDING)) {
784 Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
785 butPtr->flags |= REDRAW_PENDING;
792 *----------------------------------------------------------------------
796 * This procedure is invoked to display a button widget.
802 * Commands are output to display the button in its
805 *----------------------------------------------------------------------
809 DisplayButton(clientData)
810 ClientData clientData; /* Information about widget. */
812 Button *butPtr = (Button *) clientData;
813 int x, y, fg, bg, attr, textWidth, charWidth;
814 CkWindow *winPtr = butPtr->winPtr;
816 butPtr->flags &= ~REDRAW_PENDING;
817 if ((butPtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED)) {
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;
830 fg = butPtr->normalFg;
831 bg = butPtr->normalBg;
832 attr = butPtr->normalAttr;
836 * Display text for button.
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);
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;
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)
856 x = winPtr->width - textWidth;
857 if (butPtr->type >= TYPE_CHECK_BUTTON && x < 4)
861 if (x + textWidth > winPtr->width)
862 textWidth = winPtr->width - x;
864 switch (butPtr->anchor) {
865 case CK_ANCHOR_NW: case CK_ANCHOR_N: case CK_ANCHOR_NE:
868 case CK_ANCHOR_W: case CK_ANCHOR_CENTER: case CK_ANCHOR_E:
869 y = (winPtr->height - 1) / 2;
872 y = winPtr->height - 1;
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);
894 if (butPtr->type >= TYPE_CHECK_BUTTON) {
897 mvwaddstr(winPtr->window, y, 0, butPtr->type == TYPE_CHECK_BUTTON ?
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);
910 Ck_SetWindowAttr(winPtr, fg, bg, attr);
911 wmove(winPtr->window, y, (butPtr->type >= TYPE_CHECK_BUTTON) ? 1 : x);
912 Ck_EventuallyRefresh(winPtr);
916 *--------------------------------------------------------------
920 * This procedure is invoked by the dispatcher for various
927 * When the window gets deleted, internal structures get
928 * cleaned up. When it gets exposed, it is redisplayed.
930 *--------------------------------------------------------------
934 ButtonEventProc(clientData, eventPtr)
935 ClientData clientData; /* Information about window. */
936 CkEvent *eventPtr; /* Information about event. */
938 Button *butPtr = (Button *) clientData;
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;
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));
951 if (butPtr->flags & REDRAW_PENDING) {
952 Tk_CancelIdleCall(DisplayButton, (ClientData) butPtr);
954 Ck_EventuallyFree((ClientData) butPtr, (Ck_FreeProc *) DestroyButton);
959 *----------------------------------------------------------------------
961 * ComputeButtonGeometry --
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.
971 * The button's window may change size.
973 *----------------------------------------------------------------------
977 ComputeButtonGeometry(butPtr)
978 Button *butPtr; /* Button whose geometry may have changed. */
980 int width, height, dummy;
981 CkWindow *winPtr = butPtr->winPtr;
983 butPtr->textLength = butPtr->text == NULL ? 0 : strlen(butPtr->text);
984 if (butPtr->height > 0)
985 height = butPtr->height;
988 if (butPtr->width > 0)
989 width = butPtr->width;
991 CkMeasureChars(winPtr->mainPtr,
992 butPtr->text == NULL ? "" : butPtr->text,
993 butPtr->textLength, 0, 100000, 0,
994 CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS,
998 * When issuing the geometry request, add extra space for the selector,
1002 if (butPtr->type >= TYPE_CHECK_BUTTON)
1005 Ck_GeometryRequest(butPtr->winPtr, width, height);
1009 *----------------------------------------------------------------------
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.
1019 * A standard Tcl return value. Information is also left in
1023 * Depends on the button and its associated command.
1025 *----------------------------------------------------------------------
1029 InvokeButton(butPtr)
1030 Button *butPtr; /* Information about button. */
1032 if (butPtr->type == TYPE_CHECK_BUTTON) {
1033 if (butPtr->flags & SELECTED) {
1034 Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue,
1037 Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
1040 } else if (butPtr->type == TYPE_RADIO_BUTTON) {
1041 Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
1044 if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
1045 return CkCopyAndGlobalEval(butPtr->interp, butPtr->command);
1051 *--------------------------------------------------------------
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.
1061 * NULL is always returned.
1064 * The button may become selected or deselected.
1066 *--------------------------------------------------------------
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. */
1077 Button *butPtr = (Button *) clientData;
1081 * If the variable is being unset, then just re-establish the
1082 * trace unless the whole interpreter is going away.
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);
1096 * Use the value of the variable to update the selected status of
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;
1105 butPtr->flags |= SELECTED;
1106 } else if (butPtr->flags & SELECTED) {
1107 butPtr->flags &= ~SELECTED;
1109 return (char *) NULL;
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;
1118 return (char *) NULL;
1122 *--------------------------------------------------------------
1124 * ButtonTextVarProc --
1126 * This procedure is invoked when someone changes the variable
1127 * whose contents are to be displayed in a button.
1130 * NULL is always returned.
1133 * The text displayed in the button will change to match the
1136 *--------------------------------------------------------------
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. */
1147 Button *butPtr = (Button *) clientData;
1151 * If the variable is unset, then immediately recreate it unless
1152 * the whole interpreter is going away.
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);
1164 return (char *) NULL;
1167 value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
1168 if (value == NULL) {
1171 if (butPtr->text != NULL) {
1172 ckfree(butPtr->text);
1174 butPtr->text = ckalloc((unsigned) (strlen(value) + 1));
1175 strcpy(butPtr->text, value);
1176 ComputeButtonGeometry(butPtr);
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;
1183 return (char *) NULL;