1 /*
2 * tkButton.c --
3 *
4 * This module implements a collection of button-like
5 * widgets for the Tk 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 *
12 * See the file "license.terms" for information on usage and redistribution
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 *
15 * SCCS: @(#) tkButton.c 1.128 96/03/01 17:34:49
16 */
17
18 #include "tkInt.h"
19 #include "tkDefault.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 Tk_Window tkwin; /* Window that embodies the button. NULL
28 * means that the window has been destroyed. */
29 Display *display; /* Display containing widget. Needed to
30 * free up resources after tkwin is gone. */
31 Tcl_Interp *interp; /* Interpreter associated with button. */
32 Tcl_Command widgetCmd; /* Token for button's widget command. */
33 int type; /* Type of widget: restricts operations
34 * that may be performed on widget. See
35 * below for possible values. */
36
37 /*
38 * Information about what's in the button.
39 */
40
41 char *text; /* Text to display in button (malloc'ed)
42 * or NULL. */
43 int numChars; /* # of characters in text. */
44 int underline; /* Index of character to underline. < 0 means
45 * don't underline anything. */
46 char *textVarName; /* Name of variable (malloc'ed) or NULL.
47 * If non-NULL, button displays the contents
48 * of this variable. */
49 Pixmap bitmap; /* Bitmap to display or None. If not None
50 * then text and textVar are ignored. */
51 char *imageString; /* Name of image to display (malloc'ed), or
52 * NULL. If non-NULL, bitmap, text, and
53 * textVarName are ignored. */
54 Tk_Image image; /* Image to display in window, or NULL if
55 * none. */
56 char *selectImageString; /* Name of image to display when selected
57 * (malloc'ed), or NULL. */
58 Tk_Image selectImage; /* Image to display in window when selected,
59 * or NULL if none. Ignored if image is
60 * NULL. */
61
62 /*
63 * Information used when displaying widget:
64 */
65
66 Tk_Uid state; /* State of button for display purposes:
67 * normal, active, or disabled. */
68 Tk_3DBorder normalBorder; /* Structure used to draw 3-D
69 * border and background when window
70 * isn't active. NULL means no such
71 * border exists. */
72 Tk_3DBorder activeBorder; /* Structure used to draw 3-D
73 * border and background when window
74 * is active. NULL means no such
75 * border exists. */
76 int borderWidth; /* Width of border. */
77 int relief; /* 3-d effect: TK_RELIEF_RAISED, etc. */
78 int highlightWidth; /* Width in pixels of highlight to draw
79 * around widget when it has the focus.
80 * <= 0 means don't draw a highlight. */
81 XColor *highlightBgColorPtr;
82 /* Color for drawing traversal highlight
83 * area when highlight is off. */
84 XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
85 int inset; /* Total width of all borders, including
86 * traversal highlight and 3-D border.
87 * Indicates how much interior stuff must
88 * be offset from outside edges to leave
89 * room for borders. */
90 XFontStruct *fontPtr; /* Information about text font, or NULL. */
91 XColor *normalFg; /* Foreground color in normal mode. */
92 XColor *activeFg; /* Foreground color in active mode. NULL
93 * means use normalFg instead. */
94 XColor *disabledFg; /* Foreground color when disabled. NULL
95 * means use normalFg with a 50% stipple
96 * instead. */
97 GC normalTextGC; /* GC for drawing text in normal mode. Also
98 * used to copy from off-screen pixmap onto
99 * screen. */
100 GC activeTextGC; /* GC for drawing text in active mode (NULL
101 * means use normalTextGC). */
102 Pixmap gray; /* Pixmap for displaying disabled text if
103 * disabledFg is NULL. */
104 GC disabledGC; /* Used to produce disabled effect. If
105 * disabledFg isn't NULL, this GC is used to
106 * draw button text or icon. Otherwise
107 * text or icon is drawn with normalGC and
108 * this GC is used to stipple background
109 * across it. For labels this is None. */
110 GC copyGC; /* Used for copying information from an
111 * off-screen pixmap to the screen. */
112 char *widthString; /* Value of -width option. Malloc'ed. */
113 char *heightString; /* Value of -height option. Malloc'ed. */
114 int width, height; /* If > 0, these specify dimensions to request
115 * for window, in characters for text and in
116 * pixels for bitmaps. In this case the actual
117 * size of the text string or bitmap is
118 * ignored in computing desired window size. */
119 int wrapLength; /* Line length (in pixels) at which to wrap
120 * onto next line. <= 0 means don't wrap
121 * except at newlines. */
122 int padX, padY; /* Extra space around text (pixels to leave
123 * on each side). Ignored for bitmaps and
124 * images. */
125 Tk_Anchor anchor; /* Where text/bitmap should be displayed
126 * inside button region. */
127 Tk_Justify justify; /* Justification to use for multi-line text. */
128 int indicatorOn; /* True means draw indicator, false means
129 * don't draw it. */
130 Tk_3DBorder selectBorder; /* For drawing indicator background, or perhaps
131 * widget background, when selected. */
132 int textWidth; /* Width needed to display text as requested,
133 * in pixels. */
134 int textHeight; /* Height needed to display text as requested,
135 * in pixels. */
136 int indicatorSpace; /* Horizontal space (in pixels) allocated for
137 * display of indicator. */
138 int indicatorDiameter; /* Diameter of indicator, in pixels. */
139
140 /*
141 * For check and radio buttons, the fields below are used
142 * to manage the variable indicating the button's state.
143 */
144
145 char *selVarName; /* Name of variable used to control selected
146 * state of button. Malloc'ed (if
147 * not NULL). */
148 char *onValue; /* Value to store in variable when
149 * this button is selected. Malloc'ed (if
150 * not NULL). */
151 char *offValue; /* Value to store in variable when this
152 * button isn't selected. Malloc'ed
153 * (if not NULL). Valid only for check
154 * buttons. */
155
156 /*
157 * Miscellaneous information:
158 */
159
160 Tk_Cursor cursor; /* Current cursor for window, or None. */
161 char *takeFocus; /* Value of -takefocus option; not used in
162 * the C code, but used by keyboard traversal
163 * scripts. Malloc'ed, but may be NULL. */
164 char *command; /* Command to execute when button is
165 * invoked; valid for buttons only.
166 * If not NULL, it's malloc-ed. */
167 int flags; /* Various flags; see below for
168 * definitions. */
169 } Button;
170
171 /*
172 * Possible "type" values for buttons. These are the kinds of
173 * widgets supported by this file. The ordering of the type
174 * numbers is significant: greater means more features and is
175 * used in the code.
176 */
177
178 #define TYPE_LABEL 0
179 #define TYPE_BUTTON 1
180 #define TYPE_CHECK_BUTTON 2
181 #define TYPE_RADIO_BUTTON 3
182
183 /*
184 * Class names for buttons, indexed by one of the type values above.
185 */
186
187 static char *classNames[] = {"Label", "Button", "Checkbutton", "Radiobutton"};
188
189 /*
190 * Flag bits for buttons:
191 *
192 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
193 * has already been queued to redraw
194 * this window.
195 * SELECTED: Non-zero means this button is selected,
196 * so special highlight should be drawn.
197 * GOT_FOCUS: Non-zero means this button currently
198 * has the input focus.
199 */
200
201 #define REDRAW_PENDING 1
202 #define SELECTED 2
203 #define GOT_FOCUS 4
204
205 /*
206 * Mask values used to selectively enable entries in the
207 * configuration specs:
208 */
209
210 #define LABEL_MASK TK_CONFIG_USER_BIT
211 #define BUTTON_MASK TK_CONFIG_USER_BIT << 1
212 #define CHECK_BUTTON_MASK TK_CONFIG_USER_BIT << 2
213 #define RADIO_BUTTON_MASK TK_CONFIG_USER_BIT << 3
214 #define ALL_MASK (LABEL_MASK | BUTTON_MASK \
215 | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK)
216
217 static int configFlags[] = {LABEL_MASK, BUTTON_MASK,
218 CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
219
220 /*
221 * Information used for parsing configuration specs:
222 */
223
224 static Tk_ConfigSpec configSpecs[] = {
225 {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
226 DEF_BUTTON_ACTIVE_BG_COLOR, Tk_Offset(Button, activeBorder),
227 BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
228 |TK_CONFIG_COLOR_ONLY},
229 {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
230 DEF_BUTTON_ACTIVE_BG_MONO, Tk_Offset(Button, activeBorder),
231 BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
232 |TK_CONFIG_MONO_ONLY},
233 {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
234 DEF_BUTTON_ACTIVE_FG_COLOR, Tk_Offset(Button, activeFg),
235 BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
236 |TK_CONFIG_COLOR_ONLY},
237 {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
238 DEF_BUTTON_ACTIVE_FG_MONO, Tk_Offset(Button, activeFg),
239 BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
240 |TK_CONFIG_MONO_ONLY},
241 {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
242 DEF_BUTTON_ANCHOR, Tk_Offset(Button, anchor), ALL_MASK},
243 {TK_CONFIG_BORDER, "-background", "background", "Background",
244 DEF_BUTTON_BG_COLOR, Tk_Offset(Button, normalBorder),
245 ALL_MASK | TK_CONFIG_COLOR_ONLY},
246 {TK_CONFIG_BORDER, "-background", "background", "Background",
247 DEF_BUTTON_BG_MONO, Tk_Offset(Button, normalBorder),
248 ALL_MASK | TK_CONFIG_MONO_ONLY},
249 {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
250 (char *) NULL, 0, ALL_MASK},
251 {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
252 (char *) NULL, 0, ALL_MASK},
253 {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
254 DEF_BUTTON_BITMAP, Tk_Offset(Button, bitmap),
255 ALL_MASK|TK_CONFIG_NULL_OK},
256 {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
257 DEF_BUTTON_BORDER_WIDTH, Tk_Offset(Button, borderWidth), ALL_MASK},
258 {TK_CONFIG_STRING, "-command", "command", "Command",
259 DEF_BUTTON_COMMAND, Tk_Offset(Button, command),
260 BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
261 {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
262 DEF_BUTTON_CURSOR, Tk_Offset(Button, cursor),
263 ALL_MASK|TK_CONFIG_NULL_OK},
264 {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
265 "DisabledForeground", DEF_BUTTON_DISABLED_FG_COLOR,
266 Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
267 |RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
268 {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
269 "DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO,
270 Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
271 |RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
272 {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
273 (char *) NULL, 0, ALL_MASK},
274 {TK_CONFIG_FONT, "-font", "font", "Font",
275 DEF_BUTTON_FONT, Tk_Offset(Button, fontPtr),
276 ALL_MASK},
277 {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
278 DEF_BUTTON_FG, Tk_Offset(Button, normalFg), ALL_MASK},
279 {TK_CONFIG_STRING, "-height", "height", "Height",
280 DEF_BUTTON_HEIGHT, Tk_Offset(Button, heightString), ALL_MASK},
281 {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
282 "HighlightBackground", DEF_BUTTON_HIGHLIGHT_BG,
283 Tk_Offset(Button, highlightBgColorPtr), ALL_MASK},
284 {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
285 DEF_BUTTON_HIGHLIGHT, Tk_Offset(Button, highlightColorPtr),
286 ALL_MASK},
287 {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
288 "HighlightThickness",
289 DEF_LABEL_HIGHLIGHT_WIDTH, Tk_Offset(Button, highlightWidth),
290 LABEL_MASK},
291 {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
292 "HighlightThickness",
293 DEF_BUTTON_HIGHLIGHT_WIDTH, Tk_Offset(Button, highlightWidth),
294 BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
295 {TK_CONFIG_STRING, "-image", "image", "Image",
296 DEF_BUTTON_IMAGE, Tk_Offset(Button, imageString),
297 ALL_MASK|TK_CONFIG_NULL_OK},
298 {TK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
299 DEF_BUTTON_INDICATOR, Tk_Offset(Button, indicatorOn),
300 CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
301 {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
302 DEF_BUTTON_JUSTIFY, Tk_Offset(Button, justify), ALL_MASK},
303 {TK_CONFIG_STRING, "-offvalue", "offValue", "Value",
304 DEF_BUTTON_OFF_VALUE, Tk_Offset(Button, offValue),
305 CHECK_BUTTON_MASK},
306 {TK_CONFIG_STRING, "-onvalue", "onValue", "Value",
307 DEF_BUTTON_ON_VALUE, Tk_Offset(Button, onValue),
308 CHECK_BUTTON_MASK},
309 {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
310 DEF_BUTTON_PADX, Tk_Offset(Button, padX), BUTTON_MASK},
311 {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
312 DEF_LABCHKRAD_PADX, Tk_Offset(Button, padX),
313 LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
314 {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
315 DEF_BUTTON_PADY, Tk_Offset(Button, padY), BUTTON_MASK},
316 {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
317 DEF_LABCHKRAD_PADY, Tk_Offset(Button, padY),
318 LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
319 {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
320 DEF_BUTTON_RELIEF, Tk_Offset(Button, relief), BUTTON_MASK},
321 {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
322 DEF_LABCHKRAD_RELIEF, Tk_Offset(Button, relief),
323 LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
324 {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
325 DEF_BUTTON_SELECT_COLOR, Tk_Offset(Button, selectBorder),
326 CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY
327 |TK_CONFIG_NULL_OK},
328 {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
329 DEF_BUTTON_SELECT_MONO, Tk_Offset(Button, selectBorder),
330 CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY
331 |TK_CONFIG_NULL_OK},
332 {TK_CONFIG_STRING, "-selectimage", "selectImage", "SelectImage",
333 DEF_BUTTON_SELECT_IMAGE, Tk_Offset(Button, selectImageString),
334 CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
335 {TK_CONFIG_UID, "-state", "state", "State",
336 DEF_BUTTON_STATE, Tk_Offset(Button, state),
337 BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
338 {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
339 DEF_LABEL_TAKE_FOCUS, Tk_Offset(Button, takeFocus),
340 LABEL_MASK|TK_CONFIG_NULL_OK},
341 {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
342 DEF_BUTTON_TAKE_FOCUS, Tk_Offset(Button, takeFocus),
343 BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
344 {TK_CONFIG_STRING, "-text", "text", "Text",
345 DEF_BUTTON_TEXT, Tk_Offset(Button, text), ALL_MASK},
346 {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
347 DEF_BUTTON_TEXT_VARIABLE, Tk_Offset(Button, textVarName),
348 ALL_MASK|TK_CONFIG_NULL_OK},
349 {TK_CONFIG_INT, "-underline", "underline", "Underline",
350 DEF_BUTTON_UNDERLINE, Tk_Offset(Button, underline), ALL_MASK},
351 {TK_CONFIG_STRING, "-value", "value", "Value",
352 DEF_BUTTON_VALUE, Tk_Offset(Button, onValue),
353 RADIO_BUTTON_MASK},
354 {TK_CONFIG_STRING, "-variable", "variable", "Variable",
355 DEF_RADIOBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
356 RADIO_BUTTON_MASK},
357 {TK_CONFIG_STRING, "-variable", "variable", "Variable",
358 DEF_CHECKBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
359 CHECK_BUTTON_MASK|TK_CONFIG_NULL_OK},
360 {TK_CONFIG_STRING, "-width", "width", "Width",
361 DEF_BUTTON_WIDTH, Tk_Offset(Button, widthString), ALL_MASK},
362 {TK_CONFIG_PIXELS, "-wraplength", "wrapLength", "WrapLength",
363 DEF_BUTTON_WRAP_LENGTH, Tk_Offset(Button, wrapLength), ALL_MASK},
364 {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
365 (char *) NULL, 0, 0}
366 };
367
368 /*
369 * String to print out in error messages, identifying options for
370 * widget commands for different types of labels or buttons:
371 */
372
373 static char *optionStrings[] = {
374 "cget or configure",
375 "cget, configure, flash, or invoke",
376 "cget, configure, deselect, flash, invoke, select, or toggle",
377 "cget, configure, deselect, flash, invoke, or select"
378 };
379
380 /*
381 * Forward declarations for procedures defined later in this file:
382 */
383
384 static void ButtonCmdDeletedProc _ANSI_ARGS_((
385 ClientData clientData));
386 static int ButtonCreate _ANSI_ARGS_((ClientData clientData,
387 Tcl_Interp *interp, int argc, char **argv,
388 int type));
389 static void ButtonEventProc _ANSI_ARGS_((ClientData clientData,
390 XEvent *eventPtr));
391 static void ButtonImageProc _ANSI_ARGS_((ClientData clientData,
392 int x, int y, int width, int height,
393 int imgWidth, int imgHeight));
394 static void ButtonSelectImageProc _ANSI_ARGS_((
395 ClientData clientData, int x, int y, int width,
396 int height, int imgWidth, int imgHeight));
397 static char * ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
398 Tcl_Interp *interp, char *name1, char *name2,
399 int flags));
400 static char * ButtonVarProc _ANSI_ARGS_((ClientData clientData,
401 Tcl_Interp *interp, char *name1, char *name2,
402 int flags));
403 static int ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
404 Tcl_Interp *interp, int argc, char **argv));
405 static void ComputeButtonGeometry _ANSI_ARGS_((Button *butPtr));
406 static int ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
407 Button *butPtr, int argc, char **argv,
408 int flags));
409 static void DestroyButton _ANSI_ARGS_((Button *butPtr));
410 static void DisplayButton _ANSI_ARGS_((ClientData clientData));
411 static int InvokeButton _ANSI_ARGS_((Button *butPtr));
412
413 /*
414 *--------------------------------------------------------------
415 *
416 * Tk_ButtonCmd, Tk_CheckbuttonCmd, Tk_LabelCmd, Tk_RadiobuttonCmd --
417 *
418 * These procedures are invoked to process the "button", "label",
419 * "radiobutton", and "checkbutton" Tcl commands. See the
420 * user documentation for details on what they do.
421 *
422 * Results:
423 * A standard Tcl result.
424 *
425 * Side effects:
426 * See the user documentation. These procedures are just wrappers;
427 * they call ButtonCreate to do all of the real work.
428 *
429 *--------------------------------------------------------------
430 */
431
432 int
Tk_ButtonCmd(clientData,interp,argc,argv)433 Tk_ButtonCmd(clientData, interp, argc, argv)
434 ClientData clientData; /* Main window associated with
435 * interpreter. */
436 Tcl_Interp *interp; /* Current interpreter. */
437 int argc; /* Number of arguments. */
438 char **argv; /* Argument strings. */
439 {
440 return ButtonCreate(clientData, interp, argc, argv, TYPE_BUTTON);
441 }
442
443 int
Tk_CheckbuttonCmd(clientData,interp,argc,argv)444 Tk_CheckbuttonCmd(clientData, interp, argc, argv)
445 ClientData clientData; /* Main window associated with
446 * interpreter. */
447 Tcl_Interp *interp; /* Current interpreter. */
448 int argc; /* Number of arguments. */
449 char **argv; /* Argument strings. */
450 {
451 return ButtonCreate(clientData, interp, argc, argv, TYPE_CHECK_BUTTON);
452 }
453
454 int
Tk_LabelCmd(clientData,interp,argc,argv)455 Tk_LabelCmd(clientData, interp, argc, argv)
456 ClientData clientData; /* Main window associated with
457 * interpreter. */
458 Tcl_Interp *interp; /* Current interpreter. */
459 int argc; /* Number of arguments. */
460 char **argv; /* Argument strings. */
461 {
462 return ButtonCreate(clientData, interp, argc, argv, TYPE_LABEL);
463 }
464
465 int
Tk_RadiobuttonCmd(clientData,interp,argc,argv)466 Tk_RadiobuttonCmd(clientData, interp, argc, argv)
467 ClientData clientData; /* Main window associated with
468 * interpreter. */
469 Tcl_Interp *interp; /* Current interpreter. */
470 int argc; /* Number of arguments. */
471 char **argv; /* Argument strings. */
472 {
473 return ButtonCreate(clientData, interp, argc, argv, TYPE_RADIO_BUTTON);
474 }
475
476 /*
477 *--------------------------------------------------------------
478 *
479 * ButtonCreate --
480 *
481 * This procedure does all the real work of implementing the
482 * "button", "label", "radiobutton", and "checkbutton" Tcl
483 * commands. See the user documentation for details on what it does.
484 *
485 * Results:
486 * A standard Tcl result.
487 *
488 * Side effects:
489 * See the user documentation.
490 *
491 *--------------------------------------------------------------
492 */
493
494 static int
ButtonCreate(clientData,interp,argc,argv,type)495 ButtonCreate(clientData, interp, argc, argv, type)
496 ClientData clientData; /* Main window associated with
497 * interpreter. */
498 Tcl_Interp *interp; /* Current interpreter. */
499 int argc; /* Number of arguments. */
500 char **argv; /* Argument strings. */
501 int type; /* Type of button to create: TYPE_LABEL,
502 * TYPE_BUTTON, TYPE_CHECK_BUTTON, or
503 * TYPE_RADIO_BUTTON. */
504 {
505 register Button *butPtr;
506 Tk_Window tkwin = (Tk_Window) clientData;
507 Tk_Window new;
508
509 if (argc < 2) {
510 Tcl_AppendResult(interp, "wrong # args: should be \"",
511 argv[0], " pathName ?options?\"", (char *) NULL);
512 return TCL_ERROR;
513 }
514
515 /*
516 * Create the new window.
517 */
518
519 new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
520 if (new == NULL) {
521 return TCL_ERROR;
522 }
523
524 /*
525 * Initialize the data structure for the button.
526 */
527
528 butPtr = (Button *) ckalloc(sizeof(Button));
529 butPtr->tkwin = new;
530 butPtr->display = Tk_Display(new);
531 butPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin),
532 ButtonWidgetCmd, (ClientData) butPtr, ButtonCmdDeletedProc);
533 butPtr->interp = interp;
534 butPtr->type = type;
535 butPtr->text = NULL;
536 butPtr->numChars = 0;
537 butPtr->underline = -1;
538 butPtr->textVarName = NULL;
539 butPtr->bitmap = None;
540 butPtr->imageString = NULL;
541 butPtr->image = NULL;
542 butPtr->selectImageString = NULL;
543 butPtr->selectImage = NULL;
544 butPtr->state = tkNormalUid;
545 butPtr->normalBorder = NULL;
546 butPtr->activeBorder = NULL;
547 butPtr->borderWidth = 0;
548 butPtr->relief = TK_RELIEF_FLAT;
549 butPtr->highlightWidth = 0;
550 butPtr->highlightBgColorPtr = NULL;
551 butPtr->highlightColorPtr = NULL;
552 butPtr->inset = 0;
553 butPtr->fontPtr = NULL;
554 butPtr->normalFg = NULL;
555 butPtr->activeFg = NULL;
556 butPtr->disabledFg = NULL;
557 butPtr->normalTextGC = None;
558 butPtr->activeTextGC = None;
559 butPtr->gray = None;
560 butPtr->disabledGC = None;
561 butPtr->copyGC = None;
562 butPtr->widthString = NULL;
563 butPtr->heightString = NULL;
564 butPtr->width = 0;
565 butPtr->height = 0;
566 butPtr->wrapLength = 0;
567 butPtr->padX = 0;
568 butPtr->padY = 0;
569 butPtr->anchor = TK_ANCHOR_CENTER;
570 butPtr->justify = TK_JUSTIFY_CENTER;
571 butPtr->indicatorOn = 0;
572 butPtr->selectBorder = NULL;
573 butPtr->indicatorSpace = 0;
574 butPtr->indicatorDiameter = 0;
575 butPtr->selVarName = NULL;
576 butPtr->onValue = NULL;
577 butPtr->offValue = NULL;
578 butPtr->cursor = None;
579 butPtr->command = NULL;
580 butPtr->takeFocus = NULL;
581 butPtr->flags = 0;
582
583 Tk_SetClass(new, classNames[type]);
584 Tk_CreateEventHandler(butPtr->tkwin,
585 ExposureMask|StructureNotifyMask|FocusChangeMask,
586 ButtonEventProc, (ClientData) butPtr);
587 if (ConfigureButton(interp, butPtr, argc-2, argv+2,
588 configFlags[type]) != TCL_OK) {
589 Tk_DestroyWindow(butPtr->tkwin);
590 return TCL_ERROR;
591 }
592
593 interp->result = Tk_PathName(butPtr->tkwin);
594 return TCL_OK;
595 }
596
597 /*
598 *--------------------------------------------------------------
599 *
600 * ButtonWidgetCmd --
601 *
602 * This procedure is invoked to process the Tcl command
603 * that corresponds to a widget managed by this module.
604 * See the user documentation for details on what it does.
605 *
606 * Results:
607 * A standard Tcl result.
608 *
609 * Side effects:
610 * See the user documentation.
611 *
612 *--------------------------------------------------------------
613 */
614
615 static int
ButtonWidgetCmd(clientData,interp,argc,argv)616 ButtonWidgetCmd(clientData, interp, argc, argv)
617 ClientData clientData; /* Information about button widget. */
618 Tcl_Interp *interp; /* Current interpreter. */
619 int argc; /* Number of arguments. */
620 char **argv; /* Argument strings. */
621 {
622 register Button *butPtr = (Button *) clientData;
623 int result = TCL_OK;
624 size_t length;
625 int c;
626
627 if (argc < 2) {
628 sprintf(interp->result,
629 "wrong # args: should be \"%.50s option ?arg arg ...?\"",
630 argv[0]);
631 return TCL_ERROR;
632 }
633 Tcl_Preserve((ClientData) butPtr);
634 c = argv[1][0];
635 length = strlen(argv[1]);
636 if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
637 && (length >= 2)) {
638 if (argc != 3) {
639 Tcl_AppendResult(interp, "wrong # args: should be \"",
640 argv[0], " cget option\"",
641 (char *) NULL);
642 goto error;
643 }
644 result = Tk_ConfigureValue(interp, butPtr->tkwin, configSpecs,
645 (char *) butPtr, argv[2], configFlags[butPtr->type]);
646 } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
647 && (length >= 2)) {
648 if (argc == 2) {
649 result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
650 (char *) butPtr, (char *) NULL, configFlags[butPtr->type]);
651 } else if (argc == 3) {
652 result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
653 (char *) butPtr, argv[2],
654 configFlags[butPtr->type]);
655 } else {
656 result = ConfigureButton(interp, butPtr, argc-2, argv+2,
657 configFlags[butPtr->type] | TK_CONFIG_ARGV_ONLY);
658 }
659 } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
660 && (butPtr->type >= TYPE_CHECK_BUTTON)) {
661 if (argc > 2) {
662 sprintf(interp->result,
663 "wrong # args: should be \"%.50s deselect\"",
664 argv[0]);
665 goto error;
666 }
667 if (butPtr->type == TYPE_CHECK_BUTTON) {
668 if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
669 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
670 result = TCL_ERROR;
671 }
672 } else if (butPtr->flags & SELECTED) {
673 if (Tcl_SetVar(interp, butPtr->selVarName, "",
674 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
675 result = TCL_ERROR;
676 };
677 }
678 } else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0)
679 && (butPtr->type != TYPE_LABEL)) {
680 int i;
681
682 if (argc > 2) {
683 sprintf(interp->result,
684 "wrong # args: should be \"%.50s flash\"",
685 argv[0]);
686 goto error;
687 }
688 if (butPtr->state != tkDisabledUid) {
689 for (i = 0; i < 4; i++) {
690 butPtr->state = (butPtr->state == tkNormalUid)
691 ? tkActiveUid : tkNormalUid;
692 Tk_SetBackgroundFromBorder(butPtr->tkwin,
693 (butPtr->state == tkActiveUid) ? butPtr->activeBorder
694 : butPtr->normalBorder);
695 DisplayButton((ClientData) butPtr);
696
697 /*
698 * Special note: must cancel any existing idle handler
699 * for DisplayButton; it's no longer needed, and DisplayButton
700 * cleared the REDRAW_PENDING flag.
701 */
702
703 Tcl_CancelIdleCall(DisplayButton, (ClientData) butPtr);
704 XFlush(butPtr->display);
705 Tcl_Sleep(50);
706 }
707 }
708 } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
709 && (butPtr->type > TYPE_LABEL)) {
710 if (argc > 2) {
711 sprintf(interp->result,
712 "wrong # args: should be \"%.50s invoke\"",
713 argv[0]);
714 goto error;
715 }
716 if (butPtr->state != tkDisabledUid) {
717 result = InvokeButton(butPtr);
718 }
719 } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
720 && (butPtr->type >= TYPE_CHECK_BUTTON)) {
721 if (argc > 2) {
722 sprintf(interp->result,
723 "wrong # args: should be \"%.50s select\"",
724 argv[0]);
725 goto error;
726 }
727 if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
728 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
729 result = TCL_ERROR;
730 }
731 } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
732 && (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
733 if (argc > 2) {
734 sprintf(interp->result,
735 "wrong # args: should be \"%.50s toggle\"",
736 argv[0]);
737 goto error;
738 }
739 if (butPtr->flags & SELECTED) {
740 if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
741 TCL_GLOBAL_ONLY) == NULL) {
742 result = TCL_ERROR;
743 }
744 } else {
745 if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
746 TCL_GLOBAL_ONLY) == NULL) {
747 result = TCL_ERROR;
748 }
749 }
750 } else {
751 sprintf(interp->result,
752 "bad option \"%.50s\": must be %s", argv[1],
753 optionStrings[butPtr->type]);
754 goto error;
755 }
756 Tcl_Release((ClientData) butPtr);
757 return result;
758
759 error:
760 Tcl_Release((ClientData) butPtr);
761 return TCL_ERROR;
762 }
763
764 /*
765 *----------------------------------------------------------------------
766 *
767 * DestroyButton --
768 *
769 * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
770 * to clean up the internal structure of a button at a safe time
771 * (when no-one is using it anymore).
772 *
773 * Results:
774 * None.
775 *
776 * Side effects:
777 * Everything associated with the widget is freed up.
778 *
779 *----------------------------------------------------------------------
780 */
781
782 static void
DestroyButton(butPtr)783 DestroyButton(butPtr)
784 Button *butPtr; /* Info about button widget. */
785 {
786 /*
787 * Free up all the stuff that requires special handling, then
788 * let Tk_FreeOptions handle all the standard option-related
789 * stuff.
790 */
791
792 if (butPtr->textVarName != NULL) {
793 Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
794 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
795 ButtonTextVarProc, (ClientData) butPtr);
796 }
797 if (butPtr->image != NULL) {
798 Tk_FreeImage(butPtr->image);
799 }
800 if (butPtr->selectImage != NULL) {
801 Tk_FreeImage(butPtr->selectImage);
802 }
803 if (butPtr->normalTextGC != None) {
804 Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
805 }
806 if (butPtr->activeTextGC != None) {
807 Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
808 }
809 if (butPtr->gray != None) {
810 Tk_FreeBitmap(butPtr->display, butPtr->gray);
811 }
812 if (butPtr->disabledGC != None) {
813 Tk_FreeGC(butPtr->display, butPtr->disabledGC);
814 }
815 if (butPtr->copyGC != None) {
816 Tk_FreeGC(butPtr->display, butPtr->copyGC);
817 }
818 if (butPtr->selVarName != NULL) {
819 Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
820 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
821 ButtonVarProc, (ClientData) butPtr);
822 }
823 Tk_FreeOptions(configSpecs, (char *) butPtr, butPtr->display,
824 configFlags[butPtr->type]);
825 Tcl_EventuallyFree((ClientData)butPtr, TCL_DYNAMIC);
826 }
827
828 /*
829 *----------------------------------------------------------------------
830 *
831 * ConfigureButton --
832 *
833 * This procedure is called to process an argv/argc list, plus
834 * the Tk option database, in order to configure (or
835 * reconfigure) a button widget.
836 *
837 * Results:
838 * The return value is a standard Tcl result. If TCL_ERROR is
839 * returned, then interp->result contains an error message.
840 *
841 * Side effects:
842 * Configuration information, such as text string, colors, font,
843 * etc. get set for butPtr; old resources get freed, if there
844 * were any. The button is redisplayed.
845 *
846 *----------------------------------------------------------------------
847 */
848
849 static int
ConfigureButton(interp,butPtr,argc,argv,flags)850 ConfigureButton(interp, butPtr, argc, argv, flags)
851 Tcl_Interp *interp; /* Used for error reporting. */
852 register Button *butPtr; /* Information about widget; may or may
853 * not already have values for some fields. */
854 int argc; /* Number of valid entries in argv. */
855 char **argv; /* Arguments. */
856 int flags; /* Flags to pass to Tk_ConfigureWidget. */
857 {
858 XGCValues gcValues;
859 GC newGC;
860 unsigned long mask;
861 Tk_Image image;
862
863 /*
864 * Eliminate any existing trace on variables monitored by the button.
865 */
866
867 if (butPtr->textVarName != NULL) {
868 Tcl_UntraceVar(interp, butPtr->textVarName,
869 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
870 ButtonTextVarProc, (ClientData) butPtr);
871 }
872 if (butPtr->selVarName != NULL) {
873 Tcl_UntraceVar(interp, butPtr->selVarName,
874 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
875 ButtonVarProc, (ClientData) butPtr);
876 }
877
878 if (Tk_ConfigureWidget(interp, butPtr->tkwin, configSpecs,
879 argc, argv, (char *) butPtr, flags) != TCL_OK) {
880 return TCL_ERROR;
881 }
882
883 /*
884 * A few options need special processing, such as setting the
885 * background from a 3-D border, or filling in complicated
886 * defaults that couldn't be specified to Tk_ConfigureWidget.
887 */
888
889 if ((butPtr->state == tkActiveUid) && !Tk_StrictMotif(butPtr->tkwin)) {
890 Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
891 } else {
892 Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
893 if ((butPtr->state != tkNormalUid) && (butPtr->state != tkActiveUid)
894 && (butPtr->state != tkDisabledUid)) {
895 Tcl_AppendResult(interp, "bad state value \"", butPtr->state,
896 "\": must be normal, active, or disabled", (char *) NULL);
897 butPtr->state = tkNormalUid;
898 return TCL_ERROR;
899 }
900 }
901
902 if (butPtr->highlightWidth < 0) {
903 butPtr->highlightWidth = 0;
904 }
905
906 gcValues.font = butPtr->fontPtr->fid;
907 gcValues.foreground = butPtr->normalFg->pixel;
908 gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
909
910 /*
911 * Note: GraphicsExpose events are disabled in normalTextGC because it's
912 * used to copy stuff from an off-screen pixmap onto the screen (we know
913 * that there's no problem with obscured areas).
914 */
915
916 gcValues.graphics_exposures = False;
917 newGC = Tk_GetGC(butPtr->tkwin,
918 GCForeground|GCBackground|GCFont|GCGraphicsExposures,
919 &gcValues);
920 if (butPtr->normalTextGC != None) {
921 Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
922 }
923 butPtr->normalTextGC = newGC;
924
925 if (butPtr->activeFg != NULL) {
926 gcValues.font = butPtr->fontPtr->fid;
927 gcValues.foreground = butPtr->activeFg->pixel;
928 gcValues.background = Tk_3DBorderColor(butPtr->activeBorder)->pixel;
929 newGC = Tk_GetGC(butPtr->tkwin,
930 GCForeground|GCBackground|GCFont, &gcValues);
931 if (butPtr->activeTextGC != None) {
932 Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
933 }
934 butPtr->activeTextGC = newGC;
935 }
936
937 if (butPtr->type != TYPE_LABEL) {
938 gcValues.font = butPtr->fontPtr-> fid;
939 gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
940 if ((butPtr->disabledFg != NULL) && (butPtr->imageString == NULL)) {
941 gcValues.foreground = butPtr->disabledFg->pixel;
942 mask = GCForeground|GCBackground|GCFont;
943 } else {
944 gcValues.foreground = gcValues.background;
945 if (butPtr->gray == None) {
946 butPtr->gray = Tk_GetBitmap(interp, butPtr->tkwin,
947 Tk_GetUid("gray50"));
948 if (butPtr->gray == None) {
949 return TCL_ERROR;
950 }
951 }
952 gcValues.fill_style = FillStippled;
953 gcValues.stipple = butPtr->gray;
954 mask = GCForeground|GCFillStyle|GCStipple;
955 }
956 newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
957 if (butPtr->disabledGC != None) {
958 Tk_FreeGC(butPtr->display, butPtr->disabledGC);
959 }
960 butPtr->disabledGC = newGC;
961 }
962
963 if (butPtr->copyGC == None) {
964 butPtr->copyGC = Tk_GetGC(butPtr->tkwin, 0, &gcValues);
965 }
966
967 if (butPtr->padX < 0) {
968 butPtr->padX = 0;
969 }
970 if (butPtr->padY < 0) {
971 butPtr->padY = 0;
972 }
973
974 if (butPtr->type >= TYPE_CHECK_BUTTON) {
975 char *value;
976
977 if (butPtr->selVarName == NULL) {
978 butPtr->selVarName = (char *) ckalloc((unsigned)
979 (strlen(Tk_Name(butPtr->tkwin)) + 1));
980 strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin));
981 }
982
983 /*
984 * Select the button if the associated variable has the
985 * appropriate value, initialize the variable if it doesn't
986 * exist, then set a trace on the variable to monitor future
987 * changes to its value.
988 */
989
990 value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
991 butPtr->flags &= ~SELECTED;
992 if (value != NULL) {
993 if (strcmp(value, butPtr->onValue) == 0) {
994 butPtr->flags |= SELECTED;
995 }
996 } else {
997 if (Tcl_SetVar(interp, butPtr->selVarName,
998 (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
999 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
1000 return TCL_ERROR;
1001 }
1002 }
1003 Tcl_TraceVar(interp, butPtr->selVarName,
1004 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1005 ButtonVarProc, (ClientData) butPtr);
1006 }
1007
1008 /*
1009 * Get the images for the widget, if there are any. Allocate the
1010 * new images before freeing the old ones, so that the reference
1011 * counts don't go to zero and cause image data to be discarded.
1012 */
1013
1014 if (butPtr->imageString != NULL) {
1015 image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
1016 butPtr->imageString, ButtonImageProc, (ClientData) butPtr);
1017 if (image == NULL) {
1018 return TCL_ERROR;
1019 }
1020 } else {
1021 image = NULL;
1022 }
1023 if (butPtr->image != NULL) {
1024 Tk_FreeImage(butPtr->image);
1025 }
1026 butPtr->image = image;
1027 if (butPtr->selectImageString != NULL) {
1028 image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
1029 butPtr->selectImageString, ButtonSelectImageProc,
1030 (ClientData) butPtr);
1031 if (image == NULL) {
1032 return TCL_ERROR;
1033 }
1034 } else {
1035 image = NULL;
1036 }
1037 if (butPtr->selectImage != NULL) {
1038 Tk_FreeImage(butPtr->selectImage);
1039 }
1040 butPtr->selectImage = image;
1041
1042 if ((butPtr->image == NULL) && (butPtr->bitmap == None)
1043 && (butPtr->textVarName != NULL)) {
1044 /*
1045 * The button must display the value of a variable: set up a trace
1046 * on the variable's value, create the variable if it doesn't
1047 * exist, and fetch its current value.
1048 */
1049
1050 char *value;
1051
1052 value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
1053 if (value == NULL) {
1054 if (Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
1055 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
1056 return TCL_ERROR;
1057 }
1058 } else {
1059 if (butPtr->text != NULL) {
1060 ckfree(butPtr->text);
1061 }
1062 butPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
1063 strcpy(butPtr->text, value);
1064 }
1065 Tcl_TraceVar(interp, butPtr->textVarName,
1066 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1067 ButtonTextVarProc, (ClientData) butPtr);
1068 }
1069
1070 if ((butPtr->bitmap != None) || (butPtr->image != NULL)) {
1071 if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->widthString,
1072 &butPtr->width) != TCL_OK) {
1073 widthError:
1074 Tcl_AddErrorInfo(interp, "\n (processing -width option)");
1075 return TCL_ERROR;
1076 }
1077 if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->heightString,
1078 &butPtr->height) != TCL_OK) {
1079 heightError:
1080 Tcl_AddErrorInfo(interp, "\n (processing -height option)");
1081 return TCL_ERROR;
1082 }
1083 } else {
1084 if (Tcl_GetInt(interp, butPtr->widthString, &butPtr->width)
1085 != TCL_OK) {
1086 goto widthError;
1087 }
1088 if (Tcl_GetInt(interp, butPtr->heightString, &butPtr->height)
1089 != TCL_OK) {
1090 goto heightError;
1091 }
1092 }
1093 ComputeButtonGeometry(butPtr);
1094
1095 /*
1096 * Lastly, arrange for the button to be redisplayed.
1097 */
1098
1099 if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
1100 Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
1101 butPtr->flags |= REDRAW_PENDING;
1102 }
1103
1104 return TCL_OK;
1105 }
1106
1107 /*
1108 *----------------------------------------------------------------------
1109 *
1110 * DisplayButton --
1111 *
1112 * This procedure is invoked to display a button widget. It is
1113 * normally invoked as an idle handler.
1114 *
1115 * Results:
1116 * None.
1117 *
1118 * Side effects:
1119 * Commands are output to X to display the button in its
1120 * current mode. The REDRAW_PENDING flag is cleared.
1121 *
1122 *----------------------------------------------------------------------
1123 */
1124
1125 static void
DisplayButton(clientData)1126 DisplayButton(clientData)
1127 ClientData clientData; /* Information about widget. */
1128 {
1129 register Button *butPtr = (Button *) clientData;
1130 GC gc;
1131 Tk_3DBorder border;
1132 Pixmap pixmap;
1133 int x = 0; /* Initialization only needed to stop
1134 * compiler warning. */
1135 int y, relief;
1136 register Tk_Window tkwin = butPtr->tkwin;
1137 int width, height;
1138 int offset; /* 0 means this is a label widget. 1 means
1139 * it is a flavor of button, so we offset
1140 * the text to make the button appear to
1141 * move up and down as the relief changes. */
1142
1143 butPtr->flags &= ~REDRAW_PENDING;
1144 if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
1145 return;
1146 }
1147
1148 border = butPtr->normalBorder;
1149 if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) {
1150 gc = butPtr->disabledGC;
1151 } else if ((butPtr->state == tkActiveUid)
1152 && !Tk_StrictMotif(butPtr->tkwin)) {
1153 gc = butPtr->activeTextGC;
1154 border = butPtr->activeBorder;
1155 } else {
1156 gc = butPtr->normalTextGC;
1157 }
1158 if ((butPtr->flags & SELECTED) && (butPtr->state != tkActiveUid)
1159 && (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) {
1160 border = butPtr->selectBorder;
1161 }
1162
1163 /*
1164 * Override the relief specified for the button if this is a
1165 * checkbutton or radiobutton and there's no indicator.
1166 */
1167
1168 relief = butPtr->relief;
1169 if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
1170 relief = (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN
1171 : TK_RELIEF_RAISED;
1172 }
1173
1174 offset = (butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin);
1175
1176 /*
1177 * In order to avoid screen flashes, this procedure redraws
1178 * the button in a pixmap, then copies the pixmap to the
1179 * screen in a single operation. This means that there's no
1180 * point in time where the on-sreen image has been cleared.
1181 */
1182
1183 pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
1184 Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
1185 Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
1186 Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
1187
1188 /*
1189 * Display image or bitmap or text for button.
1190 */
1191
1192 if (butPtr->image != None) {
1193 Tk_SizeOfImage(butPtr->image, &width, &height);
1194
1195 imageOrBitmap:
1196 switch (butPtr->anchor) {
1197 case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
1198 x = butPtr->inset + butPtr->indicatorSpace + offset;
1199 break;
1200 case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
1201 x = ((int) (Tk_Width(tkwin) + butPtr->indicatorSpace
1202 - width))/2;
1203 break;
1204 default:
1205 x = Tk_Width(tkwin) - butPtr->inset - width - offset;
1206 break;
1207 }
1208 switch (butPtr->anchor) {
1209 case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
1210 y = butPtr->inset + offset;
1211 break;
1212 case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
1213 y = ((int) (Tk_Height(tkwin) - height))/2;
1214 break;
1215 default:
1216 y = Tk_Height(tkwin) - butPtr->inset - height - offset;
1217 break;
1218 }
1219 if (relief == TK_RELIEF_RAISED) {
1220 x -= offset;
1221 y -= offset;
1222 } else if (relief == TK_RELIEF_SUNKEN) {
1223 x += offset;
1224 y += offset;
1225 }
1226 if (butPtr->image != NULL) {
1227 if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
1228 Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height, pixmap,
1229 x, y);
1230 } else {
1231 Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap,
1232 x, y);
1233 }
1234 } else {
1235 XSetClipOrigin(butPtr->display, gc, x, y);
1236 XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
1237 (unsigned int) width, (unsigned int) height, x, y, 1);
1238 XSetClipOrigin(butPtr->display, gc, 0, 0);
1239 }
1240 y += height/2;
1241 } else if (butPtr->bitmap != None) {
1242 Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
1243 goto imageOrBitmap;
1244 } else {
1245 switch (butPtr->anchor) {
1246 case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
1247 x = butPtr->inset + butPtr->padX + butPtr->indicatorSpace
1248 + offset;
1249 break;
1250 case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
1251 x = ((int) (Tk_Width(tkwin) + butPtr->indicatorSpace
1252 - butPtr->textWidth))/2;
1253 break;
1254 default:
1255 x = Tk_Width(tkwin) - butPtr->inset - butPtr->padX
1256 - butPtr->textWidth - offset;
1257 break;
1258 }
1259 switch (butPtr->anchor) {
1260 case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
1261 y = butPtr->inset + butPtr->padY + offset;
1262 break;
1263 case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
1264 y = ((int) (Tk_Height(tkwin) - butPtr->textHeight))/2;
1265 break;
1266 default:
1267 y = Tk_Height(tkwin) - butPtr->inset - butPtr->padY
1268 - butPtr->textHeight - offset;
1269 break;
1270 }
1271 if (relief == TK_RELIEF_RAISED) {
1272 x -= offset;
1273 y -= offset;
1274 } else if (relief == TK_RELIEF_SUNKEN) {
1275 x += offset;
1276 y += offset;
1277 }
1278 TkDisplayText(butPtr->display, pixmap, butPtr->fontPtr,
1279 butPtr->text, butPtr->numChars, x, y, butPtr->textWidth,
1280 butPtr->justify, butPtr->underline, gc);
1281 y += butPtr->textHeight/2;
1282 }
1283
1284 /*
1285 * Draw the indicator for check buttons and radio buttons. At this
1286 * point x and y refer to the top-left corner of the text or image
1287 * or bitmap.
1288 */
1289
1290 if ((butPtr->type == TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
1291 int dim;
1292
1293 dim = butPtr->indicatorDiameter;
1294 x -= butPtr->indicatorSpace;
1295 y -= dim/2;
1296 if (dim > 2*butPtr->borderWidth) {
1297 Tk_Draw3DRectangle(tkwin, pixmap, border, x, y, dim, dim,
1298 butPtr->borderWidth,
1299 (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
1300 TK_RELIEF_RAISED);
1301 x += butPtr->borderWidth;
1302 y += butPtr->borderWidth;
1303 dim -= 2*butPtr->borderWidth;
1304 if (butPtr->flags & SELECTED) {
1305 GC gc;
1306
1307 gc = Tk_3DBorderGC(tkwin,(butPtr->selectBorder != NULL)
1308 ? butPtr->selectBorder : butPtr->normalBorder,
1309 TK_3D_FLAT_GC);
1310 XFillRectangle(butPtr->display, pixmap, gc, x, y,
1311 (unsigned int) dim, (unsigned int) dim);
1312 } else {
1313 Tk_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, x, y,
1314 dim, dim, butPtr->borderWidth, TK_RELIEF_FLAT);
1315 }
1316 }
1317 } else if ((butPtr->type == TYPE_RADIO_BUTTON) && butPtr->indicatorOn) {
1318 XPoint points[4];
1319 int radius;
1320
1321 radius = butPtr->indicatorDiameter/2;
1322 points[0].x = x - butPtr->indicatorSpace;
1323 points[0].y = y;
1324 points[1].x = points[0].x + radius;
1325 points[1].y = points[0].y + radius;
1326 points[2].x = points[1].x + radius;
1327 points[2].y = points[0].y;
1328 points[3].x = points[1].x;
1329 points[3].y = points[0].y - radius;
1330 if (butPtr->flags & SELECTED) {
1331 GC gc;
1332
1333 gc = Tk_3DBorderGC(tkwin, (butPtr->selectBorder != NULL)
1334 ? butPtr->selectBorder : butPtr->normalBorder,
1335 TK_3D_FLAT_GC);
1336 XFillPolygon(butPtr->display, pixmap, gc, points, 4, Convex,
1337 CoordModeOrigin);
1338 } else {
1339 Tk_Fill3DPolygon(tkwin, pixmap, butPtr->normalBorder, points,
1340 4, butPtr->borderWidth, TK_RELIEF_FLAT);
1341 }
1342 Tk_Draw3DPolygon(tkwin, pixmap, border, points, 4, butPtr->borderWidth,
1343 (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
1344 TK_RELIEF_RAISED);
1345 }
1346
1347 /*
1348 * If the button is disabled with a stipple rather than a special
1349 * foreground color, generate the stippled effect. If the widget
1350 * is selected and we use a different background color when selected,
1351 * must temporarily modify the GC.
1352 */
1353
1354 if ((butPtr->state == tkDisabledUid)
1355 && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
1356 if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
1357 && (butPtr->selectBorder != NULL)) {
1358 XSetForeground(butPtr->display, butPtr->disabledGC,
1359 Tk_3DBorderColor(butPtr->selectBorder)->pixel);
1360 }
1361 XFillRectangle(butPtr->display, pixmap, butPtr->disabledGC,
1362 butPtr->inset, butPtr->inset,
1363 (unsigned) (Tk_Width(tkwin) - 2*butPtr->inset),
1364 (unsigned) (Tk_Height(tkwin) - 2*butPtr->inset));
1365 if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
1366 && (butPtr->selectBorder != NULL)) {
1367 XSetForeground(butPtr->display, butPtr->disabledGC,
1368 Tk_3DBorderColor(butPtr->normalBorder)->pixel);
1369 }
1370 }
1371
1372 /*
1373 * Draw the border and traversal highlight last. This way, if the
1374 * button's contents overflow they'll be covered up by the border.
1375 */
1376
1377 if (relief != TK_RELIEF_FLAT) {
1378 Tk_Draw3DRectangle(tkwin, pixmap, border,
1379 butPtr->highlightWidth, butPtr->highlightWidth,
1380 Tk_Width(tkwin) - 2*butPtr->highlightWidth,
1381 Tk_Height(tkwin) - 2*butPtr->highlightWidth,
1382 butPtr->borderWidth, relief);
1383 }
1384 if (butPtr->highlightWidth != 0) {
1385 GC gc;
1386
1387 if (butPtr->flags & GOT_FOCUS) {
1388 gc = Tk_GCForColor(butPtr->highlightColorPtr, pixmap);
1389 } else {
1390 gc = Tk_GCForColor(butPtr->highlightBgColorPtr, pixmap);
1391 }
1392 Tk_DrawFocusHighlight(tkwin, gc, butPtr->highlightWidth, pixmap);
1393 }
1394
1395 /*
1396 * Copy the information from the off-screen pixmap onto the screen,
1397 * then delete the pixmap.
1398 */
1399
1400 XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
1401 butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
1402 (unsigned) Tk_Height(tkwin), 0, 0);
1403 Tk_FreePixmap(butPtr->display, pixmap);
1404 }
1405
1406 /*
1407 *--------------------------------------------------------------
1408 *
1409 * ButtonEventProc --
1410 *
1411 * This procedure is invoked by the Tk dispatcher for various
1412 * events on buttons.
1413 *
1414 * Results:
1415 * None.
1416 *
1417 * Side effects:
1418 * When the window gets deleted, internal structures get
1419 * cleaned up. When it gets exposed, it is redisplayed.
1420 *
1421 *--------------------------------------------------------------
1422 */
1423
1424 static void
ButtonEventProc(clientData,eventPtr)1425 ButtonEventProc(clientData, eventPtr)
1426 ClientData clientData; /* Information about window. */
1427 XEvent *eventPtr; /* Information about event. */
1428 {
1429 Button *butPtr = (Button *) clientData;
1430 if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
1431 goto redraw;
1432 } else if (eventPtr->type == ConfigureNotify) {
1433 /*
1434 * Must redraw after size changes, since layout could have changed
1435 * and borders will need to be redrawn.
1436 */
1437
1438 goto redraw;
1439 } else if (eventPtr->type == DestroyNotify) {
1440 if (butPtr->tkwin != NULL) {
1441 butPtr->tkwin = NULL;
1442 Tcl_DeleteCommand(butPtr->interp,
1443 Tcl_GetCommandName(butPtr->interp, butPtr->widgetCmd));
1444 }
1445 if (butPtr->flags & REDRAW_PENDING) {
1446 Tcl_CancelIdleCall(DisplayButton, (ClientData) butPtr);
1447 }
1448 DestroyButton(butPtr);
1449 } else if (eventPtr->type == FocusIn) {
1450 if (eventPtr->xfocus.detail != NotifyInferior) {
1451 butPtr->flags |= GOT_FOCUS;
1452 if (butPtr->highlightWidth > 0) {
1453 goto redraw;
1454 }
1455 }
1456 } else if (eventPtr->type == FocusOut) {
1457 if (eventPtr->xfocus.detail != NotifyInferior) {
1458 butPtr->flags &= ~GOT_FOCUS;
1459 if (butPtr->highlightWidth > 0) {
1460 goto redraw;
1461 }
1462 }
1463 }
1464 return;
1465
1466 redraw:
1467 if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
1468 Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
1469 butPtr->flags |= REDRAW_PENDING;
1470 }
1471 }
1472
1473 /*
1474 *----------------------------------------------------------------------
1475 *
1476 * ButtonCmdDeletedProc --
1477 *
1478 * This procedure is invoked when a widget command is deleted. If
1479 * the widget isn't already in the process of being destroyed,
1480 * this command destroys it.
1481 *
1482 * Results:
1483 * None.
1484 *
1485 * Side effects:
1486 * The widget is destroyed.
1487 *
1488 *----------------------------------------------------------------------
1489 */
1490
1491 static void
ButtonCmdDeletedProc(clientData)1492 ButtonCmdDeletedProc(clientData)
1493 ClientData clientData; /* Pointer to widget record for widget. */
1494 {
1495 Button *butPtr = (Button *) clientData;
1496 Tk_Window tkwin = butPtr->tkwin;
1497
1498 /*
1499 * This procedure could be invoked either because the window was
1500 * destroyed and the command was then deleted (in which case tkwin
1501 * is NULL) or because the command was deleted, and then this procedure
1502 * destroys the widget.
1503 */
1504
1505 if (tkwin != NULL) {
1506 butPtr->tkwin = NULL;
1507 Tk_DestroyWindow(tkwin);
1508 }
1509 }
1510
1511 /*
1512 *----------------------------------------------------------------------
1513 *
1514 * ComputeButtonGeometry --
1515 *
1516 * After changes in a button's text or bitmap, this procedure
1517 * recomputes the button's geometry and passes this information
1518 * along to the geometry manager for the window.
1519 *
1520 * Results:
1521 * None.
1522 *
1523 * Side effects:
1524 * The button's window may change size.
1525 *
1526 *----------------------------------------------------------------------
1527 */
1528
1529 static void
ComputeButtonGeometry(butPtr)1530 ComputeButtonGeometry(butPtr)
1531 register Button *butPtr; /* Button whose geometry may have changed. */
1532 {
1533 int width, height;
1534
1535 if (butPtr->highlightWidth < 0) {
1536 butPtr->highlightWidth = 0;
1537 }
1538 butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;
1539 butPtr->indicatorSpace = 0;
1540 if (butPtr->image != NULL) {
1541 Tk_SizeOfImage(butPtr->image, &width, &height);
1542 imageOrBitmap:
1543 if (butPtr->width > 0) {
1544 width = butPtr->width;
1545 }
1546 if (butPtr->height > 0) {
1547 height = butPtr->height;
1548 }
1549 if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
1550 butPtr->indicatorSpace = height;
1551 if (butPtr->type == TYPE_CHECK_BUTTON) {
1552 butPtr->indicatorDiameter = (65*height)/100;
1553 } else {
1554 butPtr->indicatorDiameter = (75*height)/100;
1555 }
1556 }
1557 } else if (butPtr->bitmap != None) {
1558 Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
1559 goto imageOrBitmap;
1560 } else {
1561 butPtr->numChars = strlen(butPtr->text);
1562 TkComputeTextGeometry(butPtr->fontPtr, butPtr->text,
1563 butPtr->numChars, butPtr->wrapLength, &butPtr->textWidth,
1564 &butPtr->textHeight);
1565 width = butPtr->textWidth;
1566 height = butPtr->textHeight;
1567 if (butPtr->width > 0) {
1568 width = butPtr->width * XTextWidth(butPtr->fontPtr, "0", 1);
1569 }
1570 if (butPtr->height > 0) {
1571 height = butPtr->height * (butPtr->fontPtr->ascent
1572 + butPtr->fontPtr->descent);
1573 }
1574 if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
1575 butPtr->indicatorDiameter = butPtr->fontPtr->ascent
1576 + butPtr->fontPtr->descent;
1577 if (butPtr->type == TYPE_CHECK_BUTTON) {
1578 butPtr->indicatorDiameter = (80*butPtr->indicatorDiameter)/100;
1579 }
1580 butPtr->indicatorSpace = butPtr->indicatorDiameter
1581 + XTextWidth(butPtr->fontPtr, "0", 1);
1582 }
1583 }
1584
1585 /*
1586 * When issuing the geometry request, add extra space for the indicator,
1587 * if any, and for the border and padding, plus two extra pixels so the
1588 * display can be offset by 1 pixel in either direction for the raised
1589 * or lowered effect.
1590 */
1591
1592 if ((butPtr->image == NULL) && (butPtr->bitmap == None)) {
1593 width += 2*butPtr->padX;
1594 height += 2*butPtr->padY;
1595 }
1596 if ((butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin)) {
1597 width += 2;
1598 height += 2;
1599 }
1600 Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
1601 + 2*butPtr->inset), (int) (height + 2*butPtr->inset));
1602 Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
1603 }
1604
1605 /*
1606 *----------------------------------------------------------------------
1607 *
1608 * InvokeButton --
1609 *
1610 * This procedure is called to carry out the actions associated
1611 * with a button, such as invoking a Tcl command or setting a
1612 * variable. This procedure is invoked, for example, when the
1613 * button is invoked via the mouse.
1614 *
1615 * Results:
1616 * A standard Tcl return value. Information is also left in
1617 * interp->result.
1618 *
1619 * Side effects:
1620 * Depends on the button and its associated command.
1621 *
1622 *----------------------------------------------------------------------
1623 */
1624
1625 static int
InvokeButton(butPtr)1626 InvokeButton(butPtr)
1627 register Button *butPtr; /* Information about button. */
1628 {
1629 if (butPtr->type == TYPE_CHECK_BUTTON) {
1630 if (butPtr->flags & SELECTED) {
1631 if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue,
1632 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
1633 return TCL_ERROR;
1634 }
1635 } else {
1636 if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
1637 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
1638 return TCL_ERROR;
1639 }
1640 }
1641 } else if (butPtr->type == TYPE_RADIO_BUTTON) {
1642 if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
1643 TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
1644 return TCL_ERROR;
1645 }
1646 }
1647 if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
1648 return TkCopyAndGlobalEval(butPtr->interp, butPtr->command);
1649 }
1650 return TCL_OK;
1651 }
1652
1653 /*
1654 *--------------------------------------------------------------
1655 *
1656 * ButtonVarProc --
1657 *
1658 * This procedure is invoked when someone changes the
1659 * state variable associated with a radio button. Depending
1660 * on the new value of the button's variable, the button
1661 * may be selected or deselected.
1662 *
1663 * Results:
1664 * NULL is always returned.
1665 *
1666 * Side effects:
1667 * The button may become selected or deselected.
1668 *
1669 *--------------------------------------------------------------
1670 */
1671
1672 /* ARGSUSED */
1673 static char *
ButtonVarProc(clientData,interp,name1,name2,flags)1674 ButtonVarProc(clientData, interp, name1, name2, flags)
1675 ClientData clientData; /* Information about button. */
1676 Tcl_Interp *interp; /* Interpreter containing variable. */
1677 char *name1; /* Name of variable. */
1678 char *name2; /* Second part of variable name. */
1679 int flags; /* Information about what happened. */
1680 {
1681 register Button *butPtr = (Button *) clientData;
1682 char *value;
1683
1684 /*
1685 * If the variable is being unset, then just re-establish the
1686 * trace unless the whole interpreter is going away.
1687 */
1688
1689 if (flags & TCL_TRACE_UNSETS) {
1690 butPtr->flags &= ~SELECTED;
1691 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1692 Tcl_TraceVar(interp, butPtr->selVarName,
1693 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1694 ButtonVarProc, clientData);
1695 }
1696 goto redisplay;
1697 }
1698
1699 /*
1700 * Use the value of the variable to update the selected status of
1701 * the button.
1702 */
1703
1704 value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
1705 if (value == NULL) {
1706 value = "";
1707 }
1708 if (strcmp(value, butPtr->onValue) == 0) {
1709 if (butPtr->flags & SELECTED) {
1710 return (char *) NULL;
1711 }
1712 butPtr->flags |= SELECTED;
1713 } else if (butPtr->flags & SELECTED) {
1714 butPtr->flags &= ~SELECTED;
1715 } else {
1716 return (char *) NULL;
1717 }
1718
1719 redisplay:
1720 if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
1721 && !(butPtr->flags & REDRAW_PENDING)) {
1722 Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
1723 butPtr->flags |= REDRAW_PENDING;
1724 }
1725 return (char *) NULL;
1726 }
1727
1728 /*
1729 *--------------------------------------------------------------
1730 *
1731 * ButtonTextVarProc --
1732 *
1733 * This procedure is invoked when someone changes the variable
1734 * whose contents are to be displayed in a button.
1735 *
1736 * Results:
1737 * NULL is always returned.
1738 *
1739 * Side effects:
1740 * The text displayed in the button will change to match the
1741 * variable.
1742 *
1743 *--------------------------------------------------------------
1744 */
1745
1746 /* ARGSUSED */
1747 static char *
ButtonTextVarProc(clientData,interp,name1,name2,flags)1748 ButtonTextVarProc(clientData, interp, name1, name2, flags)
1749 ClientData clientData; /* Information about button. */
1750 Tcl_Interp *interp; /* Interpreter containing variable. */
1751 char *name1; /* Not used. */
1752 char *name2; /* Not used. */
1753 int flags; /* Information about what happened. */
1754 {
1755 register Button *butPtr = (Button *) clientData;
1756 char *value;
1757
1758 /*
1759 * If the variable is unset, then immediately recreate it unless
1760 * the whole interpreter is going away.
1761 */
1762
1763 if (flags & TCL_TRACE_UNSETS) {
1764 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1765 Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
1766 TCL_GLOBAL_ONLY);
1767 Tcl_TraceVar(interp, butPtr->textVarName,
1768 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1769 ButtonTextVarProc, clientData);
1770 }
1771 return (char *) NULL;
1772 }
1773
1774 value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
1775 if (value == NULL) {
1776 value = "";
1777 }
1778 if (butPtr->text != NULL) {
1779 ckfree(butPtr->text);
1780 }
1781 butPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
1782 strcpy(butPtr->text, value);
1783 ComputeButtonGeometry(butPtr);
1784
1785 if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
1786 && !(butPtr->flags & REDRAW_PENDING)) {
1787 Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
1788 butPtr->flags |= REDRAW_PENDING;
1789 }
1790 return (char *) NULL;
1791 }
1792
1793 /*
1794 *----------------------------------------------------------------------
1795 *
1796 * ButtonImageProc --
1797 *
1798 * This procedure is invoked by the image code whenever the manager
1799 * for an image does something that affects the size of contents
1800 * of an image displayed in a button.
1801 *
1802 * Results:
1803 * None.
1804 *
1805 * Side effects:
1806 * Arranges for the button to get redisplayed.
1807 *
1808 *----------------------------------------------------------------------
1809 */
1810
1811 static void
ButtonImageProc(clientData,x,y,width,height,imgWidth,imgHeight)1812 ButtonImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
1813 ClientData clientData; /* Pointer to widget record. */
1814 int x, y; /* Upper left pixel (within image)
1815 * that must be redisplayed. */
1816 int width, height; /* Dimensions of area to redisplay
1817 * (may be <= 0). */
1818 int imgWidth, imgHeight; /* New dimensions of image. */
1819 {
1820 register Button *butPtr = (Button *) clientData;
1821
1822 if (butPtr->tkwin != NULL) {
1823 ComputeButtonGeometry(butPtr);
1824 if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
1825 Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
1826 butPtr->flags |= REDRAW_PENDING;
1827 }
1828 }
1829 }
1830
1831 /*
1832 *----------------------------------------------------------------------
1833 *
1834 * ButtonSelectImageProc --
1835 *
1836 * This procedure is invoked by the image code whenever the manager
1837 * for an image does something that affects the size of contents
1838 * of the image displayed in a button when it is selected.
1839 *
1840 * Results:
1841 * None.
1842 *
1843 * Side effects:
1844 * May arrange for the button to get redisplayed.
1845 *
1846 *----------------------------------------------------------------------
1847 */
1848
1849 static void
ButtonSelectImageProc(clientData,x,y,width,height,imgWidth,imgHeight)1850 ButtonSelectImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
1851 ClientData clientData; /* Pointer to widget record. */
1852 int x, y; /* Upper left pixel (within image)
1853 * that must be redisplayed. */
1854 int width, height; /* Dimensions of area to redisplay
1855 * (may be <= 0). */
1856 int imgWidth, imgHeight; /* New dimensions of image. */
1857 {
1858 register Button *butPtr = (Button *) clientData;
1859
1860 /*
1861 * Don't recompute geometry: it's controlled by the primary image.
1862 */
1863
1864 if ((butPtr->flags & SELECTED) && (butPtr->tkwin != NULL)
1865 && Tk_IsMapped(butPtr->tkwin)
1866 && !(butPtr->flags & REDRAW_PENDING)) {
1867 Tcl_DoWhenIdle(DisplayButton, (ClientData) butPtr);
1868 butPtr->flags |= REDRAW_PENDING;
1869 }
1870 }
1871