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