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