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