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
9
* Copyright (c) 1990-1994 The Regents of the University of California.
10
* Copyright (c) 1994-1995 Sun Microsystems, Inc.
12
* See the file "license.terms" for information on usage and redistribution
13
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15
* RCS: @(#) $Id: tkButton.c,v 1.2 1998/09/14 18:23:04 stanton Exp $
22
* Class names for buttons, indexed by one of the type values above.
25
static char *classNames[] = {"Label", "Button", "Checkbutton", "Radiobutton"};
28
* The class procedure table for the button widget.
31
static int configFlags[] = {LABEL_MASK, BUTTON_MASK,
32
CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
35
* Information used for parsing configuration specs:
38
Tk_ConfigSpec tkpButtonConfigSpecs[] = {
39
{TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
40
DEF_BUTTON_ACTIVE_BG_COLOR, Tk_Offset(TkButton, activeBorder),
41
BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
42
|TK_CONFIG_COLOR_ONLY},
43
{TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
44
DEF_BUTTON_ACTIVE_BG_MONO, Tk_Offset(TkButton, activeBorder),
45
BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
46
|TK_CONFIG_MONO_ONLY},
47
{TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
48
DEF_BUTTON_ACTIVE_FG_COLOR, Tk_Offset(TkButton, activeFg),
49
BUTTON_MASK|TK_CONFIG_COLOR_ONLY},
50
{TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
51
DEF_CHKRAD_ACTIVE_FG_COLOR, Tk_Offset(TkButton, activeFg),
52
CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY},
53
{TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
54
DEF_BUTTON_ACTIVE_FG_MONO, Tk_Offset(TkButton, activeFg),
55
BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
56
|TK_CONFIG_MONO_ONLY},
57
{TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
58
DEF_BUTTON_ANCHOR, Tk_Offset(TkButton, anchor), ALL_MASK},
59
{TK_CONFIG_BORDER, "-background", "background", "Background",
60
DEF_BUTTON_BG_COLOR, Tk_Offset(TkButton, normalBorder),
61
ALL_MASK | TK_CONFIG_COLOR_ONLY},
62
{TK_CONFIG_BORDER, "-background", "background", "Background",
63
DEF_BUTTON_BG_MONO, Tk_Offset(TkButton, normalBorder),
64
ALL_MASK | TK_CONFIG_MONO_ONLY},
65
{TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
66
(char *) NULL, 0, ALL_MASK},
67
{TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
68
(char *) NULL, 0, ALL_MASK},
69
{TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
70
DEF_BUTTON_BITMAP, Tk_Offset(TkButton, bitmap),
71
ALL_MASK|TK_CONFIG_NULL_OK},
72
{TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
73
DEF_BUTTON_BORDER_WIDTH, Tk_Offset(TkButton, borderWidth), ALL_MASK},
74
{TK_CONFIG_STRING, "-command", "command", "Command",
75
DEF_BUTTON_COMMAND, Tk_Offset(TkButton, command),
76
BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
77
{TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
78
DEF_BUTTON_CURSOR, Tk_Offset(TkButton, cursor),
79
ALL_MASK|TK_CONFIG_NULL_OK},
80
{TK_CONFIG_UID, "-default", "default", "Default",
81
DEF_BUTTON_DEFAULT, Tk_Offset(TkButton, defaultState), BUTTON_MASK},
82
{TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
83
"DisabledForeground", DEF_BUTTON_DISABLED_FG_COLOR,
84
Tk_Offset(TkButton, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
85
|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
86
{TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
87
"DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO,
88
Tk_Offset(TkButton, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
89
|RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
90
{TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
91
(char *) NULL, 0, ALL_MASK},
92
{TK_CONFIG_FONT, "-font", "font", "Font",
93
DEF_BUTTON_FONT, Tk_Offset(TkButton, tkfont),
95
{TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
96
DEF_BUTTON_FG, Tk_Offset(TkButton, normalFg), LABEL_MASK|BUTTON_MASK},
97
{TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
98
DEF_CHKRAD_FG, Tk_Offset(TkButton, normalFg), CHECK_BUTTON_MASK
100
{TK_CONFIG_STRING, "-height", "height", "Height",
101
DEF_BUTTON_HEIGHT, Tk_Offset(TkButton, heightString), ALL_MASK},
102
{TK_CONFIG_BORDER, "-highlightbackground", "highlightBackground",
103
"HighlightBackground", DEF_BUTTON_HIGHLIGHT_BG,
104
Tk_Offset(TkButton, highlightBorder), ALL_MASK},
105
{TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
106
DEF_BUTTON_HIGHLIGHT, Tk_Offset(TkButton, highlightColorPtr),
108
{TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
109
"HighlightThickness",
110
DEF_LABEL_HIGHLIGHT_WIDTH, Tk_Offset(TkButton, highlightWidth),
112
{TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
113
"HighlightThickness",
114
DEF_BUTTON_HIGHLIGHT_WIDTH, Tk_Offset(TkButton, highlightWidth),
115
BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
116
{TK_CONFIG_STRING, "-image", "image", "Image",
117
DEF_BUTTON_IMAGE, Tk_Offset(TkButton, imageString),
118
ALL_MASK|TK_CONFIG_NULL_OK},
119
{TK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
120
DEF_BUTTON_INDICATOR, Tk_Offset(TkButton, indicatorOn),
121
CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
122
{TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
123
DEF_BUTTON_JUSTIFY, Tk_Offset(TkButton, justify), ALL_MASK},
124
{TK_CONFIG_STRING, "-offvalue", "offValue", "Value",
125
DEF_BUTTON_OFF_VALUE, Tk_Offset(TkButton, offValue),
127
{TK_CONFIG_STRING, "-onvalue", "onValue", "Value",
128
DEF_BUTTON_ON_VALUE, Tk_Offset(TkButton, onValue),
130
{TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
131
DEF_BUTTON_PADX, Tk_Offset(TkButton, padX), BUTTON_MASK},
132
{TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
133
DEF_LABCHKRAD_PADX, Tk_Offset(TkButton, padX),
134
LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
135
{TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
136
DEF_BUTTON_PADY, Tk_Offset(TkButton, padY), BUTTON_MASK},
137
{TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
138
DEF_LABCHKRAD_PADY, Tk_Offset(TkButton, padY),
139
LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
140
{TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
141
DEF_BUTTON_RELIEF, Tk_Offset(TkButton, relief), BUTTON_MASK},
142
{TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
143
DEF_LABCHKRAD_RELIEF, Tk_Offset(TkButton, relief),
144
LABEL_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
145
{TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
146
DEF_BUTTON_SELECT_COLOR, Tk_Offset(TkButton, selectBorder),
147
CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY
149
{TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
150
DEF_BUTTON_SELECT_MONO, Tk_Offset(TkButton, selectBorder),
151
CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY
153
{TK_CONFIG_STRING, "-selectimage", "selectImage", "SelectImage",
154
DEF_BUTTON_SELECT_IMAGE, Tk_Offset(TkButton, selectImageString),
155
CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
156
{TK_CONFIG_UID, "-state", "state", "State",
157
DEF_BUTTON_STATE, Tk_Offset(TkButton, state),
158
BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
159
{TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
160
DEF_LABEL_TAKE_FOCUS, Tk_Offset(TkButton, takeFocus),
161
LABEL_MASK|TK_CONFIG_NULL_OK},
162
{TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
163
DEF_BUTTON_TAKE_FOCUS, Tk_Offset(TkButton, takeFocus),
164
BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_NULL_OK},
165
{TK_CONFIG_STRING, "-text", "text", "Text",
166
DEF_BUTTON_TEXT, Tk_Offset(TkButton, text), ALL_MASK},
167
{TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
168
DEF_BUTTON_TEXT_VARIABLE, Tk_Offset(TkButton, textVarName),
169
ALL_MASK|TK_CONFIG_NULL_OK},
170
{TK_CONFIG_INT, "-underline", "underline", "Underline",
171
DEF_BUTTON_UNDERLINE, Tk_Offset(TkButton, underline), ALL_MASK},
172
{TK_CONFIG_STRING, "-value", "value", "Value",
173
DEF_BUTTON_VALUE, Tk_Offset(TkButton, onValue),
175
{TK_CONFIG_STRING, "-variable", "variable", "Variable",
176
DEF_RADIOBUTTON_VARIABLE, Tk_Offset(TkButton, selVarName),
178
{TK_CONFIG_STRING, "-variable", "variable", "Variable",
179
DEF_CHECKBUTTON_VARIABLE, Tk_Offset(TkButton, selVarName),
180
CHECK_BUTTON_MASK|TK_CONFIG_NULL_OK},
181
{TK_CONFIG_STRING, "-width", "width", "Width",
182
DEF_BUTTON_WIDTH, Tk_Offset(TkButton, widthString), ALL_MASK},
183
{TK_CONFIG_PIXELS, "-wraplength", "wrapLength", "WrapLength",
184
DEF_BUTTON_WRAP_LENGTH, Tk_Offset(TkButton, wrapLength), ALL_MASK},
185
{TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
190
* String to print out in error messages, identifying options for
191
* widget commands for different types of labels or buttons:
194
static char *optionStrings[] = {
196
"cget, configure, flash, or invoke",
197
"cget, configure, deselect, flash, invoke, select, or toggle",
198
"cget, configure, deselect, flash, invoke, or select"
202
* Forward declarations for procedures defined later in this file:
205
static void ButtonCmdDeletedProc _ANSI_ARGS_((
206
ClientData clientData));
207
static int ButtonCreate _ANSI_ARGS_((ClientData clientData,
208
Tcl_Interp *interp, int argc, char **argv,
210
static void ButtonEventProc _ANSI_ARGS_((ClientData clientData,
212
static void ButtonImageProc _ANSI_ARGS_((ClientData clientData,
213
int x, int y, int width, int height,
214
int imgWidth, int imgHeight));
215
static void ButtonSelectImageProc _ANSI_ARGS_((
216
ClientData clientData, int x, int y, int width,
217
int height, int imgWidth, int imgHeight));
218
static char * ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
219
Tcl_Interp *interp, char *name1, char *name2,
221
static char * ButtonVarProc _ANSI_ARGS_((ClientData clientData,
222
Tcl_Interp *interp, char *name1, char *name2,
224
static int ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
225
Tcl_Interp *interp, int argc, char **argv));
226
static int ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
227
TkButton *butPtr, int argc, char **argv,
229
static void DestroyButton _ANSI_ARGS_((TkButton *butPtr));
233
*--------------------------------------------------------------
235
* Tk_ButtonCmd, Tk_CheckbuttonCmd, Tk_LabelCmd, Tk_RadiobuttonCmd --
237
* These procedures are invoked to process the "button", "label",
238
* "radiobutton", and "checkbutton" Tcl commands. See the
239
* user documentation for details on what they do.
242
* A standard Tcl result.
245
* See the user documentation. These procedures are just wrappers;
246
* they call ButtonCreate to do all of the real work.
248
*--------------------------------------------------------------
252
Tk_ButtonCmd(clientData, interp, argc, argv)
253
ClientData clientData; /* Main window associated with
255
Tcl_Interp *interp; /* Current interpreter. */
256
int argc; /* Number of arguments. */
257
char **argv; /* Argument strings. */
259
return ButtonCreate(clientData, interp, argc, argv, TYPE_BUTTON);
263
Tk_CheckbuttonCmd(clientData, interp, argc, argv)
264
ClientData clientData; /* Main window associated with
266
Tcl_Interp *interp; /* Current interpreter. */
267
int argc; /* Number of arguments. */
268
char **argv; /* Argument strings. */
270
return ButtonCreate(clientData, interp, argc, argv, TYPE_CHECK_BUTTON);
274
Tk_LabelCmd(clientData, interp, argc, argv)
275
ClientData clientData; /* Main window associated with
277
Tcl_Interp *interp; /* Current interpreter. */
278
int argc; /* Number of arguments. */
279
char **argv; /* Argument strings. */
281
return ButtonCreate(clientData, interp, argc, argv, TYPE_LABEL);
285
Tk_RadiobuttonCmd(clientData, interp, argc, argv)
286
ClientData clientData; /* Main window associated with
288
Tcl_Interp *interp; /* Current interpreter. */
289
int argc; /* Number of arguments. */
290
char **argv; /* Argument strings. */
292
return ButtonCreate(clientData, interp, argc, argv, TYPE_RADIO_BUTTON);
296
*--------------------------------------------------------------
300
* This procedure does all the real work of implementing the
301
* "button", "label", "radiobutton", and "checkbutton" Tcl
302
* commands. See the user documentation for details on what it does.
305
* A standard Tcl result.
308
* See the user documentation.
310
*--------------------------------------------------------------
314
ButtonCreate(clientData, interp, argc, argv, type)
315
ClientData clientData; /* Main window associated with
317
Tcl_Interp *interp; /* Current interpreter. */
318
int argc; /* Number of arguments. */
319
char **argv; /* Argument strings. */
320
int type; /* Type of button to create: TYPE_LABEL,
321
* TYPE_BUTTON, TYPE_CHECK_BUTTON, or
322
* TYPE_RADIO_BUTTON. */
324
register TkButton *butPtr;
325
Tk_Window tkwin = (Tk_Window) clientData;
329
Tcl_AppendResult(interp, "wrong # args: should be \"",
330
argv[0], " pathName ?options?\"", (char *) NULL);
335
* Create the new window.
338
new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
343
Tk_SetClass(new, classNames[type]);
344
butPtr = TkpCreateButton(new);
346
TkSetClassProcs(new, &tkpButtonProcs, (ClientData) butPtr);
349
* Initialize the data structure for the button.
353
butPtr->display = Tk_Display(new);
354
butPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin),
355
ButtonWidgetCmd, (ClientData) butPtr, ButtonCmdDeletedProc);
356
butPtr->interp = interp;
359
butPtr->underline = -1;
360
butPtr->textVarName = NULL;
361
butPtr->bitmap = None;
362
butPtr->imageString = NULL;
363
butPtr->image = NULL;
364
butPtr->selectImageString = NULL;
365
butPtr->selectImage = NULL;
366
butPtr->state = tkNormalUid;
367
butPtr->normalBorder = NULL;
368
butPtr->activeBorder = NULL;
369
butPtr->borderWidth = 0;
370
butPtr->relief = TK_RELIEF_FLAT;
371
butPtr->highlightWidth = 0;
372
butPtr->highlightBorder = NULL;
373
butPtr->highlightColorPtr = NULL;
375
butPtr->tkfont = NULL;
376
butPtr->normalFg = NULL;
377
butPtr->activeFg = NULL;
378
butPtr->disabledFg = NULL;
379
butPtr->normalTextGC = None;
380
butPtr->activeTextGC = None;
382
butPtr->disabledGC = None;
383
butPtr->copyGC = None;
384
butPtr->widthString = NULL;
385
butPtr->heightString = NULL;
388
butPtr->wrapLength = 0;
391
butPtr->anchor = TK_ANCHOR_CENTER;
392
butPtr->justify = TK_JUSTIFY_CENTER;
393
butPtr->textLayout = NULL;
394
butPtr->indicatorOn = 0;
395
butPtr->selectBorder = NULL;
396
butPtr->indicatorSpace = 0;
397
butPtr->indicatorDiameter = 0;
398
butPtr->defaultState = tkDisabledUid;
399
butPtr->selVarName = NULL;
400
butPtr->onValue = NULL;
401
butPtr->offValue = NULL;
402
butPtr->cursor = None;
403
butPtr->command = NULL;
404
butPtr->takeFocus = NULL;
407
Tk_CreateEventHandler(butPtr->tkwin,
408
ExposureMask|StructureNotifyMask|FocusChangeMask,
409
ButtonEventProc, (ClientData) butPtr);
411
if (ConfigureButton(interp, butPtr, argc - 2, argv + 2,
412
configFlags[type]) != TCL_OK) {
413
Tk_DestroyWindow(butPtr->tkwin);
417
interp->result = Tk_PathName(butPtr->tkwin);
422
*--------------------------------------------------------------
426
* This procedure is invoked to process the Tcl command
427
* that corresponds to a widget managed by this module.
428
* See the user documentation for details on what it does.
431
* A standard Tcl result.
434
* See the user documentation.
436
*--------------------------------------------------------------
440
ButtonWidgetCmd(clientData, interp, argc, argv)
441
ClientData clientData; /* Information about button widget. */
442
Tcl_Interp *interp; /* Current interpreter. */
443
int argc; /* Number of arguments. */
444
char **argv; /* Argument strings. */
446
register TkButton *butPtr = (TkButton *) clientData;
452
sprintf(interp->result,
453
"wrong # args: should be \"%.50s option ?arg arg ...?\"",
457
Tcl_Preserve((ClientData) butPtr);
459
length = strlen(argv[1]);
461
if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
464
Tcl_AppendResult(interp, "wrong # args: should be \"",
465
argv[0], " cget option\"",
469
result = Tk_ConfigureValue(interp, butPtr->tkwin, tkpButtonConfigSpecs,
470
(char *) butPtr, argv[2], configFlags[butPtr->type]);
471
} else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
474
result = Tk_ConfigureInfo(interp, butPtr->tkwin,
475
tkpButtonConfigSpecs, (char *) butPtr, (char *) NULL,
476
configFlags[butPtr->type]);
477
} else if (argc == 3) {
478
result = Tk_ConfigureInfo(interp, butPtr->tkwin,
479
tkpButtonConfigSpecs, (char *) butPtr, argv[2],
480
configFlags[butPtr->type]);
482
result = ConfigureButton(interp, butPtr, argc-2, argv+2,
483
configFlags[butPtr->type] | TK_CONFIG_ARGV_ONLY);
485
} else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
486
&& (butPtr->type >= TYPE_CHECK_BUTTON)) {
488
sprintf(interp->result,
489
"wrong # args: should be \"%.50s deselect\"",
493
if (butPtr->type == TYPE_CHECK_BUTTON) {
494
if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
495
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
498
} else if (butPtr->flags & SELECTED) {
499
if (Tcl_SetVar(interp, butPtr->selVarName, "",
500
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
504
} else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0)
505
&& (butPtr->type != TYPE_LABEL)) {
509
sprintf(interp->result,
510
"wrong # args: should be \"%.50s flash\"",
514
if (butPtr->state != tkDisabledUid) {
515
for (i = 0; i < 4; i++) {
516
butPtr->state = (butPtr->state == tkNormalUid)
517
? tkActiveUid : tkNormalUid;
518
Tk_SetBackgroundFromBorder(butPtr->tkwin,
519
(butPtr->state == tkActiveUid) ? butPtr->activeBorder
520
: butPtr->normalBorder);
521
TkpDisplayButton((ClientData) butPtr);
524
* Special note: must cancel any existing idle handler
525
* for TkpDisplayButton; it's no longer needed, and TkpDisplayButton
526
* cleared the REDRAW_PENDING flag.
529
Tcl_CancelIdleCall(TkpDisplayButton, (ClientData) butPtr);
530
XFlush(butPtr->display);
534
} else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
535
&& (butPtr->type > TYPE_LABEL)) {
537
sprintf(interp->result,
538
"wrong # args: should be \"%.50s invoke\"",
542
if (butPtr->state != tkDisabledUid) {
543
result = TkInvokeButton(butPtr);
545
} else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
546
&& (butPtr->type >= TYPE_CHECK_BUTTON)) {
548
sprintf(interp->result,
549
"wrong # args: should be \"%.50s select\"",
553
if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
554
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
557
} else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
558
&& (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
560
sprintf(interp->result,
561
"wrong # args: should be \"%.50s toggle\"",
565
if (butPtr->flags & SELECTED) {
566
if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
567
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
571
if (Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue,
572
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
577
sprintf(interp->result,
578
"bad option \"%.50s\": must be %s", argv[1],
579
optionStrings[butPtr->type]);
582
Tcl_Release((ClientData) butPtr);
586
Tcl_Release((ClientData) butPtr);
591
*----------------------------------------------------------------------
595
* This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
596
* to clean up the internal structure of a button at a safe time
597
* (when no-one is using it anymore).
603
* Everything associated with the widget is freed up.
605
*----------------------------------------------------------------------
609
DestroyButton(butPtr)
610
TkButton *butPtr; /* Info about button widget. */
613
* Free up all the stuff that requires special handling, then
614
* let Tk_FreeOptions handle all the standard option-related
618
if (butPtr->textVarName != NULL) {
619
Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
620
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
621
ButtonTextVarProc, (ClientData) butPtr);
623
if (butPtr->image != NULL) {
624
Tk_FreeImage(butPtr->image);
626
if (butPtr->selectImage != NULL) {
627
Tk_FreeImage(butPtr->selectImage);
629
if (butPtr->normalTextGC != None) {
630
Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
632
if (butPtr->activeTextGC != None) {
633
Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
635
if (butPtr->gray != None) {
636
Tk_FreeBitmap(butPtr->display, butPtr->gray);
638
if (butPtr->disabledGC != None) {
639
Tk_FreeGC(butPtr->display, butPtr->disabledGC);
641
if (butPtr->copyGC != None) {
642
Tk_FreeGC(butPtr->display, butPtr->copyGC);
644
if (butPtr->selVarName != NULL) {
645
Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
646
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
647
ButtonVarProc, (ClientData) butPtr);
649
Tk_FreeTextLayout(butPtr->textLayout);
650
Tk_FreeOptions(tkpButtonConfigSpecs, (char *) butPtr, butPtr->display,
651
configFlags[butPtr->type]);
652
Tcl_EventuallyFree((ClientData)butPtr, TCL_DYNAMIC);
656
*----------------------------------------------------------------------
660
* This procedure is called to process an argv/argc list, plus
661
* the Tk option database, in order to configure (or
662
* reconfigure) a button widget.
665
* The return value is a standard Tcl result. If TCL_ERROR is
666
* returned, then interp->result contains an error message.
669
* Configuration information, such as text string, colors, font,
670
* etc. get set for butPtr; old resources get freed, if there
671
* were any. The button is redisplayed.
673
*----------------------------------------------------------------------
677
ConfigureButton(interp, butPtr, argc, argv, flags)
678
Tcl_Interp *interp; /* Used for error reporting. */
679
register TkButton *butPtr; /* Information about widget; may or may
680
* not already have values for some fields. */
681
int argc; /* Number of valid entries in argv. */
682
char **argv; /* Arguments. */
683
int flags; /* Flags to pass to Tk_ConfigureWidget. */
688
* Eliminate any existing trace on variables monitored by the button.
691
if (butPtr->textVarName != NULL) {
692
Tcl_UntraceVar(interp, butPtr->textVarName,
693
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
694
ButtonTextVarProc, (ClientData) butPtr);
696
if (butPtr->selVarName != NULL) {
697
Tcl_UntraceVar(interp, butPtr->selVarName,
698
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
699
ButtonVarProc, (ClientData) butPtr);
704
if (Tk_ConfigureWidget(interp, butPtr->tkwin, tkpButtonConfigSpecs,
705
argc, argv, (char *) butPtr, flags) != TCL_OK) {
710
* A few options need special processing, such as setting the
711
* background from a 3-D border, or filling in complicated
712
* defaults that couldn't be specified to Tk_ConfigureWidget.
715
if ((butPtr->state == tkActiveUid) && !Tk_StrictMotif(butPtr->tkwin)) {
716
Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
718
Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
719
if ((butPtr->state != tkNormalUid) && (butPtr->state != tkActiveUid)
720
&& (butPtr->state != tkDisabledUid)) {
721
Tcl_AppendResult(interp, "bad state value \"", butPtr->state,
722
"\": must be normal, active, or disabled", (char *) NULL);
723
butPtr->state = tkNormalUid;
728
if ((butPtr->defaultState != tkActiveUid)
729
&& (butPtr->defaultState != tkDisabledUid)
730
&& (butPtr->defaultState != tkNormalUid)) {
731
Tcl_AppendResult(interp, "bad -default value \"", butPtr->defaultState,
732
"\": must be normal, active, or disabled", (char *) NULL);
733
butPtr->defaultState = tkDisabledUid;
737
if (butPtr->highlightWidth < 0) {
738
butPtr->highlightWidth = 0;
741
if (butPtr->padX < 0) {
744
if (butPtr->padY < 0) {
748
if (butPtr->type >= TYPE_CHECK_BUTTON) {
751
if (butPtr->selVarName == NULL) {
752
butPtr->selVarName = (char *) ckalloc((unsigned)
753
(strlen(Tk_Name(butPtr->tkwin)) + 1));
754
strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin));
758
* Select the button if the associated variable has the
759
* appropriate value, initialize the variable if it doesn't
760
* exist, then set a trace on the variable to monitor future
761
* changes to its value.
764
value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
765
butPtr->flags &= ~SELECTED;
767
if (strcmp(value, butPtr->onValue) == 0) {
768
butPtr->flags |= SELECTED;
771
if (Tcl_SetVar(interp, butPtr->selVarName,
772
(butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
773
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
777
Tcl_TraceVar(interp, butPtr->selVarName,
778
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
779
ButtonVarProc, (ClientData) butPtr);
783
* Get the images for the widget, if there are any. Allocate the
784
* new images before freeing the old ones, so that the reference
785
* counts don't go to zero and cause image data to be discarded.
788
if (butPtr->imageString != NULL) {
789
image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
790
butPtr->imageString, ButtonImageProc, (ClientData) butPtr);
797
if (butPtr->image != NULL) {
798
Tk_FreeImage(butPtr->image);
800
butPtr->image = image;
801
if (butPtr->selectImageString != NULL) {
802
image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
803
butPtr->selectImageString, ButtonSelectImageProc,
804
(ClientData) butPtr);
811
if (butPtr->selectImage != NULL) {
812
Tk_FreeImage(butPtr->selectImage);
814
butPtr->selectImage = image;
816
if ((butPtr->image == NULL) && (butPtr->bitmap == None)
817
&& (butPtr->textVarName != NULL)) {
819
* The button must display the value of a variable: set up a trace
820
* on the variable's value, create the variable if it doesn't
821
* exist, and fetch its current value.
826
value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
828
if (Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
829
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
833
if (butPtr->text != NULL) {
834
ckfree(butPtr->text);
836
butPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
837
strcpy(butPtr->text, value);
839
Tcl_TraceVar(interp, butPtr->textVarName,
840
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
841
ButtonTextVarProc, (ClientData) butPtr);
844
if ((butPtr->bitmap != None) || (butPtr->image != NULL)) {
845
if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->widthString,
846
&butPtr->width) != TCL_OK) {
848
Tcl_AddErrorInfo(interp, "\n (processing -width option)");
851
if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->heightString,
852
&butPtr->height) != TCL_OK) {
854
Tcl_AddErrorInfo(interp, "\n (processing -height option)");
858
if (Tcl_GetInt(interp, butPtr->widthString, &butPtr->width)
862
if (Tcl_GetInt(interp, butPtr->heightString, &butPtr->height)
868
TkButtonWorldChanged((ClientData) butPtr);
873
*---------------------------------------------------------------------------
875
* TkButtonWorldChanged --
877
* This procedure is called when the world has changed in some
878
* way and the widget needs to recompute all its graphics contexts
879
* and determine its new geometry.
885
* Button will be relayed out and redisplayed.
887
*---------------------------------------------------------------------------
891
TkButtonWorldChanged(instanceData)
892
ClientData instanceData; /* Information about widget. */
899
butPtr = (TkButton *) instanceData;
905
gcValues.font = Tk_FontId(butPtr->tkfont);
906
gcValues.foreground = butPtr->normalFg->pixel;
907
gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
910
* Note: GraphicsExpose events are disabled in normalTextGC because it's
911
* used to copy stuff from an off-screen pixmap onto the screen (we know
912
* that there's no problem with obscured areas).
915
gcValues.graphics_exposures = False;
916
mask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
917
newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
918
if (butPtr->normalTextGC != None) {
919
Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
921
butPtr->normalTextGC = newGC;
923
if (butPtr->activeFg != NULL) {
924
gcValues.font = Tk_FontId(butPtr->tkfont);
925
gcValues.foreground = butPtr->activeFg->pixel;
926
gcValues.background = Tk_3DBorderColor(butPtr->activeBorder)->pixel;
927
mask = GCForeground | GCBackground | GCFont;
928
newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
929
if (butPtr->activeTextGC != None) {
930
Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
932
butPtr->activeTextGC = newGC;
935
if (butPtr->type != TYPE_LABEL) {
936
gcValues.font = Tk_FontId(butPtr->tkfont);
937
gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
938
if ((butPtr->disabledFg != NULL) && (butPtr->imageString == NULL)) {
939
gcValues.foreground = butPtr->disabledFg->pixel;
940
mask = GCForeground | GCBackground | GCFont;
942
gcValues.foreground = gcValues.background;
944
if (butPtr->gray == None) {
945
butPtr->gray = Tk_GetBitmap(NULL, butPtr->tkwin,
946
Tk_GetUid("gray50"));
948
if (butPtr->gray != None) {
949
gcValues.fill_style = FillStippled;
950
gcValues.stipple = butPtr->gray;
951
mask |= GCFillStyle | GCStipple;
954
newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
955
if (butPtr->disabledGC != None) {
956
Tk_FreeGC(butPtr->display, butPtr->disabledGC);
958
butPtr->disabledGC = newGC;
961
if (butPtr->copyGC == None) {
962
butPtr->copyGC = Tk_GetGC(butPtr->tkwin, 0, &gcValues);
965
TkpComputeButtonGeometry(butPtr);
968
* Lastly, arrange for the button to be redisplayed.
971
if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
972
Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
973
butPtr->flags |= REDRAW_PENDING;
978
*--------------------------------------------------------------
982
* This procedure is invoked by the Tk dispatcher for various
989
* When the window gets deleted, internal structures get
990
* cleaned up. When it gets exposed, it is redisplayed.
992
*--------------------------------------------------------------
996
ButtonEventProc(clientData, eventPtr)
997
ClientData clientData; /* Information about window. */
998
XEvent *eventPtr; /* Information about event. */
1000
TkButton *butPtr = (TkButton *) clientData;
1001
if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
1003
} else if (eventPtr->type == ConfigureNotify) {
1005
* Must redraw after size changes, since layout could have changed
1006
* and borders will need to be redrawn.
1010
} else if (eventPtr->type == DestroyNotify) {
1011
TkpDestroyButton(butPtr);
1012
if (butPtr->tkwin != NULL) {
1013
butPtr->tkwin = NULL;
1014
Tcl_DeleteCommandFromToken(butPtr->interp, butPtr->widgetCmd);
1016
if (butPtr->flags & REDRAW_PENDING) {
1017
Tcl_CancelIdleCall(TkpDisplayButton, (ClientData) butPtr);
1019
DestroyButton(butPtr);
1020
} else if (eventPtr->type == FocusIn) {
1021
if (eventPtr->xfocus.detail != NotifyInferior) {
1022
butPtr->flags |= GOT_FOCUS;
1023
if (butPtr->highlightWidth > 0) {
1027
} else if (eventPtr->type == FocusOut) {
1028
if (eventPtr->xfocus.detail != NotifyInferior) {
1029
butPtr->flags &= ~GOT_FOCUS;
1030
if (butPtr->highlightWidth > 0) {
1038
if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
1039
Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
1040
butPtr->flags |= REDRAW_PENDING;
1045
*----------------------------------------------------------------------
1047
* ButtonCmdDeletedProc --
1049
* This procedure is invoked when a widget command is deleted. If
1050
* the widget isn't already in the process of being destroyed,
1051
* this command destroys it.
1057
* The widget is destroyed.
1059
*----------------------------------------------------------------------
1063
ButtonCmdDeletedProc(clientData)
1064
ClientData clientData; /* Pointer to widget record for widget. */
1066
TkButton *butPtr = (TkButton *) clientData;
1067
Tk_Window tkwin = butPtr->tkwin;
1070
* This procedure could be invoked either because the window was
1071
* destroyed and the command was then deleted (in which case tkwin
1072
* is NULL) or because the command was deleted, and then this procedure
1073
* destroys the widget.
1076
if (tkwin != NULL) {
1077
butPtr->tkwin = NULL;
1078
Tk_DestroyWindow(tkwin);
1083
*----------------------------------------------------------------------
1087
* This procedure is called to carry out the actions associated
1088
* with a button, such as invoking a Tcl command or setting a
1089
* variable. This procedure is invoked, for example, when the
1090
* button is invoked via the mouse.
1093
* A standard Tcl return value. Information is also left in
1097
* Depends on the button and its associated command.
1099
*----------------------------------------------------------------------
1103
TkInvokeButton(butPtr)
1104
register TkButton *butPtr; /* Information about button. */
1106
if (butPtr->type == TYPE_CHECK_BUTTON) {
1107
if (butPtr->flags & SELECTED) {
1108
if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue,
1109
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
1113
if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
1114
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
1118
} else if (butPtr->type == TYPE_RADIO_BUTTON) {
1119
if (Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
1120
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) {
1124
if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
1125
return TkCopyAndGlobalEval(butPtr->interp, butPtr->command);
1131
*--------------------------------------------------------------
1135
* This procedure is invoked when someone changes the
1136
* state variable associated with a radio button. Depending
1137
* on the new value of the button's variable, the button
1138
* may be selected or deselected.
1141
* NULL is always returned.
1144
* The button may become selected or deselected.
1146
*--------------------------------------------------------------
1151
ButtonVarProc(clientData, interp, name1, name2, flags)
1152
ClientData clientData; /* Information about button. */
1153
Tcl_Interp *interp; /* Interpreter containing variable. */
1154
char *name1; /* Name of variable. */
1155
char *name2; /* Second part of variable name. */
1156
int flags; /* Information about what happened. */
1158
register TkButton *butPtr = (TkButton *) clientData;
1162
* If the variable is being unset, then just re-establish the
1163
* trace unless the whole interpreter is going away.
1166
if (flags & TCL_TRACE_UNSETS) {
1167
butPtr->flags &= ~SELECTED;
1168
if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1169
Tcl_TraceVar(interp, butPtr->selVarName,
1170
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1171
ButtonVarProc, clientData);
1177
* Use the value of the variable to update the selected status of
1181
value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
1182
if (value == NULL) {
1185
if (strcmp(value, butPtr->onValue) == 0) {
1186
if (butPtr->flags & SELECTED) {
1187
return (char *) NULL;
1189
butPtr->flags |= SELECTED;
1190
} else if (butPtr->flags & SELECTED) {
1191
butPtr->flags &= ~SELECTED;
1193
return (char *) NULL;
1197
if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
1198
&& !(butPtr->flags & REDRAW_PENDING)) {
1199
Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
1200
butPtr->flags |= REDRAW_PENDING;
1202
return (char *) NULL;
1206
*--------------------------------------------------------------
1208
* ButtonTextVarProc --
1210
* This procedure is invoked when someone changes the variable
1211
* whose contents are to be displayed in a button.
1214
* NULL is always returned.
1217
* The text displayed in the button will change to match the
1220
*--------------------------------------------------------------
1225
ButtonTextVarProc(clientData, interp, name1, name2, flags)
1226
ClientData clientData; /* Information about button. */
1227
Tcl_Interp *interp; /* Interpreter containing variable. */
1228
char *name1; /* Not used. */
1229
char *name2; /* Not used. */
1230
int flags; /* Information about what happened. */
1232
register TkButton *butPtr = (TkButton *) clientData;
1236
* If the variable is unset, then immediately recreate it unless
1237
* the whole interpreter is going away.
1240
if (flags & TCL_TRACE_UNSETS) {
1241
if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1242
Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
1244
Tcl_TraceVar(interp, butPtr->textVarName,
1245
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1246
ButtonTextVarProc, clientData);
1248
return (char *) NULL;
1251
value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
1252
if (value == NULL) {
1255
if (butPtr->text != NULL) {
1256
ckfree(butPtr->text);
1258
butPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
1259
strcpy(butPtr->text, value);
1260
TkpComputeButtonGeometry(butPtr);
1262
if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
1263
&& !(butPtr->flags & REDRAW_PENDING)) {
1264
Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
1265
butPtr->flags |= REDRAW_PENDING;
1267
return (char *) NULL;
1271
*----------------------------------------------------------------------
1273
* ButtonImageProc --
1275
* This procedure is invoked by the image code whenever the manager
1276
* for an image does something that affects the size of contents
1277
* of an image displayed in a button.
1283
* Arranges for the button to get redisplayed.
1285
*----------------------------------------------------------------------
1289
ButtonImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
1290
ClientData clientData; /* Pointer to widget record. */
1291
int x, y; /* Upper left pixel (within image)
1292
* that must be redisplayed. */
1293
int width, height; /* Dimensions of area to redisplay
1295
int imgWidth, imgHeight; /* New dimensions of image. */
1297
register TkButton *butPtr = (TkButton *) clientData;
1299
if (butPtr->tkwin != NULL) {
1300
TkpComputeButtonGeometry(butPtr);
1301
if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
1302
Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
1303
butPtr->flags |= REDRAW_PENDING;
1309
*----------------------------------------------------------------------
1311
* ButtonSelectImageProc --
1313
* This procedure is invoked by the image code whenever the manager
1314
* for an image does something that affects the size of contents
1315
* of the image displayed in a button when it is selected.
1321
* May arrange for the button to get redisplayed.
1323
*----------------------------------------------------------------------
1327
ButtonSelectImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
1328
ClientData clientData; /* Pointer to widget record. */
1329
int x, y; /* Upper left pixel (within image)
1330
* that must be redisplayed. */
1331
int width, height; /* Dimensions of area to redisplay
1333
int imgWidth, imgHeight; /* New dimensions of image. */
1335
register TkButton *butPtr = (TkButton *) clientData;
1338
* Don't recompute geometry: it's controlled by the primary image.
1341
if ((butPtr->flags & SELECTED) && (butPtr->tkwin != NULL)
1342
&& Tk_IsMapped(butPtr->tkwin)
1343
&& !(butPtr->flags & REDRAW_PENDING)) {
1344
Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) butPtr);
1345
butPtr->flags |= REDRAW_PENDING;