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