]>
Commit | Line | Data |
---|---|---|
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, check buttons, and radio | |
7 | * buttons. | |
8 | * | |
9 | * Copyright 1990-1992 Regents of the University of California. | |
10 | * Permission to use, copy, modify, and distribute this | |
11 | * software and its documentation for any purpose and without | |
12 | * fee is hereby granted, provided that the above copyright | |
13 | * notice appear in all copies. The University of California | |
14 | * makes no representations about the suitability of this | |
15 | * software for any purpose. It is provided "as is" without | |
16 | * express or implied warranty. | |
17 | */ | |
18 | ||
19 | #ifndef lint | |
20 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkButton.c,v 1.69 92/08/21 11:42:47 ouster Exp $ SPRITE (Berkeley)"; | |
21 | #endif | |
22 | ||
23 | #include "default.h" | |
24 | #include "tkconfig.h" | |
25 | #include "tkint.h" | |
26 | ||
27 | #include <assert.h> | |
28 | ||
29 | /* | |
30 | * A data structure of the following type is kept for each | |
31 | * widget managed by this file: | |
32 | */ | |
33 | ||
34 | typedef struct { | |
35 | Tk_Window tkwin; /* Window that embodies the button. NULL | |
36 | * means that the window has been destroyed. */ | |
37 | Tcl_Interp *interp; /* Interpreter associated with button. */ | |
38 | int type; /* Type of widget: restricts operations | |
39 | * that may be performed on widget. See | |
40 | * below for possible values. */ | |
41 | ||
42 | /* | |
43 | * Information about what's in the button. | |
44 | */ | |
45 | ||
46 | char *text; /* Text to display in button (malloc'ed) | |
47 | * or NULL. */ | |
48 | int textLength; /* # of characters in text. */ | |
49 | char *textVarName; /* Name of variable (malloc'ed) or NULL. | |
50 | * If non-NULL, button displays the contents | |
51 | * of this variable. */ | |
52 | Pixmap bitmap; /* Bitmap to display or None. If not None | |
53 | * then text and textVar are ignored. */ | |
54 | ||
55 | /* | |
56 | * Information used when displaying widget: | |
57 | */ | |
58 | ||
59 | Tk_Uid state; /* State of button for display purposes: | |
60 | * normal, active, or disabled. */ | |
61 | Tk_3DBorder normalBorder; /* Structure used to draw 3-D | |
62 | * border and background when window | |
63 | * isn't active. NULL means no such | |
64 | * border exists. */ | |
65 | Tk_3DBorder activeBorder; /* Structure used to draw 3-D | |
66 | * border and background when window | |
67 | * is active. NULL means no such | |
68 | * border exists. */ | |
69 | int borderWidth; /* Width of border. */ | |
70 | int relief; /* 3-d effect: TK_RELIEF_RAISED, etc. */ | |
71 | XFontStruct *fontPtr; /* Information about text font, or NULL. */ | |
72 | XColor *normalFg; /* Foreground color in normal mode. */ | |
73 | XColor *activeFg; /* Foreground color in active mode. NULL | |
74 | * means use normalFg instead. */ | |
75 | XColor *disabledFg; /* Foreground color when disabled. NULL | |
76 | * means use normalFg with a 50% stipple | |
77 | * instead. */ | |
78 | GC normalTextGC; /* GC for drawing text in normal mode. Also | |
79 | * used to copy from off-screen pixmap onto | |
80 | * screen. */ | |
81 | GC activeTextGC; /* GC for drawing text in active mode (NULL | |
82 | * means use normalTextGC). */ | |
83 | Pixmap gray; /* Pixmap for displaying disabled text if | |
84 | * disabledFg is NULL. */ | |
85 | GC disabledGC; /* Used to produce disabled effect. If | |
86 | * disabledFg isn't NULL, this GC is used to | |
87 | * draw button text or icon. Otherwise | |
88 | * text or icon is drawn with normalGC and | |
89 | * this GC is used to stipple background | |
90 | * across it. */ | |
91 | int leftBearing; /* Amount text sticks left from its origin, | |
92 | * in pixels. */ | |
93 | int rightBearing; /* Amount text sticks right from its origin. */ | |
94 | int width, height; /* If > 0, these specify dimensions to request | |
95 | * for window, in characters for text and in | |
96 | * pixels for bitmaps. In this case the actual | |
97 | * size of the text string or bitmap is | |
98 | * ignored in computing desired window size. */ | |
99 | int padX, padY; /* Extra space around text or bitmap (pixels | |
100 | * on each side). */ | |
101 | Tk_Anchor anchor; /* Where text/bitmap should be displayed | |
102 | * inside button region. */ | |
103 | XColor *selectorFg; /* Color for selector. */ | |
104 | GC selectorGC; /* For drawing highlight when this button | |
105 | * is in selected state. */ | |
106 | int selectorSpace; /* Horizontal space (in pixels) allocated for | |
107 | * display of selector. */ | |
108 | int selectorDiameter; /* Diameter of selector, in pixels. */ | |
109 | ||
110 | /* | |
111 | * For check and radio buttons, the fields below are used | |
112 | * to manage the variable indicating the button's state. | |
113 | */ | |
114 | ||
115 | char *selVarName; /* Name of variable used to control selected | |
116 | * state of button. Malloc'ed (if | |
117 | * not NULL). */ | |
118 | char *onValue; /* Value to store in variable when | |
119 | * this button is selected. Malloc'ed (if | |
120 | * not NULL). */ | |
121 | char *offValue; /* Value to store in variable when this | |
122 | * button isn't selected. Malloc'ed | |
123 | * (if not NULL). Valid only for check | |
124 | * buttons. */ | |
125 | ||
126 | /* | |
127 | * Miscellaneous information: | |
128 | */ | |
129 | ||
130 | Cursor cursor; /* Current cursor for window, or None. */ | |
131 | char *command; /* Command to execute when button is | |
132 | * invoked; valid for buttons only. | |
133 | * If not NULL, it's malloc-ed. */ | |
134 | int flags; /* Various flags; see below for | |
135 | * definitions. */ | |
136 | Tk_TimerToken updateTimerToken; /* Added by Don to optimize rapid | |
137 | * updates. */ | |
138 | } Button; | |
139 | ||
140 | /* | |
141 | * Possible "type" values for buttons. These are the kinds of | |
142 | * widgets supported by this file. The ordering of the type | |
143 | * numbers is significant: greater means more features and is | |
144 | * used in the code. | |
145 | */ | |
146 | ||
147 | #define TYPE_LABEL 0 | |
148 | #define TYPE_BUTTON 1 | |
149 | #define TYPE_CHECK_BUTTON 2 | |
150 | #define TYPE_RADIO_BUTTON 3 | |
151 | ||
152 | /* | |
153 | * Class names for buttons, indexed by one of the type values above. | |
154 | */ | |
155 | ||
156 | static char *classNames[] = {"Label", "Button", "CheckButton", "RadioButton"}; | |
157 | ||
158 | /* | |
159 | * Flag bits for buttons: | |
160 | * | |
161 | * REDRAW_PENDING: Non-zero means a DoWhenIdle handler | |
162 | * has already been queued to redraw | |
163 | * this window. | |
164 | * SELECTED: Non-zero means this button is selected, | |
165 | * so special highlight should be drawn. | |
166 | */ | |
167 | ||
168 | #define REDRAW_PENDING 1 | |
169 | #define SELECTED 2 | |
170 | ||
171 | /* | |
172 | * Mask values used to selectively enable entries in the | |
173 | * configuration specs: | |
174 | */ | |
175 | ||
176 | #define LABEL_MASK TK_CONFIG_USER_BIT | |
177 | #define BUTTON_MASK TK_CONFIG_USER_BIT << 1 | |
178 | #define CHECK_BUTTON_MASK TK_CONFIG_USER_BIT << 2 | |
179 | #define RADIO_BUTTON_MASK TK_CONFIG_USER_BIT << 3 | |
180 | #define ALL_MASK (LABEL_MASK | BUTTON_MASK \ | |
181 | | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK) | |
182 | ||
183 | static int configFlags[] = {LABEL_MASK, BUTTON_MASK, | |
184 | CHECK_BUTTON_MASK, RADIO_BUTTON_MASK}; | |
185 | /* | |
186 | * Information used for parsing configuration specs: | |
187 | */ | |
188 | ||
189 | static Tk_ConfigSpec configSpecs[] = { | |
190 | {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground", | |
191 | DEF_BUTTON_ACTIVE_BG_COLOR, Tk_Offset(Button, activeBorder), | |
192 | BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK | |
193 | |TK_CONFIG_COLOR_ONLY}, | |
194 | {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground", | |
195 | DEF_BUTTON_ACTIVE_BG_MONO, Tk_Offset(Button, activeBorder), | |
196 | BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK | |
197 | |TK_CONFIG_MONO_ONLY}, | |
198 | {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background", | |
199 | DEF_BUTTON_ACTIVE_FG_COLOR, Tk_Offset(Button, activeFg), | |
200 | BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK | |
201 | |TK_CONFIG_COLOR_ONLY}, | |
202 | {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background", | |
203 | DEF_BUTTON_ACTIVE_FG_MONO, Tk_Offset(Button, activeFg), | |
204 | BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK | |
205 | |TK_CONFIG_MONO_ONLY}, | |
206 | {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor", | |
207 | DEF_BUTTON_ANCHOR, Tk_Offset(Button, anchor), ALL_MASK}, | |
208 | {TK_CONFIG_BORDER, "-background", "background", "Background", | |
209 | DEF_BUTTON_BG_COLOR, Tk_Offset(Button, normalBorder), | |
210 | ALL_MASK | TK_CONFIG_COLOR_ONLY}, | |
211 | {TK_CONFIG_BORDER, "-background", "background", "Background", | |
212 | DEF_BUTTON_BG_MONO, Tk_Offset(Button, normalBorder), | |
213 | ALL_MASK | TK_CONFIG_MONO_ONLY}, | |
214 | {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL, | |
215 | (char *) NULL, 0, ALL_MASK}, | |
216 | {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, | |
217 | (char *) NULL, 0, ALL_MASK}, | |
218 | #if defined(USE_XPM3) | |
219 | {TK_CONFIG_PIXMAP, "-bitmap", "bitmap", "Bitmap", | |
220 | DEF_BUTTON_BITMAP, Tk_Offset(Button, bitmap), | |
221 | ALL_MASK|TK_CONFIG_NULL_OK}, | |
222 | #else | |
223 | {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap", | |
224 | DEF_BUTTON_BITMAP, Tk_Offset(Button, bitmap), | |
225 | ALL_MASK|TK_CONFIG_NULL_OK}, | |
226 | #endif | |
227 | {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", | |
228 | DEF_BUTTON_BORDER_WIDTH, Tk_Offset(Button, borderWidth), ALL_MASK}, | |
229 | {TK_CONFIG_STRING, "-command", "command", "Command", | |
230 | DEF_BUTTON_COMMAND, Tk_Offset(Button, command), | |
231 | BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK}, | |
232 | {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", | |
233 | DEF_BUTTON_CURSOR, Tk_Offset(Button, cursor), | |
234 | ALL_MASK|TK_CONFIG_NULL_OK}, | |
235 | {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground", | |
236 | "DisabledForeground", DEF_BUTTON_DISABLED_FG_COLOR, | |
237 | Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK | |
238 | |RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK}, | |
239 | {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground", | |
240 | "DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO, | |
241 | Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK | |
242 | |RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK}, | |
243 | {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL, | |
244 | (char *) NULL, 0, ALL_MASK}, | |
245 | {TK_CONFIG_FONT, "-font", "font", "Font", | |
246 | DEF_BUTTON_FONT, Tk_Offset(Button, fontPtr), | |
247 | ALL_MASK}, | |
248 | {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground", | |
249 | DEF_BUTTON_FG, Tk_Offset(Button, normalFg), ALL_MASK}, | |
250 | {TK_CONFIG_INT, "-height", "height", "Height", | |
251 | DEF_BUTTON_HEIGHT, Tk_Offset(Button, height), ALL_MASK}, | |
252 | {TK_CONFIG_STRING, "-offvalue", "offValue", "Value", | |
253 | DEF_BUTTON_OFF_VALUE, Tk_Offset(Button, offValue), | |
254 | CHECK_BUTTON_MASK}, | |
255 | {TK_CONFIG_STRING, "-onvalue", "onValue", "Value", | |
256 | DEF_BUTTON_ON_VALUE, Tk_Offset(Button, onValue), | |
257 | CHECK_BUTTON_MASK}, | |
258 | {TK_CONFIG_PIXELS, "-padx", "padX", "Pad", | |
259 | DEF_BUTTON_PADX, Tk_Offset(Button, padX), ALL_MASK}, | |
260 | {TK_CONFIG_PIXELS, "-pady", "padY", "Pad", | |
261 | DEF_BUTTON_PADY, Tk_Offset(Button, padY), ALL_MASK}, | |
262 | {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", | |
263 | DEF_BUTTON_RELIEF, Tk_Offset(Button, relief), | |
264 | BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK}, | |
265 | {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", | |
266 | DEF_LABEL_RELIEF, Tk_Offset(Button, relief), LABEL_MASK}, | |
267 | {TK_CONFIG_COLOR, "-selector", "selector", "Foreground", | |
268 | DEF_BUTTON_SELECTOR_COLOR, Tk_Offset(Button, selectorFg), | |
269 | CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY | |
270 | |TK_CONFIG_NULL_OK}, | |
271 | {TK_CONFIG_COLOR, "-selector", "selector", "Foreground", | |
272 | DEF_BUTTON_SELECTOR_MONO, Tk_Offset(Button, selectorFg), | |
273 | CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY | |
274 | |TK_CONFIG_NULL_OK}, | |
275 | {TK_CONFIG_UID, "-state", "state", "State", | |
276 | DEF_BUTTON_STATE, Tk_Offset(Button, state), | |
277 | BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK}, | |
278 | {TK_CONFIG_STRING, "-text", "text", "Text", | |
279 | DEF_BUTTON_TEXT, Tk_Offset(Button, text), ALL_MASK}, | |
280 | {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable", | |
281 | DEF_BUTTON_TEXT_VARIABLE, Tk_Offset(Button, textVarName), | |
282 | ALL_MASK|TK_CONFIG_NULL_OK}, | |
283 | {TK_CONFIG_STRING, "-value", "value", "Value", | |
284 | DEF_BUTTON_VALUE, Tk_Offset(Button, onValue), | |
285 | RADIO_BUTTON_MASK}, | |
286 | {TK_CONFIG_STRING, "-variable", "variable", "Variable", | |
287 | DEF_RADIOBUTTON_VARIABLE, Tk_Offset(Button, selVarName), | |
288 | RADIO_BUTTON_MASK}, | |
289 | {TK_CONFIG_STRING, "-variable", "variable", "Variable", | |
290 | DEF_CHECKBUTTON_VARIABLE, Tk_Offset(Button, selVarName), | |
291 | CHECK_BUTTON_MASK}, | |
292 | {TK_CONFIG_INT, "-width", "width", "Width", | |
293 | DEF_BUTTON_WIDTH, Tk_Offset(Button, width), ALL_MASK}, | |
294 | {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, | |
295 | (char *) NULL, 0, 0} | |
296 | }; | |
297 | ||
298 | /* | |
299 | * String to print out in error messages, identifying options for | |
300 | * widget commands for different types of labels or buttons: | |
301 | */ | |
302 | ||
303 | static char *optionStrings[] = { | |
304 | "configure", | |
305 | "activate, configure, deactivate, flash, or invoke", | |
306 | "activate, configure, deactivate, deselect, flash, invoke, select, or toggle", | |
307 | "activate, configure, deactivate, deselect, flash, invoke, or select" | |
308 | }; | |
309 | ||
310 | static int ButtonUpdateTime = 200; // Added by Don. | |
311 | ||
312 | /* | |
313 | * Forward declarations for procedures defined later in this file: | |
314 | */ | |
315 | ||
316 | static void ButtonEventProc _ANSI_ARGS_((ClientData clientData, | |
317 | XEvent *eventPtr)); | |
318 | static char * ButtonTextVarProc _ANSI_ARGS_((ClientData clientData, | |
319 | Tcl_Interp *interp, char *name1, char *name2, | |
320 | int flags)); | |
321 | static char * ButtonVarProc _ANSI_ARGS_((ClientData clientData, | |
322 | Tcl_Interp *interp, char *name1, char *name2, | |
323 | int flags)); | |
324 | static int ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData, | |
325 | Tcl_Interp *interp, int argc, char **argv)); | |
326 | static void ComputeButtonGeometry _ANSI_ARGS_((Button *butPtr)); | |
327 | static int ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp, | |
328 | Button *butPtr, int argc, char **argv, | |
329 | int flags)); | |
330 | static void DestroyButton _ANSI_ARGS_((ClientData clientData)); | |
331 | static void DisplayButton _ANSI_ARGS_((ClientData clientData)); | |
332 | static int InvokeButton _ANSI_ARGS_((Button *butPtr)); | |
333 | \f | |
334 | /* | |
335 | *-------------------------------------------------------------- | |
336 | * | |
337 | * Tk_ButtonCmd -- | |
338 | * | |
339 | * This procedure is invoked to process the "button", "label", | |
340 | * "radiobutton", and "checkbutton" Tcl commands. See the | |
341 | * user documentation for details on what it does. | |
342 | * | |
343 | * Results: | |
344 | * A standard Tcl result. | |
345 | * | |
346 | * Side effects: | |
347 | * See the user documentation. | |
348 | * | |
349 | *-------------------------------------------------------------- | |
350 | */ | |
351 | ||
352 | int | |
353 | Tk_ButtonCmd(clientData, interp, argc, argv) | |
354 | ClientData clientData; /* Main window associated with | |
355 | * interpreter. */ | |
356 | Tcl_Interp *interp; /* Current interpreter. */ | |
357 | int argc; /* Number of arguments. */ | |
358 | char **argv; /* Argument strings. */ | |
359 | { | |
360 | register Button *butPtr; | |
361 | int type; | |
362 | Tk_Window tkwin = (Tk_Window) clientData; | |
363 | Tk_Window new; | |
364 | ||
365 | if (argc < 2) { | |
366 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
367 | argv[0], " pathName ?options?\"", (char *) NULL); | |
368 | return TCL_ERROR; | |
369 | } | |
370 | ||
371 | switch (argv[0][0]) { | |
372 | case 'l': | |
373 | type = TYPE_LABEL; | |
374 | break; | |
375 | case 'b': | |
376 | type = TYPE_BUTTON; | |
377 | break; | |
378 | case 'c': | |
379 | type = TYPE_CHECK_BUTTON; | |
380 | break; | |
381 | case 'r': | |
382 | type = TYPE_RADIO_BUTTON; | |
383 | break; | |
384 | default: | |
385 | sprintf(interp->result, | |
386 | "unknown button-creation command \"%.50s\""); | |
387 | return TCL_ERROR; | |
388 | } | |
389 | ||
390 | /* | |
391 | * Create the new window. | |
392 | */ | |
393 | ||
394 | new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL); | |
395 | if (new == NULL) { | |
396 | return TCL_ERROR; | |
397 | } | |
398 | ||
399 | /* | |
400 | * Initialize the data structure for the button. | |
401 | */ | |
402 | ||
403 | butPtr = (Button *) ckalloc(sizeof(Button)); | |
404 | butPtr->tkwin = new; | |
405 | butPtr->interp = interp; | |
406 | butPtr->type = type; | |
407 | butPtr->text = NULL; | |
408 | butPtr->textVarName = NULL; | |
409 | butPtr->bitmap = None; | |
410 | butPtr->state = tkNormalUid; | |
411 | butPtr->normalBorder = NULL; | |
412 | butPtr->activeBorder = NULL; | |
413 | butPtr->borderWidth = 0; | |
414 | butPtr->relief = TK_RELIEF_FLAT; | |
415 | butPtr->fontPtr = NULL; | |
416 | butPtr->normalFg = NULL; | |
417 | butPtr->activeFg = NULL; | |
418 | butPtr->disabledFg = NULL; | |
419 | butPtr->normalTextGC = None; | |
420 | butPtr->activeTextGC = None; | |
421 | butPtr->gray = None; | |
422 | butPtr->disabledGC = None; | |
423 | butPtr->selectorFg = NULL; | |
424 | butPtr->selectorGC = None; | |
425 | butPtr->selVarName = NULL; | |
426 | butPtr->onValue = NULL; | |
427 | butPtr->offValue = NULL; | |
428 | butPtr->cursor = None; | |
429 | butPtr->command = NULL; | |
430 | butPtr->flags = 0; | |
431 | butPtr->updateTimerToken = 0; | |
432 | ||
433 | Tk_SetClass(new, classNames[type]); | |
434 | //fprintf(stderr, "ButtonWidgetCmd Made %s %s\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin)); | |
435 | Tk_CreateEventHandler(butPtr->tkwin, ExposureMask|StructureNotifyMask, | |
436 | ButtonEventProc, (ClientData) butPtr); | |
437 | Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin), ButtonWidgetCmd, | |
438 | (ClientData) butPtr, (void (*)()) NULL); | |
439 | if (ConfigureButton(interp, butPtr, argc-2, argv+2, | |
440 | configFlags[type]) != TCL_OK) { | |
441 | Tk_DestroyWindow(butPtr->tkwin); | |
442 | return TCL_ERROR; | |
443 | } | |
444 | ||
445 | interp->result = Tk_PathName(butPtr->tkwin); | |
446 | return TCL_OK; | |
447 | } | |
448 | \f | |
449 | /* | |
450 | *-------------------------------------------------------------- | |
451 | * | |
452 | * ButtonWidgetCmd -- | |
453 | * | |
454 | * This procedure is invoked to process the Tcl command | |
455 | * that corresponds to a widget managed by this module. | |
456 | * See the user documentation for details on what it does. | |
457 | * | |
458 | * Results: | |
459 | * A standard Tcl result. | |
460 | * | |
461 | * Side effects: | |
462 | * See the user documentation. | |
463 | * | |
464 | *-------------------------------------------------------------- | |
465 | */ | |
466 | ||
467 | static int | |
468 | ButtonWidgetCmd(clientData, interp, argc, argv) | |
469 | ClientData clientData; /* Information about button widget. */ | |
470 | Tcl_Interp *interp; /* Current interpreter. */ | |
471 | int argc; /* Number of arguments. */ | |
472 | char **argv; /* Argument strings. */ | |
473 | { | |
474 | register Button *butPtr = (Button *) clientData; | |
475 | int result = TCL_OK; | |
476 | int length; | |
477 | char c; | |
478 | ||
479 | if (argc < 2) { | |
480 | sprintf(interp->result, | |
481 | "wrong # args: should be \"%.50s option [arg arg ...]\"", | |
482 | argv[0]); | |
483 | return TCL_ERROR; | |
484 | } | |
485 | Tk_Preserve((ClientData) butPtr); | |
486 | c = argv[1][0]; | |
487 | length = strlen(argv[1]); | |
488 | if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0) | |
489 | && (butPtr->type != TYPE_LABEL)) { | |
490 | if (argc > 2) { | |
491 | sprintf(interp->result, | |
492 | "wrong # args: should be \"%.50s activate\"", | |
493 | argv[0]); | |
494 | goto error; | |
495 | } | |
496 | if (butPtr->state != tkDisabledUid) { | |
497 | butPtr->state = tkActiveUid; | |
498 | Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder); | |
499 | goto redisplay; | |
500 | } | |
501 | } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) { | |
502 | if (argc == 2) { | |
503 | result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs, | |
504 | (char *) butPtr, (char *) NULL, configFlags[butPtr->type]); | |
505 | } else if (argc == 3) { | |
506 | result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs, | |
507 | (char *) butPtr, argv[2], | |
508 | configFlags[butPtr->type]); | |
509 | } else { | |
510 | result = ConfigureButton(interp, butPtr, argc-2, argv+2, | |
511 | configFlags[butPtr->type] | TK_CONFIG_ARGV_ONLY); | |
512 | } | |
513 | } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0) | |
514 | && (length > 2) && (butPtr->type != TYPE_LABEL)) { | |
515 | if (argc > 2) { | |
516 | sprintf(interp->result, | |
517 | "wrong # args: should be \"%.50s deactivate\"", | |
518 | argv[0]); | |
519 | goto error; | |
520 | } | |
521 | if (butPtr->state != tkDisabledUid) { | |
522 | butPtr->state = tkNormalUid; | |
523 | Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder); | |
524 | goto redisplay; | |
525 | } | |
526 | } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0) | |
527 | && (length > 2) && (butPtr->type >= TYPE_CHECK_BUTTON)) { | |
528 | if (argc > 2) { | |
529 | sprintf(interp->result, | |
530 | "wrong # args: should be \"%.50s deselect\"", | |
531 | argv[0]); | |
532 | goto error; | |
533 | } | |
534 | if (butPtr->type == TYPE_CHECK_BUTTON) { | |
535 | Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue, | |
536 | TCL_GLOBAL_ONLY); | |
537 | } else if (butPtr->flags & SELECTED) { | |
538 | Tcl_SetVar(interp, butPtr->selVarName, "", TCL_GLOBAL_ONLY); | |
539 | } | |
540 | } else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0) | |
541 | && (butPtr->type != TYPE_LABEL)) { | |
542 | int i; | |
543 | ||
544 | if (argc > 2) { | |
545 | sprintf(interp->result, | |
546 | "wrong # args: should be \"%.50s flash\"", | |
547 | argv[0]); | |
548 | goto error; | |
549 | } | |
550 | if (butPtr->state != tkDisabledUid) { | |
551 | for (i = 0; i < 4; i++) { | |
552 | butPtr->state = (butPtr->state == tkNormalUid) | |
553 | ? tkActiveUid : tkNormalUid; | |
554 | Tk_SetBackgroundFromBorder(butPtr->tkwin, | |
555 | (butPtr->state == tkActiveUid) ? butPtr->activeBorder | |
556 | : butPtr->normalBorder); | |
557 | DisplayButton((ClientData) butPtr); | |
558 | XFlush(Tk_Display(butPtr->tkwin)); | |
559 | Tk_Sleep(50); | |
560 | } | |
561 | } | |
562 | } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0) | |
563 | && (butPtr->type > TYPE_LABEL)) { | |
564 | if (argc > 2) { | |
565 | sprintf(interp->result, | |
566 | "wrong # args: should be \"%.50s invoke\"", | |
567 | argv[0]); | |
568 | goto error; | |
569 | } | |
570 | if (butPtr->state != tkDisabledUid) { | |
571 | result = InvokeButton(butPtr); | |
572 | } | |
573 | } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0) | |
574 | && (butPtr->type >= TYPE_CHECK_BUTTON)) { | |
575 | if (argc > 2) { | |
576 | sprintf(interp->result, | |
577 | "wrong # args: should be \"%.50s select\"", | |
578 | argv[0]); | |
579 | goto error; | |
580 | } | |
581 | Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY); | |
582 | } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0) | |
583 | && (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) { | |
584 | if (argc > 2) { | |
585 | sprintf(interp->result, | |
586 | "wrong # args: should be \"%.50s select\"", | |
587 | argv[0]); | |
588 | goto error; | |
589 | } | |
590 | if (butPtr->flags & SELECTED) { | |
591 | Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue, TCL_GLOBAL_ONLY); | |
592 | } else { | |
593 | Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY); | |
594 | } | |
595 | } else { | |
596 | sprintf(interp->result, | |
597 | "bad option \"%.50s\": must be %s", argv[1], | |
598 | optionStrings[butPtr->type]); | |
599 | goto error; | |
600 | } | |
601 | Tk_Release((ClientData) butPtr); | |
602 | return result; | |
603 | ||
604 | redisplay: | |
605 | if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) { | |
606 | //Tk_TimerToken last = butPtr->updateTimerToken; | |
607 | butPtr->flags |= REDRAW_PENDING; | |
608 | // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr); | |
609 | assert(butPtr->updateTimerToken == 0); | |
610 | if (butPtr->updateTimerToken == 0) { | |
611 | butPtr->updateTimerToken = | |
612 | Tk_CreateTimerHandler( | |
613 | ButtonUpdateTime, | |
614 | DisplayButton, | |
615 | (ClientData) butPtr); | |
616 | } | |
617 | //fprintf(stderr, "ButtonWidgetCmd Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken); | |
618 | } | |
619 | Tk_Release((ClientData) butPtr); | |
620 | return TCL_OK; | |
621 | ||
622 | error: | |
623 | Tk_Release((ClientData) butPtr); | |
624 | return TCL_ERROR; | |
625 | } | |
626 | \f | |
627 | /* | |
628 | *---------------------------------------------------------------------- | |
629 | * | |
630 | * DestroyButton -- | |
631 | * | |
632 | * This procedure is invoked by Tk_EventuallyFree or Tk_Release | |
633 | * to clean up the internal structure of a button at a safe time | |
634 | * (when no-one is using it anymore). | |
635 | * | |
636 | * Results: | |
637 | * None. | |
638 | * | |
639 | * Side effects: | |
640 | * Everything associated with the widget is freed up. | |
641 | * | |
642 | *---------------------------------------------------------------------- | |
643 | */ | |
644 | ||
645 | static void | |
646 | DestroyButton(clientData) | |
647 | ClientData clientData; /* Info about entry widget. */ | |
648 | { | |
649 | register Button *butPtr = (Button *) clientData; | |
650 | ||
651 | if (butPtr->text != NULL) { | |
652 | ckfree(butPtr->text); | |
653 | } | |
654 | if (butPtr->textVarName != NULL) { | |
655 | Tcl_UntraceVar(butPtr->interp, butPtr->textVarName, | |
656 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
657 | ButtonTextVarProc, (ClientData) butPtr); | |
658 | ckfree(butPtr->textVarName); | |
659 | } | |
660 | if (butPtr->bitmap != None) { | |
661 | #if defined(USE_XPM3) | |
662 | Tk_FreePixmap(butPtr->bitmap); | |
663 | #else | |
664 | Tk_FreeBitmap(butPtr->bitmap); | |
665 | #endif | |
666 | } | |
667 | if (butPtr->normalBorder != NULL) { | |
668 | Tk_Free3DBorder(butPtr->normalBorder); | |
669 | } | |
670 | if (butPtr->activeBorder != NULL) { | |
671 | Tk_Free3DBorder(butPtr->activeBorder); | |
672 | } | |
673 | if (butPtr->fontPtr != NULL) { | |
674 | Tk_FreeFontStruct(butPtr->fontPtr); | |
675 | } | |
676 | if (butPtr->normalFg != NULL) { | |
677 | Tk_FreeColor(butPtr->normalFg); | |
678 | } | |
679 | if (butPtr->disabledFg != NULL) { | |
680 | Tk_FreeColor(butPtr->disabledFg); | |
681 | } | |
682 | if (butPtr->activeFg != NULL) { | |
683 | Tk_FreeColor(butPtr->activeFg); | |
684 | } | |
685 | if (butPtr->normalTextGC != None) { | |
686 | Tk_FreeGC(butPtr->normalTextGC); | |
687 | } | |
688 | if (butPtr->activeTextGC != None) { | |
689 | Tk_FreeGC(butPtr->activeTextGC); | |
690 | } | |
691 | if (butPtr->gray != None) { | |
692 | Tk_FreeBitmap(butPtr->gray); | |
693 | } | |
694 | if (butPtr->disabledGC != None) { | |
695 | Tk_FreeGC(butPtr->disabledGC); | |
696 | } | |
697 | if (butPtr->selectorFg != NULL) { | |
698 | Tk_FreeColor(butPtr->selectorFg); | |
699 | } | |
700 | if (butPtr->selectorGC != None) { | |
701 | Tk_FreeGC(butPtr->selectorGC); | |
702 | } | |
703 | if (butPtr->selVarName != NULL) { | |
704 | Tcl_UntraceVar(butPtr->interp, butPtr->selVarName, | |
705 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
706 | ButtonVarProc, (ClientData) butPtr); | |
707 | ckfree(butPtr->selVarName); | |
708 | } | |
709 | if (butPtr->onValue != NULL) { | |
710 | ckfree(butPtr->onValue); | |
711 | } | |
712 | if (butPtr->offValue != NULL) { | |
713 | ckfree(butPtr->offValue); | |
714 | } | |
715 | if (butPtr->cursor != None) { | |
716 | Tk_FreeCursor(butPtr->cursor); | |
717 | } | |
718 | if (butPtr->command != NULL) { | |
719 | ckfree(butPtr->command); | |
720 | } | |
721 | if (butPtr->updateTimerToken != NULL) { | |
722 | Tk_DeleteTimerHandler(butPtr->updateTimerToken); | |
723 | //fprintf(stderr, "DestroyButton Delete Timer was %d now 0\n", butPtr->updateTimerToken); | |
724 | butPtr->updateTimerToken = 0; | |
725 | ||
726 | } | |
727 | ckfree((char *) butPtr); | |
728 | } | |
729 | \f | |
730 | /* | |
731 | *---------------------------------------------------------------------- | |
732 | * | |
733 | * ConfigureButton -- | |
734 | * | |
735 | * This procedure is called to process an argv/argc list, plus | |
736 | * the Tk option database, in order to configure (or | |
737 | * reconfigure) a button widget. | |
738 | * | |
739 | * Results: | |
740 | * The return value is a standard Tcl result. If TCL_ERROR is | |
741 | * returned, then interp->result contains an error message. | |
742 | * | |
743 | * Side effects: | |
744 | * Configuration information, such as text string, colors, font, | |
745 | * etc. get set for butPtr; old resources get freed, if there | |
746 | * were any. The button is redisplayed. | |
747 | * | |
748 | *---------------------------------------------------------------------- | |
749 | */ | |
750 | ||
751 | static int | |
752 | ConfigureButton(interp, butPtr, argc, argv, flags) | |
753 | Tcl_Interp *interp; /* Used for error reporting. */ | |
754 | register Button *butPtr; /* Information about widget; may or may | |
755 | * not already have values for some fields. */ | |
756 | int argc; /* Number of valid entries in argv. */ | |
757 | char **argv; /* Arguments. */ | |
758 | int flags; /* Flags to pass to Tk_ConfigureWidget. */ | |
759 | { | |
760 | XGCValues gcValues; | |
761 | GC newGC; | |
762 | unsigned long mask; | |
763 | ||
764 | /* | |
765 | * Eliminate any existing trace on variables monitored by the button. | |
766 | */ | |
767 | ||
768 | if (butPtr->textVarName != NULL) { | |
769 | Tcl_UntraceVar(interp, butPtr->textVarName, | |
770 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
771 | ButtonTextVarProc, (ClientData) butPtr); | |
772 | } | |
773 | if (butPtr->selVarName != NULL) { | |
774 | Tcl_UntraceVar(interp, butPtr->selVarName, | |
775 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
776 | ButtonVarProc, (ClientData) butPtr); | |
777 | } | |
778 | ||
779 | if (Tk_ConfigureWidget(interp, butPtr->tkwin, configSpecs, | |
780 | argc, argv, (char *) butPtr, flags) != TCL_OK) { | |
781 | return TCL_ERROR; | |
782 | } | |
783 | ||
784 | /* | |
785 | * A few options need special processing, such as setting the | |
786 | * background from a 3-D border, or filling in complicated | |
787 | * defaults that couldn't be specified to Tk_ConfigureWidget. | |
788 | */ | |
789 | ||
790 | if (butPtr->state == tkActiveUid) { | |
791 | Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder); | |
792 | } else { | |
793 | Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder); | |
794 | if ((butPtr->state != tkNormalUid) | |
795 | && (butPtr->state != tkDisabledUid)) { | |
796 | Tcl_AppendResult(interp, "bad state value \"", butPtr->state, | |
797 | "\": must be normal, active, or disabled", (char *) NULL); | |
798 | butPtr->state = tkNormalUid; | |
799 | return TCL_ERROR; | |
800 | } | |
801 | } | |
802 | ||
803 | gcValues.font = butPtr->fontPtr->fid; | |
804 | gcValues.foreground = butPtr->normalFg->pixel; | |
805 | gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel; | |
806 | ||
807 | /* | |
808 | * Note: GraphicsExpose events are disabled in normalTextGC because it's | |
809 | * used to copy stuff from an off-screen pixmap onto the screen (we know | |
810 | * that there's no problem with obscured areas). | |
811 | */ | |
812 | ||
813 | gcValues.graphics_exposures = False; | |
814 | newGC = Tk_GetGC(butPtr->tkwin, | |
815 | GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcValues); | |
816 | if (butPtr->normalTextGC != None) { | |
817 | Tk_FreeGC(butPtr->normalTextGC); | |
818 | } | |
819 | butPtr->normalTextGC = newGC; | |
820 | ||
821 | if (butPtr->activeFg != NULL) { | |
822 | gcValues.font = butPtr->fontPtr->fid; | |
823 | gcValues.foreground = butPtr->activeFg->pixel; | |
824 | gcValues.background = Tk_3DBorderColor(butPtr->activeBorder)->pixel; | |
825 | newGC = Tk_GetGC(butPtr->tkwin, GCForeground|GCBackground|GCFont, | |
826 | &gcValues); | |
827 | if (butPtr->activeTextGC != None) { | |
828 | Tk_FreeGC(butPtr->activeTextGC); | |
829 | } | |
830 | butPtr->activeTextGC = newGC; | |
831 | } | |
832 | ||
833 | gcValues.font = butPtr->fontPtr->fid; | |
834 | gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel; | |
835 | if (butPtr->disabledFg != NULL) { | |
836 | gcValues.foreground = butPtr->disabledFg->pixel; | |
837 | mask = GCForeground|GCBackground|GCFont; | |
838 | } else { | |
839 | gcValues.foreground = gcValues.background; | |
840 | if (butPtr->gray == None) { | |
841 | butPtr->gray = Tk_GetBitmap(interp, butPtr->tkwin, | |
842 | Tk_GetUid("gray50")); | |
843 | if (butPtr->gray == None) { | |
844 | return TCL_ERROR; | |
845 | } | |
846 | } | |
847 | gcValues.fill_style = FillStippled; | |
848 | gcValues.stipple = butPtr->gray; | |
849 | mask = GCForeground|GCFillStyle|GCStipple; | |
850 | } | |
851 | newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues); | |
852 | if (butPtr->disabledGC != None) { | |
853 | Tk_FreeGC(butPtr->disabledGC); | |
854 | } | |
855 | butPtr->disabledGC = newGC; | |
856 | ||
857 | if (butPtr->padX < 0) { | |
858 | butPtr->padX = 0; | |
859 | } | |
860 | if (butPtr->padY < 0) { | |
861 | butPtr->padY = 0; | |
862 | } | |
863 | ||
864 | if (butPtr->type >= TYPE_CHECK_BUTTON) { | |
865 | char *value; | |
866 | ||
867 | if (butPtr->selectorFg != NULL) { | |
868 | gcValues.foreground = butPtr->selectorFg->pixel; | |
869 | newGC = Tk_GetGC(butPtr->tkwin, GCForeground, &gcValues); | |
870 | } else { | |
871 | newGC = None; | |
872 | } | |
873 | if (butPtr->selectorGC != None) { | |
874 | Tk_FreeGC(butPtr->selectorGC); | |
875 | } | |
876 | butPtr->selectorGC = newGC; | |
877 | ||
878 | if (butPtr->selVarName == NULL) { | |
879 | butPtr->selVarName = (char *) ckalloc((unsigned) | |
880 | (strlen(Tk_Name(butPtr->tkwin)) + 1)); | |
881 | strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin)); | |
882 | } | |
883 | if (butPtr->onValue == NULL) { | |
884 | butPtr->onValue = (char *) ckalloc((unsigned) | |
885 | (strlen(Tk_Name(butPtr->tkwin)) + 1)); | |
886 | strcpy(butPtr->onValue, Tk_Name(butPtr->tkwin)); | |
887 | } | |
888 | ||
889 | /* | |
890 | * Select the button if the associated variable has the | |
891 | * appropriate value, initialize the variable if it doesn't | |
892 | * exist, then set a trace on the variable to monitor future | |
893 | * changes to its value. | |
894 | */ | |
895 | ||
896 | value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY); | |
897 | butPtr->flags &= ~SELECTED; | |
898 | if (value != NULL) { | |
899 | if (strcmp(value, butPtr->onValue) == 0) { | |
900 | butPtr->flags |= SELECTED; | |
901 | } | |
902 | } else { | |
903 | Tcl_SetVar(interp, butPtr->selVarName, | |
904 | (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "", | |
905 | TCL_GLOBAL_ONLY); | |
906 | } | |
907 | Tcl_TraceVar(interp, butPtr->selVarName, | |
908 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
909 | ButtonVarProc, (ClientData) butPtr); | |
910 | } | |
911 | ||
912 | /* | |
913 | * If the button is to display the value of a variable, then set up | |
914 | * a trace on the variable's value, create the variable if it doesn't | |
915 | * exist, and fetch its current value. | |
916 | */ | |
917 | ||
918 | if ((butPtr->bitmap == None) && (butPtr->textVarName != NULL)) { | |
919 | char *value; | |
920 | ||
921 | value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY); | |
922 | if (value == NULL) { | |
923 | Tcl_SetVar(interp, butPtr->textVarName, butPtr->text, | |
924 | TCL_GLOBAL_ONLY); | |
925 | } else { | |
926 | if (butPtr->text != NULL) { | |
927 | ckfree(butPtr->text); | |
928 | } | |
929 | butPtr->text = ckalloc((unsigned) (strlen(value) + 1)); | |
930 | strcpy(butPtr->text, value); | |
931 | } | |
932 | Tcl_TraceVar(interp, butPtr->textVarName, | |
933 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
934 | ButtonTextVarProc, (ClientData) butPtr); | |
935 | } | |
936 | ||
937 | ComputeButtonGeometry(butPtr); | |
938 | ||
939 | /* | |
940 | * Lastly, arrange for the button to be redisplayed. | |
941 | */ | |
942 | ||
943 | if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) { | |
944 | //Tk_TimerToken last = butPtr->updateTimerToken; | |
945 | butPtr->flags |= REDRAW_PENDING; | |
946 | // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr); | |
947 | assert(butPtr->updateTimerToken == 0); | |
948 | if (butPtr->updateTimerToken == 0) { | |
949 | butPtr->updateTimerToken = | |
950 | Tk_CreateTimerHandler( | |
951 | ButtonUpdateTime, | |
952 | DisplayButton, | |
953 | (ClientData) butPtr); | |
954 | } | |
955 | //fprintf(stderr, "ConfigureButton Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken); | |
956 | } | |
957 | ||
958 | return TCL_OK; | |
959 | } | |
960 | \f | |
961 | /* | |
962 | *---------------------------------------------------------------------- | |
963 | * | |
964 | * DisplayButton -- | |
965 | * | |
966 | * This procedure is invoked to display a button widget. | |
967 | * | |
968 | * Results: | |
969 | * None. | |
970 | * | |
971 | * Side effects: | |
972 | * Commands are output to X to display the button in its | |
973 | * current mode. | |
974 | * | |
975 | *---------------------------------------------------------------------- | |
976 | */ | |
977 | ||
978 | static void | |
979 | DisplayButton(clientData) | |
980 | ClientData clientData; /* Information about widget. */ | |
981 | { | |
982 | register Button *butPtr = (Button *) clientData; | |
983 | GC gc; | |
984 | Tk_3DBorder border; | |
985 | Pixmap pixmap; | |
986 | int x = 0; /* Initialization only needed to stop | |
987 | * compiler warning. */ | |
988 | int y; | |
989 | register Tk_Window tkwin = butPtr->tkwin; | |
990 | ||
991 | //fprintf(stderr, "DisplayButton Handled Timer %s %s was %d now 0\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), butPtr->updateTimerToken); | |
992 | ||
993 | assert(butPtr->updateTimerToken != 0); | |
994 | butPtr->updateTimerToken = 0; | |
995 | ||
996 | butPtr->flags &= ~REDRAW_PENDING; | |
997 | if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { | |
998 | return; | |
999 | } | |
1000 | ||
1001 | if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) { | |
1002 | gc = butPtr->disabledGC; | |
1003 | border = butPtr->normalBorder; | |
1004 | } else if (butPtr->state == tkActiveUid) { | |
1005 | gc = butPtr->activeTextGC; | |
1006 | border = butPtr->activeBorder; | |
1007 | } else { | |
1008 | gc = butPtr->normalTextGC; | |
1009 | border = butPtr->normalBorder; | |
1010 | } | |
1011 | ||
1012 | /* | |
1013 | * In order to avoid screen flashes, this procedure redraws | |
1014 | * the button in a pixmap, then copies the pixmap to the | |
1015 | * screen in a single operation. This means that there's no | |
1016 | * point in time where the on-sreen image has been cleared. | |
1017 | */ | |
1018 | ||
1019 | pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin), | |
1020 | Tk_Width(tkwin), Tk_Height(tkwin), | |
1021 | Tk_DefaultDepth(Tk_Screen(tkwin))); | |
1022 | Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border, | |
1023 | 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT); | |
1024 | ||
1025 | /* | |
1026 | * Display bitmap or text for button. | |
1027 | */ | |
1028 | ||
1029 | if (butPtr->bitmap != None) { | |
1030 | unsigned int width, height; | |
1031 | ||
1032 | #if defined(USE_XPM3) | |
1033 | Tk_SizeOfPixmap(butPtr->bitmap, &width, &height); | |
1034 | #else | |
1035 | Tk_SizeOfBitmap(butPtr->bitmap, &width, &height); | |
1036 | #endif | |
1037 | switch (butPtr->anchor) { | |
1038 | case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW: | |
1039 | x = butPtr->borderWidth + butPtr->selectorSpace | |
1040 | + butPtr->padX + 1; | |
1041 | break; | |
1042 | case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S: | |
1043 | x = (Tk_Width(tkwin) + butPtr->selectorSpace - width)/2; | |
1044 | break; | |
1045 | default: | |
1046 | x = Tk_Width(tkwin) - butPtr->borderWidth - butPtr->padX | |
1047 | - width - 1; | |
1048 | break; | |
1049 | } | |
1050 | switch (butPtr->anchor) { | |
1051 | case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE: | |
1052 | y = butPtr->borderWidth + butPtr->padY + 1; | |
1053 | break; | |
1054 | case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E: | |
1055 | y = (Tk_Height(tkwin) - height)/2; | |
1056 | break; | |
1057 | default: | |
1058 | y = Tk_Height(tkwin) - butPtr->borderWidth - butPtr->padY | |
1059 | - height - 1; | |
1060 | break; | |
1061 | } | |
1062 | if (butPtr->relief == TK_RELIEF_RAISED) { | |
1063 | x -= 1; | |
1064 | y -= 1; | |
1065 | } else if (butPtr->relief == TK_RELIEF_SUNKEN) { | |
1066 | x += 1; | |
1067 | y += 1; | |
1068 | } | |
1069 | #if defined(USE_XPM3) | |
1070 | XCopyArea(Tk_Display(tkwin), butPtr->bitmap, pixmap, | |
1071 | gc, 0, 0, width, height, x, y); | |
1072 | #else | |
1073 | XCopyPlane(Tk_Display(tkwin), butPtr->bitmap, pixmap, | |
1074 | gc, 0, 0, width, height, x, y, 1); | |
1075 | #endif | |
1076 | y += height/2; | |
1077 | } else { | |
1078 | switch (butPtr->anchor) { | |
1079 | case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW: | |
1080 | x = butPtr->borderWidth + butPtr->padX + butPtr->selectorSpace | |
1081 | + butPtr->leftBearing + 1; | |
1082 | break; | |
1083 | case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S: | |
1084 | x = (Tk_Width(tkwin) + butPtr->selectorSpace | |
1085 | + butPtr->leftBearing - butPtr->rightBearing)/2; | |
1086 | break; | |
1087 | default: | |
1088 | x = Tk_Width(tkwin) - butPtr->borderWidth - butPtr->padX | |
1089 | - butPtr->rightBearing - 1; | |
1090 | break; | |
1091 | } | |
1092 | switch (butPtr->anchor) { | |
1093 | case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE: | |
1094 | y = butPtr->borderWidth + butPtr->fontPtr->ascent | |
1095 | + butPtr->padY + 1; | |
1096 | break; | |
1097 | case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E: | |
1098 | y = (Tk_Height(tkwin) + butPtr->fontPtr->ascent | |
1099 | - butPtr->fontPtr->descent)/2; | |
1100 | break; | |
1101 | default: | |
1102 | y = Tk_Height(tkwin) - butPtr->borderWidth - butPtr->padY | |
1103 | - butPtr->fontPtr->descent - 1; | |
1104 | break; | |
1105 | } | |
1106 | if (butPtr->relief == TK_RELIEF_RAISED) { | |
1107 | x -= 1; | |
1108 | y -= 1; | |
1109 | } else if (butPtr->relief == TK_RELIEF_SUNKEN) { | |
1110 | x += 1; | |
1111 | y += 1; | |
1112 | } | |
1113 | XDrawString(Tk_Display(tkwin), pixmap, gc, x, y, | |
1114 | butPtr->text, butPtr->textLength); | |
1115 | y -= (butPtr->fontPtr->ascent - butPtr->fontPtr->descent)/2; | |
1116 | x -= butPtr->leftBearing; | |
1117 | } | |
1118 | ||
1119 | /* | |
1120 | * Draw the selector for check buttons and radio buttons. At this | |
1121 | * point x and y refer to the top-left corner of the text or bitmap. | |
1122 | */ | |
1123 | ||
1124 | if ((butPtr->type == TYPE_CHECK_BUTTON) && (butPtr->selectorGC != None)) { | |
1125 | int dim; | |
1126 | ||
1127 | dim = butPtr->selectorDiameter; | |
1128 | x -= (butPtr->selectorSpace + butPtr->padX + dim)/2; | |
1129 | y -= dim/2; | |
1130 | Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap, border, x, y, | |
1131 | dim, dim, butPtr->borderWidth, TK_RELIEF_SUNKEN); | |
1132 | x += butPtr->borderWidth; | |
1133 | y += butPtr->borderWidth; | |
1134 | dim -= 2*butPtr->borderWidth; | |
1135 | if (dim > 0) { | |
1136 | if (butPtr->flags & SELECTED) { | |
1137 | XFillRectangle(Tk_Display(tkwin), pixmap, butPtr->selectorGC, | |
1138 | x, y, (unsigned int) dim, (unsigned int) dim); | |
1139 | } else { | |
1140 | Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, | |
1141 | butPtr->normalBorder, x, y, dim, dim, | |
1142 | butPtr->borderWidth, TK_RELIEF_FLAT); | |
1143 | } | |
1144 | } | |
1145 | } else if ((butPtr->type == TYPE_RADIO_BUTTON) | |
1146 | && (butPtr->selectorGC != None)) { | |
1147 | XPoint points[4]; | |
1148 | int radius; | |
1149 | ||
1150 | radius = butPtr->selectorDiameter/2; | |
1151 | points[0].x = x - (butPtr->selectorSpace + butPtr->padX | |
1152 | + butPtr->selectorDiameter)/2; | |
1153 | points[0].y = y; | |
1154 | points[1].x = points[0].x + radius; | |
1155 | points[1].y = points[0].y + radius; | |
1156 | points[2].x = points[1].x + radius; | |
1157 | points[2].y = points[0].y; | |
1158 | points[3].x = points[1].x; | |
1159 | points[3].y = points[0].y - radius; | |
1160 | if (butPtr->flags & SELECTED) { | |
1161 | XFillPolygon(Tk_Display(tkwin), pixmap, butPtr->selectorGC, | |
1162 | points, 4, Convex, CoordModeOrigin); | |
1163 | } else { | |
1164 | Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, butPtr->normalBorder, | |
1165 | points, 4, butPtr->borderWidth, TK_RELIEF_FLAT); | |
1166 | } | |
1167 | Tk_Draw3DPolygon(Tk_Display(tkwin), pixmap, border, | |
1168 | points, 4, butPtr->borderWidth, TK_RELIEF_RAISED); | |
1169 | } | |
1170 | ||
1171 | /* | |
1172 | * If the button is disabled with a stipple rather than a special | |
1173 | * foreground color, generate the stippled effect. | |
1174 | */ | |
1175 | ||
1176 | if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg == NULL)) { | |
1177 | XFillRectangle(Tk_Display(tkwin), pixmap, butPtr->disabledGC, | |
1178 | butPtr->borderWidth, butPtr->borderWidth, | |
1179 | (unsigned) (Tk_Width(tkwin) - 2*butPtr->borderWidth), | |
1180 | (unsigned) (Tk_Height(tkwin) - 2*butPtr->borderWidth)); | |
1181 | } | |
1182 | ||
1183 | /* | |
1184 | * Draw the border last. This way, if the button's contents | |
1185 | * overflow onto the border they'll be covered up by the border. | |
1186 | */ | |
1187 | ||
1188 | if (butPtr->relief != TK_RELIEF_FLAT) { | |
1189 | Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap, border,0, 0, | |
1190 | Tk_Width(tkwin), Tk_Height(tkwin), butPtr->borderWidth, | |
1191 | butPtr->relief); | |
1192 | } | |
1193 | ||
1194 | /* | |
1195 | * Copy the information from the off-screen pixmap onto the screen, | |
1196 | * then delete the pixmap. | |
1197 | */ | |
1198 | ||
1199 | XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin), | |
1200 | butPtr->normalTextGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0); | |
1201 | XFreePixmap(Tk_Display(tkwin), pixmap); | |
1202 | } | |
1203 | \f | |
1204 | /* | |
1205 | *-------------------------------------------------------------- | |
1206 | * | |
1207 | * ButtonEventProc -- | |
1208 | * | |
1209 | * This procedure is invoked by the Tk dispatcher for various | |
1210 | * events on buttons. | |
1211 | * | |
1212 | * Results: | |
1213 | * None. | |
1214 | * | |
1215 | * Side effects: | |
1216 | * When the window gets deleted, internal structures get | |
1217 | * cleaned up. When it gets exposed, it is redisplayed. | |
1218 | * | |
1219 | *-------------------------------------------------------------- | |
1220 | */ | |
1221 | ||
1222 | static void | |
1223 | ButtonEventProc(clientData, eventPtr) | |
1224 | ClientData clientData; /* Information about window. */ | |
1225 | XEvent *eventPtr; /* Information about event. */ | |
1226 | { | |
1227 | Button *butPtr = (Button *) clientData; | |
1228 | if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { | |
1229 | if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) { | |
1230 | //Tk_TimerToken last = butPtr->updateTimerToken; | |
1231 | butPtr->flags |= REDRAW_PENDING; | |
1232 | // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr); | |
1233 | assert(butPtr->updateTimerToken == NULL); | |
1234 | if (butPtr->updateTimerToken == NULL) { | |
1235 | butPtr->updateTimerToken = | |
1236 | Tk_CreateTimerHandler( | |
1237 | ButtonUpdateTime, | |
1238 | DisplayButton, | |
1239 | (ClientData) butPtr); | |
1240 | } // if | |
1241 | //fprintf(stderr, "ButtonEventProc Expose Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken); | |
1242 | } | |
1243 | } else if (eventPtr->type == DestroyNotify) { | |
1244 | Tcl_DeleteCommand(butPtr->interp, Tk_PathName(butPtr->tkwin)); | |
1245 | butPtr->tkwin = NULL; | |
1246 | if (butPtr->flags & REDRAW_PENDING) { | |
1247 | //fprintf(stderr, "ButtonEventProc Destroy Timer was %d now 0\n", butPtr->updateTimerToken); | |
1248 | // Tk_CancelIdleCall(DisplayButton, (ClientData) butPtr); | |
1249 | butPtr->flags &= ~REDRAW_PENDING; | |
1250 | assert(butPtr->updateTimerToken != NULL); | |
1251 | if (butPtr->updateTimerToken != NULL) { | |
1252 | Tk_DeleteTimerHandler(butPtr->updateTimerToken); | |
1253 | butPtr->updateTimerToken = 0; | |
1254 | } | |
1255 | } | |
1256 | Tk_EventuallyFree((ClientData) butPtr, DestroyButton); | |
1257 | } | |
1258 | } | |
1259 | \f | |
1260 | /* | |
1261 | *---------------------------------------------------------------------- | |
1262 | * | |
1263 | * ComputeButtonGeometry -- | |
1264 | * | |
1265 | * After changes in a button's text or bitmap, this procedure | |
1266 | * recomputes the button's geometry and passes this information | |
1267 | * along to the geometry manager for the window. | |
1268 | * | |
1269 | * Results: | |
1270 | * None. | |
1271 | * | |
1272 | * Side effects: | |
1273 | * The button's window may change size. | |
1274 | * | |
1275 | *---------------------------------------------------------------------- | |
1276 | */ | |
1277 | ||
1278 | static void | |
1279 | ComputeButtonGeometry(butPtr) | |
1280 | register Button *butPtr; /* Button whose geometry may have changed. */ | |
1281 | { | |
1282 | XCharStruct bbox; | |
1283 | int dummy; | |
1284 | unsigned int width, height; | |
1285 | ||
1286 | butPtr->selectorSpace = 0; | |
1287 | if (butPtr->bitmap != None) { | |
1288 | #if defined(USE_XPM3) | |
1289 | Tk_SizeOfPixmap(butPtr->bitmap, &width, &height); | |
1290 | #else | |
1291 | Tk_SizeOfBitmap(butPtr->bitmap, &width, &height); | |
1292 | #endif | |
1293 | if (butPtr->width > 0) { | |
1294 | width = butPtr->width; | |
1295 | } | |
1296 | if (butPtr->height > 0) { | |
1297 | height = butPtr->height; | |
1298 | } | |
1299 | if ((butPtr->type >= TYPE_CHECK_BUTTON) | |
1300 | && (butPtr->selectorGC != None)) { | |
1301 | butPtr->selectorSpace = (14*height)/10; | |
1302 | if (butPtr->type == TYPE_CHECK_BUTTON) { | |
1303 | butPtr->selectorDiameter = (65*height)/100; | |
1304 | } else { | |
1305 | butPtr->selectorDiameter = (75*height)/100; | |
1306 | } | |
1307 | } | |
1308 | } else { | |
1309 | butPtr->textLength = strlen(butPtr->text); | |
1310 | XTextExtents(butPtr->fontPtr, butPtr->text, butPtr->textLength, | |
1311 | &dummy, &dummy, &dummy, &bbox); | |
1312 | butPtr->leftBearing = bbox.lbearing; | |
1313 | butPtr->rightBearing = bbox.rbearing; | |
1314 | width = bbox.lbearing + bbox.rbearing; | |
1315 | height = butPtr->fontPtr->ascent + butPtr->fontPtr->descent; | |
1316 | if (butPtr->width > 0) { | |
1317 | width = butPtr->width * XTextWidth(butPtr->fontPtr, "0", 1); | |
1318 | } | |
1319 | if (butPtr->height > 0) { | |
1320 | height *= butPtr->height; | |
1321 | } | |
1322 | if ((butPtr->type >= TYPE_CHECK_BUTTON) | |
1323 | && (butPtr->selectorGC != None)) { | |
1324 | butPtr->selectorDiameter = butPtr->fontPtr->ascent | |
1325 | + butPtr->fontPtr->descent; | |
1326 | if (butPtr->type == TYPE_CHECK_BUTTON) { | |
1327 | butPtr->selectorDiameter = (80*butPtr->selectorDiameter)/100; | |
1328 | } | |
1329 | butPtr->selectorSpace = butPtr->selectorDiameter + butPtr->padX; | |
1330 | } | |
1331 | } | |
1332 | ||
1333 | /* | |
1334 | * When issuing the geometry request, add extra space for the selector, | |
1335 | * if any, and for the border and padding, plus two extra pixels so the | |
1336 | * display can be offset by 1 pixel in either direction for the raised | |
1337 | * or lowered effect. | |
1338 | */ | |
1339 | ||
1340 | width += 2*butPtr->padX; | |
1341 | height += 2*butPtr->padY; | |
1342 | Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->selectorSpace | |
1343 | + 2*butPtr->borderWidth + 2), | |
1344 | (int) (height + 2*butPtr->borderWidth + 2)); | |
1345 | Tk_SetInternalBorder(butPtr->tkwin, butPtr->borderWidth); | |
1346 | } | |
1347 | \f | |
1348 | /* | |
1349 | *---------------------------------------------------------------------- | |
1350 | * | |
1351 | * InvokeButton -- | |
1352 | * | |
1353 | * This procedure is called to carry out the actions associated | |
1354 | * with a button, such as invoking a Tcl command or setting a | |
1355 | * variable. This procedure is invoked, for example, when the | |
1356 | * button is invoked via the mouse. | |
1357 | * | |
1358 | * Results: | |
1359 | * A standard Tcl return value. Information is also left in | |
1360 | * interp->result. | |
1361 | * | |
1362 | * Side effects: | |
1363 | * Depends on the button and its associated command. | |
1364 | * | |
1365 | *---------------------------------------------------------------------- | |
1366 | */ | |
1367 | ||
1368 | static int | |
1369 | InvokeButton(butPtr) | |
1370 | register Button *butPtr; /* Information about button. */ | |
1371 | { | |
1372 | if (butPtr->type == TYPE_CHECK_BUTTON) { | |
1373 | if (butPtr->flags & SELECTED) { | |
1374 | Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue, | |
1375 | TCL_GLOBAL_ONLY); | |
1376 | } else { | |
1377 | Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue, | |
1378 | TCL_GLOBAL_ONLY); | |
1379 | } | |
1380 | } else if (butPtr->type == TYPE_RADIO_BUTTON) { | |
1381 | Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue, | |
1382 | TCL_GLOBAL_ONLY); | |
1383 | } | |
1384 | if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) { | |
1385 | return Tcl_GlobalEval(butPtr->interp, butPtr->command); | |
1386 | } | |
1387 | return TCL_OK; | |
1388 | } | |
1389 | \f | |
1390 | /* | |
1391 | *-------------------------------------------------------------- | |
1392 | * | |
1393 | * ButtonVarProc -- | |
1394 | * | |
1395 | * This procedure is invoked when someone changes the | |
1396 | * state variable associated with a radio button. Depending | |
1397 | * on the new value of the button's variable, the button | |
1398 | * may be selected or deselected. | |
1399 | * | |
1400 | * Results: | |
1401 | * NULL is always returned. | |
1402 | * | |
1403 | * Side effects: | |
1404 | * The button may become selected or deselected. | |
1405 | * | |
1406 | *-------------------------------------------------------------- | |
1407 | */ | |
1408 | ||
1409 | /* ARGSUSED */ | |
1410 | static char * | |
1411 | ButtonVarProc(clientData, interp, name1, name2, flags) | |
1412 | ClientData clientData; /* Information about button. */ | |
1413 | Tcl_Interp *interp; /* Interpreter containing variable. */ | |
1414 | char *name1; /* Name of variable. */ | |
1415 | char *name2; /* Second part of variable name. */ | |
1416 | int flags; /* Information about what happened. */ | |
1417 | { | |
1418 | register Button *butPtr = (Button *) clientData; | |
1419 | char *value; | |
1420 | ||
1421 | /* | |
1422 | * If the variable is being unset, then just re-establish the | |
1423 | * trace unless the whole interpreter is going away. | |
1424 | */ | |
1425 | ||
1426 | if (flags & TCL_TRACE_UNSETS) { | |
1427 | butPtr->flags &= ~SELECTED; | |
1428 | if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { | |
1429 | Tcl_TraceVar2(interp, name1, name2, | |
1430 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
1431 | ButtonVarProc, clientData); | |
1432 | } | |
1433 | goto redisplay; | |
1434 | } | |
1435 | ||
1436 | /* | |
1437 | * Use the value of the variable to update the selected status of | |
1438 | * the button. | |
1439 | */ | |
1440 | ||
1441 | value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY); | |
1442 | if (strcmp(value, butPtr->onValue) == 0) { | |
1443 | if (butPtr->flags & SELECTED) { | |
1444 | return (char *) NULL; | |
1445 | } | |
1446 | butPtr->flags |= SELECTED; | |
1447 | } else if (butPtr->flags & SELECTED) { | |
1448 | butPtr->flags &= ~SELECTED; | |
1449 | } else { | |
1450 | return (char *) NULL; | |
1451 | } | |
1452 | ||
1453 | redisplay: | |
1454 | if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin) | |
1455 | && !(butPtr->flags & REDRAW_PENDING)) { | |
1456 | //Tk_TimerToken last = butPtr->updateTimerToken; | |
1457 | butPtr->flags |= REDRAW_PENDING; | |
1458 | // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr); | |
1459 | assert(butPtr->updateTimerToken == NULL); | |
1460 | if (butPtr->updateTimerToken == NULL) { | |
1461 | butPtr->updateTimerToken = | |
1462 | Tk_CreateTimerHandler( | |
1463 | ButtonUpdateTime, | |
1464 | DisplayButton, | |
1465 | (ClientData) butPtr); | |
1466 | } | |
1467 | //fprintf(stderr, "ButtonVarProc Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken); | |
1468 | } | |
1469 | return (char *) NULL; | |
1470 | } | |
1471 | \f | |
1472 | /* | |
1473 | *-------------------------------------------------------------- | |
1474 | * | |
1475 | * ButtonTextVarProc -- | |
1476 | * | |
1477 | * This procedure is invoked when someone changes the variable | |
1478 | * whose contents are to be displayed in a button. | |
1479 | * | |
1480 | * Results: | |
1481 | * NULL is always returned. | |
1482 | * | |
1483 | * Side effects: | |
1484 | * The text displayed in the button will change to match the | |
1485 | * variable. | |
1486 | * | |
1487 | *-------------------------------------------------------------- | |
1488 | */ | |
1489 | ||
1490 | /* ARGSUSED */ | |
1491 | static char * | |
1492 | ButtonTextVarProc(clientData, interp, name1, name2, flags) | |
1493 | ClientData clientData; /* Information about button. */ | |
1494 | Tcl_Interp *interp; /* Interpreter containing variable. */ | |
1495 | char *name1; /* Name of variable. */ | |
1496 | char *name2; /* Second part of variable name. */ | |
1497 | int flags; /* Information about what happened. */ | |
1498 | { | |
1499 | register Button *butPtr = (Button *) clientData; | |
1500 | char *value; | |
1501 | ||
1502 | /* | |
1503 | * If the variable is unset, then immediately recreate it unless | |
1504 | * the whole interpreter is going away. | |
1505 | */ | |
1506 | ||
1507 | if (flags & TCL_TRACE_UNSETS) { | |
1508 | if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { | |
1509 | Tcl_SetVar2(interp, name1, name2, butPtr->text, | |
1510 | flags & TCL_GLOBAL_ONLY); | |
1511 | Tcl_TraceVar2(interp, name1, name2, | |
1512 | TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, | |
1513 | ButtonTextVarProc, clientData); | |
1514 | } | |
1515 | return (char *) NULL; | |
1516 | } | |
1517 | ||
1518 | value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY); | |
1519 | if (value == NULL) { | |
1520 | value = ""; | |
1521 | } | |
1522 | if (butPtr->text != NULL) { | |
1523 | ckfree(butPtr->text); | |
1524 | } | |
1525 | butPtr->text = ckalloc((unsigned) (strlen(value) + 1)); | |
1526 | strcpy(butPtr->text, value); | |
1527 | ComputeButtonGeometry(butPtr); | |
1528 | ||
1529 | if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin) | |
1530 | && !(butPtr->flags & REDRAW_PENDING)) { | |
1531 | //Tk_TimerToken last = butPtr->updateTimerToken; | |
1532 | butPtr->flags |= REDRAW_PENDING; | |
1533 | // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr); | |
1534 | assert(butPtr->updateTimerToken == NULL); | |
1535 | if (butPtr->updateTimerToken == NULL) { | |
1536 | butPtr->updateTimerToken = | |
1537 | Tk_CreateTimerHandler( | |
1538 | ButtonUpdateTime, | |
1539 | DisplayButton, | |
1540 | (ClientData) butPtr); | |
1541 | } | |
1542 | //fprintf(stderr, "ButtonTextVarProc Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken); | |
1543 | } | |
1544 | return (char *) NULL; | |
1545 | } |