]> cvs.zerfleddert.de Git - micropolis/blob - src/tk/tkbutton.c
Fixes for compilation with gcc 15
[micropolis] / src / tk / tkbutton.c
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 (
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 {
361 register Button *butPtr;
362 int type;
363 Tk_Window tkwin = (Tk_Window) clientData;
364 Tk_Window new;
365
366 if (argc < 2) {
367 Tcl_AppendResult(interp, "wrong # args: should be \"",
368 argv[0], " pathName ?options?\"", (char *) NULL);
369 return TCL_ERROR;
370 }
371
372 switch (argv[0][0]) {
373 case 'l':
374 type = TYPE_LABEL;
375 break;
376 case 'b':
377 type = TYPE_BUTTON;
378 break;
379 case 'c':
380 type = TYPE_CHECK_BUTTON;
381 break;
382 case 'r':
383 type = TYPE_RADIO_BUTTON;
384 break;
385 default:
386 sprintf(interp->result,
387 "unknown button-creation command \"%.50s\"");
388 return TCL_ERROR;
389 }
390
391 /*
392 * Create the new window.
393 */
394
395 new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
396 if (new == NULL) {
397 return TCL_ERROR;
398 }
399
400 /*
401 * Initialize the data structure for the button.
402 */
403
404 butPtr = (Button *) ckalloc(sizeof(Button));
405 butPtr->tkwin = new;
406 butPtr->interp = interp;
407 butPtr->type = type;
408 butPtr->text = NULL;
409 butPtr->textVarName = NULL;
410 butPtr->bitmap = None;
411 butPtr->state = tkNormalUid;
412 butPtr->normalBorder = NULL;
413 butPtr->activeBorder = NULL;
414 butPtr->borderWidth = 0;
415 butPtr->relief = TK_RELIEF_FLAT;
416 butPtr->fontPtr = NULL;
417 butPtr->normalFg = NULL;
418 butPtr->activeFg = NULL;
419 butPtr->disabledFg = NULL;
420 butPtr->normalTextGC = None;
421 butPtr->activeTextGC = None;
422 butPtr->gray = None;
423 butPtr->disabledGC = None;
424 butPtr->selectorFg = NULL;
425 butPtr->selectorGC = None;
426 butPtr->selVarName = NULL;
427 butPtr->onValue = NULL;
428 butPtr->offValue = NULL;
429 butPtr->cursor = None;
430 butPtr->command = NULL;
431 butPtr->flags = 0;
432 butPtr->updateTimerToken = 0;
433
434 Tk_SetClass(new, classNames[type]);
435 //fprintf(stderr, "ButtonWidgetCmd Made %s %s\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin));
436 Tk_CreateEventHandler(butPtr->tkwin, ExposureMask|StructureNotifyMask,
437 ButtonEventProc, (ClientData) butPtr);
438 Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin), ButtonWidgetCmd,
439 (ClientData) butPtr, (void (*)(int *)) NULL);
440 if (ConfigureButton(interp, butPtr, argc-2, argv+2,
441 configFlags[type]) != TCL_OK) {
442 Tk_DestroyWindow(butPtr->tkwin);
443 return TCL_ERROR;
444 }
445
446 interp->result = Tk_PathName(butPtr->tkwin);
447 return TCL_OK;
448 }
449 \f
450 /*
451 *--------------------------------------------------------------
452 *
453 * ButtonWidgetCmd --
454 *
455 * This procedure is invoked to process the Tcl command
456 * that corresponds to a widget managed by this module.
457 * See the user documentation for details on what it does.
458 *
459 * Results:
460 * A standard Tcl result.
461 *
462 * Side effects:
463 * See the user documentation.
464 *
465 *--------------------------------------------------------------
466 */
467
468 static int
469 ButtonWidgetCmd (
470 ClientData clientData, /* Information about button widget. */
471 Tcl_Interp *interp, /* Current interpreter. */
472 int argc, /* Number of arguments. */
473 char **argv /* Argument strings. */
474 )
475 {
476 register Button *butPtr = (Button *) clientData;
477 int result = TCL_OK;
478 int length;
479 char c;
480
481 if (argc < 2) {
482 sprintf(interp->result,
483 "wrong # args: should be \"%.50s option [arg arg ...]\"",
484 argv[0]);
485 return TCL_ERROR;
486 }
487 Tk_Preserve((ClientData) butPtr);
488 c = argv[1][0];
489 length = strlen(argv[1]);
490 if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)
491 && (butPtr->type != TYPE_LABEL)) {
492 if (argc > 2) {
493 sprintf(interp->result,
494 "wrong # args: should be \"%.50s activate\"",
495 argv[0]);
496 goto error;
497 }
498 if (butPtr->state != tkDisabledUid) {
499 butPtr->state = tkActiveUid;
500 Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
501 goto redisplay;
502 }
503 } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
504 if (argc == 2) {
505 result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
506 (char *) butPtr, (char *) NULL, configFlags[butPtr->type]);
507 } else if (argc == 3) {
508 result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
509 (char *) butPtr, argv[2],
510 configFlags[butPtr->type]);
511 } else {
512 result = ConfigureButton(interp, butPtr, argc-2, argv+2,
513 configFlags[butPtr->type] | TK_CONFIG_ARGV_ONLY);
514 }
515 } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)
516 && (length > 2) && (butPtr->type != TYPE_LABEL)) {
517 if (argc > 2) {
518 sprintf(interp->result,
519 "wrong # args: should be \"%.50s deactivate\"",
520 argv[0]);
521 goto error;
522 }
523 if (butPtr->state != tkDisabledUid) {
524 butPtr->state = tkNormalUid;
525 Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
526 goto redisplay;
527 }
528 } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
529 && (length > 2) && (butPtr->type >= TYPE_CHECK_BUTTON)) {
530 if (argc > 2) {
531 sprintf(interp->result,
532 "wrong # args: should be \"%.50s deselect\"",
533 argv[0]);
534 goto error;
535 }
536 if (butPtr->type == TYPE_CHECK_BUTTON) {
537 Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
538 TCL_GLOBAL_ONLY);
539 } else if (butPtr->flags & SELECTED) {
540 Tcl_SetVar(interp, butPtr->selVarName, "", TCL_GLOBAL_ONLY);
541 }
542 } else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0)
543 && (butPtr->type != TYPE_LABEL)) {
544 int i;
545
546 if (argc > 2) {
547 sprintf(interp->result,
548 "wrong # args: should be \"%.50s flash\"",
549 argv[0]);
550 goto error;
551 }
552 if (butPtr->state != tkDisabledUid) {
553 for (i = 0; i < 4; i++) {
554 butPtr->state = (butPtr->state == tkNormalUid)
555 ? tkActiveUid : tkNormalUid;
556 Tk_SetBackgroundFromBorder(butPtr->tkwin,
557 (butPtr->state == tkActiveUid) ? butPtr->activeBorder
558 : butPtr->normalBorder);
559 DisplayButton((ClientData) butPtr);
560 XFlush(Tk_Display(butPtr->tkwin));
561 Tk_Sleep(50);
562 }
563 }
564 } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
565 && (butPtr->type > TYPE_LABEL)) {
566 if (argc > 2) {
567 sprintf(interp->result,
568 "wrong # args: should be \"%.50s invoke\"",
569 argv[0]);
570 goto error;
571 }
572 if (butPtr->state != tkDisabledUid) {
573 result = InvokeButton(butPtr);
574 }
575 } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
576 && (butPtr->type >= TYPE_CHECK_BUTTON)) {
577 if (argc > 2) {
578 sprintf(interp->result,
579 "wrong # args: should be \"%.50s select\"",
580 argv[0]);
581 goto error;
582 }
583 Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY);
584 } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
585 && (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
586 if (argc > 2) {
587 sprintf(interp->result,
588 "wrong # args: should be \"%.50s select\"",
589 argv[0]);
590 goto error;
591 }
592 if (butPtr->flags & SELECTED) {
593 Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue, TCL_GLOBAL_ONLY);
594 } else {
595 Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY);
596 }
597 } else {
598 sprintf(interp->result,
599 "bad option \"%.50s\": must be %s", argv[1],
600 optionStrings[butPtr->type]);
601 goto error;
602 }
603 Tk_Release((ClientData) butPtr);
604 return result;
605
606 redisplay:
607 if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
608 //Tk_TimerToken last = butPtr->updateTimerToken;
609 butPtr->flags |= REDRAW_PENDING;
610 // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
611 assert(butPtr->updateTimerToken == 0);
612 if (butPtr->updateTimerToken == 0) {
613 butPtr->updateTimerToken =
614 Tk_CreateTimerHandler(
615 ButtonUpdateTime,
616 DisplayButton,
617 (ClientData) butPtr);
618 }
619 //fprintf(stderr, "ButtonWidgetCmd Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
620 }
621 Tk_Release((ClientData) butPtr);
622 return TCL_OK;
623
624 error:
625 Tk_Release((ClientData) butPtr);
626 return TCL_ERROR;
627 }
628 \f
629 /*
630 *----------------------------------------------------------------------
631 *
632 * DestroyButton --
633 *
634 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
635 * to clean up the internal structure of a button at a safe time
636 * (when no-one is using it anymore).
637 *
638 * Results:
639 * None.
640 *
641 * Side effects:
642 * Everything associated with the widget is freed up.
643 *
644 *----------------------------------------------------------------------
645 */
646
647 static void
648 DestroyButton (
649 ClientData clientData /* Info about entry widget. */
650 )
651 {
652 register Button *butPtr = (Button *) clientData;
653
654 if (butPtr->text != NULL) {
655 ckfree(butPtr->text);
656 }
657 if (butPtr->textVarName != NULL) {
658 Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
659 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
660 ButtonTextVarProc, (ClientData) butPtr);
661 ckfree(butPtr->textVarName);
662 }
663 if (butPtr->bitmap != None) {
664 #if defined(USE_XPM3)
665 Tk_FreePixmap(butPtr->bitmap);
666 #else
667 Tk_FreeBitmap(butPtr->bitmap);
668 #endif
669 }
670 if (butPtr->normalBorder != NULL) {
671 Tk_Free3DBorder(butPtr->normalBorder);
672 }
673 if (butPtr->activeBorder != NULL) {
674 Tk_Free3DBorder(butPtr->activeBorder);
675 }
676 if (butPtr->fontPtr != NULL) {
677 Tk_FreeFontStruct(butPtr->fontPtr);
678 }
679 if (butPtr->normalFg != NULL) {
680 Tk_FreeColor(butPtr->normalFg);
681 }
682 if (butPtr->disabledFg != NULL) {
683 Tk_FreeColor(butPtr->disabledFg);
684 }
685 if (butPtr->activeFg != NULL) {
686 Tk_FreeColor(butPtr->activeFg);
687 }
688 if (butPtr->normalTextGC != None) {
689 Tk_FreeGC(butPtr->normalTextGC);
690 }
691 if (butPtr->activeTextGC != None) {
692 Tk_FreeGC(butPtr->activeTextGC);
693 }
694 if (butPtr->gray != None) {
695 Tk_FreeBitmap(butPtr->gray);
696 }
697 if (butPtr->disabledGC != None) {
698 Tk_FreeGC(butPtr->disabledGC);
699 }
700 if (butPtr->selectorFg != NULL) {
701 Tk_FreeColor(butPtr->selectorFg);
702 }
703 if (butPtr->selectorGC != None) {
704 Tk_FreeGC(butPtr->selectorGC);
705 }
706 if (butPtr->selVarName != NULL) {
707 Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
708 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
709 ButtonVarProc, (ClientData) butPtr);
710 ckfree(butPtr->selVarName);
711 }
712 if (butPtr->onValue != NULL) {
713 ckfree(butPtr->onValue);
714 }
715 if (butPtr->offValue != NULL) {
716 ckfree(butPtr->offValue);
717 }
718 if (butPtr->cursor != None) {
719 Tk_FreeCursor(butPtr->cursor);
720 }
721 if (butPtr->command != NULL) {
722 ckfree(butPtr->command);
723 }
724 if (butPtr->updateTimerToken != NULL) {
725 Tk_DeleteTimerHandler(butPtr->updateTimerToken);
726 //fprintf(stderr, "DestroyButton Delete Timer was %d now 0\n", butPtr->updateTimerToken);
727 butPtr->updateTimerToken = 0;
728
729 }
730 ckfree((char *) butPtr);
731 }
732 \f
733 /*
734 *----------------------------------------------------------------------
735 *
736 * ConfigureButton --
737 *
738 * This procedure is called to process an argv/argc list, plus
739 * the Tk option database, in order to configure (or
740 * reconfigure) a button widget.
741 *
742 * Results:
743 * The return value is a standard Tcl result. If TCL_ERROR is
744 * returned, then interp->result contains an error message.
745 *
746 * Side effects:
747 * Configuration information, such as text string, colors, font,
748 * etc. get set for butPtr; old resources get freed, if there
749 * were any. The button is redisplayed.
750 *
751 *----------------------------------------------------------------------
752 */
753
754 static int
755 ConfigureButton(
756 Tcl_Interp *interp, /* Used for error reporting. */
757 register Button *butPtr, /* Information about widget; may or may
758 * not already have values for some fields. */
759 int argc, /* Number of valid entries in argv. */
760 char **argv, /* Arguments. */
761 int flags /* Flags to pass to Tk_ConfigureWidget. */
762 )
763 {
764 XGCValues gcValues;
765 GC newGC;
766 unsigned long mask;
767
768 /*
769 * Eliminate any existing trace on variables monitored by the button.
770 */
771
772 if (butPtr->textVarName != NULL) {
773 Tcl_UntraceVar(interp, butPtr->textVarName,
774 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
775 ButtonTextVarProc, (ClientData) butPtr);
776 }
777 if (butPtr->selVarName != NULL) {
778 Tcl_UntraceVar(interp, butPtr->selVarName,
779 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
780 ButtonVarProc, (ClientData) butPtr);
781 }
782
783 if (Tk_ConfigureWidget(interp, butPtr->tkwin, configSpecs,
784 argc, argv, (char *) butPtr, flags) != TCL_OK) {
785 return TCL_ERROR;
786 }
787
788 /*
789 * A few options need special processing, such as setting the
790 * background from a 3-D border, or filling in complicated
791 * defaults that couldn't be specified to Tk_ConfigureWidget.
792 */
793
794 if (butPtr->state == tkActiveUid) {
795 Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
796 } else {
797 Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
798 if ((butPtr->state != tkNormalUid)
799 && (butPtr->state != tkDisabledUid)) {
800 Tcl_AppendResult(interp, "bad state value \"", butPtr->state,
801 "\": must be normal, active, or disabled", (char *) NULL);
802 butPtr->state = tkNormalUid;
803 return TCL_ERROR;
804 }
805 }
806
807 gcValues.font = butPtr->fontPtr->fid;
808 gcValues.foreground = butPtr->normalFg->pixel;
809 gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
810
811 /*
812 * Note: GraphicsExpose events are disabled in normalTextGC because it's
813 * used to copy stuff from an off-screen pixmap onto the screen (we know
814 * that there's no problem with obscured areas).
815 */
816
817 gcValues.graphics_exposures = False;
818 newGC = Tk_GetGC(butPtr->tkwin,
819 GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcValues);
820 if (butPtr->normalTextGC != None) {
821 Tk_FreeGC(butPtr->normalTextGC);
822 }
823 butPtr->normalTextGC = newGC;
824
825 if (butPtr->activeFg != NULL) {
826 gcValues.font = butPtr->fontPtr->fid;
827 gcValues.foreground = butPtr->activeFg->pixel;
828 gcValues.background = Tk_3DBorderColor(butPtr->activeBorder)->pixel;
829 newGC = Tk_GetGC(butPtr->tkwin, GCForeground|GCBackground|GCFont,
830 &gcValues);
831 if (butPtr->activeTextGC != None) {
832 Tk_FreeGC(butPtr->activeTextGC);
833 }
834 butPtr->activeTextGC = newGC;
835 }
836
837 gcValues.font = butPtr->fontPtr->fid;
838 gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
839 if (butPtr->disabledFg != NULL) {
840 gcValues.foreground = butPtr->disabledFg->pixel;
841 mask = GCForeground|GCBackground|GCFont;
842 } else {
843 gcValues.foreground = gcValues.background;
844 if (butPtr->gray == None) {
845 butPtr->gray = Tk_GetBitmap(interp, butPtr->tkwin,
846 Tk_GetUid("gray50"));
847 if (butPtr->gray == None) {
848 return TCL_ERROR;
849 }
850 }
851 gcValues.fill_style = FillStippled;
852 gcValues.stipple = butPtr->gray;
853 mask = GCForeground|GCFillStyle|GCStipple;
854 }
855 newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
856 if (butPtr->disabledGC != None) {
857 Tk_FreeGC(butPtr->disabledGC);
858 }
859 butPtr->disabledGC = newGC;
860
861 if (butPtr->padX < 0) {
862 butPtr->padX = 0;
863 }
864 if (butPtr->padY < 0) {
865 butPtr->padY = 0;
866 }
867
868 if (butPtr->type >= TYPE_CHECK_BUTTON) {
869 char *value;
870
871 if (butPtr->selectorFg != NULL) {
872 gcValues.foreground = butPtr->selectorFg->pixel;
873 newGC = Tk_GetGC(butPtr->tkwin, GCForeground, &gcValues);
874 } else {
875 newGC = None;
876 }
877 if (butPtr->selectorGC != None) {
878 Tk_FreeGC(butPtr->selectorGC);
879 }
880 butPtr->selectorGC = newGC;
881
882 if (butPtr->selVarName == NULL) {
883 butPtr->selVarName = (char *) ckalloc((unsigned)
884 (strlen(Tk_Name(butPtr->tkwin)) + 1));
885 strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin));
886 }
887 if (butPtr->onValue == NULL) {
888 butPtr->onValue = (char *) ckalloc((unsigned)
889 (strlen(Tk_Name(butPtr->tkwin)) + 1));
890 strcpy(butPtr->onValue, Tk_Name(butPtr->tkwin));
891 }
892
893 /*
894 * Select the button if the associated variable has the
895 * appropriate value, initialize the variable if it doesn't
896 * exist, then set a trace on the variable to monitor future
897 * changes to its value.
898 */
899
900 value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
901 butPtr->flags &= ~SELECTED;
902 if (value != NULL) {
903 if (strcmp(value, butPtr->onValue) == 0) {
904 butPtr->flags |= SELECTED;
905 }
906 } else {
907 Tcl_SetVar(interp, butPtr->selVarName,
908 (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
909 TCL_GLOBAL_ONLY);
910 }
911 Tcl_TraceVar(interp, butPtr->selVarName,
912 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
913 ButtonVarProc, (ClientData) butPtr);
914 }
915
916 /*
917 * If the button is to display the value of a variable, then set up
918 * a trace on the variable's value, create the variable if it doesn't
919 * exist, and fetch its current value.
920 */
921
922 if ((butPtr->bitmap == None) && (butPtr->textVarName != NULL)) {
923 char *value;
924
925 value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
926 if (value == NULL) {
927 Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
928 TCL_GLOBAL_ONLY);
929 } else {
930 if (butPtr->text != NULL) {
931 ckfree(butPtr->text);
932 }
933 butPtr->text = ckalloc((unsigned) (strlen(value) + 1));
934 strcpy(butPtr->text, value);
935 }
936 Tcl_TraceVar(interp, butPtr->textVarName,
937 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
938 ButtonTextVarProc, (ClientData) butPtr);
939 }
940
941 ComputeButtonGeometry(butPtr);
942
943 /*
944 * Lastly, arrange for the button to be redisplayed.
945 */
946
947 if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
948 //Tk_TimerToken last = butPtr->updateTimerToken;
949 butPtr->flags |= REDRAW_PENDING;
950 // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
951 assert(butPtr->updateTimerToken == 0);
952 if (butPtr->updateTimerToken == 0) {
953 butPtr->updateTimerToken =
954 Tk_CreateTimerHandler(
955 ButtonUpdateTime,
956 DisplayButton,
957 (ClientData) butPtr);
958 }
959 //fprintf(stderr, "ConfigureButton Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
960 }
961
962 return TCL_OK;
963 }
964 \f
965 /*
966 *----------------------------------------------------------------------
967 *
968 * DisplayButton --
969 *
970 * This procedure is invoked to display a button widget.
971 *
972 * Results:
973 * None.
974 *
975 * Side effects:
976 * Commands are output to X to display the button in its
977 * current mode.
978 *
979 *----------------------------------------------------------------------
980 */
981
982 static void
983 DisplayButton (
984 ClientData clientData /* Information about widget. */
985 )
986 {
987 register Button *butPtr = (Button *) clientData;
988 GC gc;
989 Tk_3DBorder border;
990 Pixmap pixmap;
991 int x = 0; /* Initialization only needed to stop
992 * compiler warning. */
993 int y;
994 register Tk_Window tkwin = butPtr->tkwin;
995
996 //fprintf(stderr, "DisplayButton Handled Timer %s %s was %d now 0\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), butPtr->updateTimerToken);
997
998 assert(butPtr->updateTimerToken != 0);
999 butPtr->updateTimerToken = 0;
1000
1001 butPtr->flags &= ~REDRAW_PENDING;
1002 if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
1003 return;
1004 }
1005
1006 if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) {
1007 gc = butPtr->disabledGC;
1008 border = butPtr->normalBorder;
1009 } else if (butPtr->state == tkActiveUid) {
1010 gc = butPtr->activeTextGC;
1011 border = butPtr->activeBorder;
1012 } else {
1013 gc = butPtr->normalTextGC;
1014 border = butPtr->normalBorder;
1015 }
1016
1017 /*
1018 * In order to avoid screen flashes, this procedure redraws
1019 * the button in a pixmap, then copies the pixmap to the
1020 * screen in a single operation. This means that there's no
1021 * point in time where the on-sreen image has been cleared.
1022 */
1023
1024 pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
1025 Tk_Width(tkwin), Tk_Height(tkwin),
1026 Tk_DefaultDepth(Tk_Screen(tkwin)));
1027 Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border,
1028 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
1029
1030 /*
1031 * Display bitmap or text for button.
1032 */
1033
1034 if (butPtr->bitmap != None) {
1035 unsigned int width, height;
1036
1037 #if defined(USE_XPM3)
1038 Tk_SizeOfPixmap(butPtr->bitmap, &width, &height);
1039 #else
1040 Tk_SizeOfBitmap(butPtr->bitmap, &width, &height);
1041 #endif
1042 switch (butPtr->anchor) {
1043 case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
1044 x = butPtr->borderWidth + butPtr->selectorSpace
1045 + butPtr->padX + 1;
1046 break;
1047 case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
1048 x = (Tk_Width(tkwin) + butPtr->selectorSpace - width)/2;
1049 break;
1050 default:
1051 x = Tk_Width(tkwin) - butPtr->borderWidth - butPtr->padX
1052 - width - 1;
1053 break;
1054 }
1055 switch (butPtr->anchor) {
1056 case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
1057 y = butPtr->borderWidth + butPtr->padY + 1;
1058 break;
1059 case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
1060 y = (Tk_Height(tkwin) - height)/2;
1061 break;
1062 default:
1063 y = Tk_Height(tkwin) - butPtr->borderWidth - butPtr->padY
1064 - height - 1;
1065 break;
1066 }
1067 if (butPtr->relief == TK_RELIEF_RAISED) {
1068 x -= 1;
1069 y -= 1;
1070 } else if (butPtr->relief == TK_RELIEF_SUNKEN) {
1071 x += 1;
1072 y += 1;
1073 }
1074 #if defined(USE_XPM3)
1075 XCopyArea(Tk_Display(tkwin), butPtr->bitmap, pixmap,
1076 gc, 0, 0, width, height, x, y);
1077 #else
1078 XCopyPlane(Tk_Display(tkwin), butPtr->bitmap, pixmap,
1079 gc, 0, 0, width, height, x, y, 1);
1080 #endif
1081 y += height/2;
1082 } else {
1083 switch (butPtr->anchor) {
1084 case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
1085 x = butPtr->borderWidth + butPtr->padX + butPtr->selectorSpace
1086 + butPtr->leftBearing + 1;
1087 break;
1088 case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
1089 x = (Tk_Width(tkwin) + butPtr->selectorSpace
1090 + butPtr->leftBearing - butPtr->rightBearing)/2;
1091 break;
1092 default:
1093 x = Tk_Width(tkwin) - butPtr->borderWidth - butPtr->padX
1094 - butPtr->rightBearing - 1;
1095 break;
1096 }
1097 switch (butPtr->anchor) {
1098 case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
1099 y = butPtr->borderWidth + butPtr->fontPtr->ascent
1100 + butPtr->padY + 1;
1101 break;
1102 case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
1103 y = (Tk_Height(tkwin) + butPtr->fontPtr->ascent
1104 - butPtr->fontPtr->descent)/2;
1105 break;
1106 default:
1107 y = Tk_Height(tkwin) - butPtr->borderWidth - butPtr->padY
1108 - butPtr->fontPtr->descent - 1;
1109 break;
1110 }
1111 if (butPtr->relief == TK_RELIEF_RAISED) {
1112 x -= 1;
1113 y -= 1;
1114 } else if (butPtr->relief == TK_RELIEF_SUNKEN) {
1115 x += 1;
1116 y += 1;
1117 }
1118 XDrawString(Tk_Display(tkwin), pixmap, gc, x, y,
1119 butPtr->text, butPtr->textLength);
1120 y -= (butPtr->fontPtr->ascent - butPtr->fontPtr->descent)/2;
1121 x -= butPtr->leftBearing;
1122 }
1123
1124 /*
1125 * Draw the selector for check buttons and radio buttons. At this
1126 * point x and y refer to the top-left corner of the text or bitmap.
1127 */
1128
1129 if ((butPtr->type == TYPE_CHECK_BUTTON) && (butPtr->selectorGC != None)) {
1130 int dim;
1131
1132 dim = butPtr->selectorDiameter;
1133 x -= (butPtr->selectorSpace + butPtr->padX + dim)/2;
1134 y -= dim/2;
1135 Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap, border, x, y,
1136 dim, dim, butPtr->borderWidth, TK_RELIEF_SUNKEN);
1137 x += butPtr->borderWidth;
1138 y += butPtr->borderWidth;
1139 dim -= 2*butPtr->borderWidth;
1140 if (dim > 0) {
1141 if (butPtr->flags & SELECTED) {
1142 XFillRectangle(Tk_Display(tkwin), pixmap, butPtr->selectorGC,
1143 x, y, (unsigned int) dim, (unsigned int) dim);
1144 } else {
1145 Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap,
1146 butPtr->normalBorder, x, y, dim, dim,
1147 butPtr->borderWidth, TK_RELIEF_FLAT);
1148 }
1149 }
1150 } else if ((butPtr->type == TYPE_RADIO_BUTTON)
1151 && (butPtr->selectorGC != None)) {
1152 XPoint points[4];
1153 int radius;
1154
1155 radius = butPtr->selectorDiameter/2;
1156 points[0].x = x - (butPtr->selectorSpace + butPtr->padX
1157 + butPtr->selectorDiameter)/2;
1158 points[0].y = y;
1159 points[1].x = points[0].x + radius;
1160 points[1].y = points[0].y + radius;
1161 points[2].x = points[1].x + radius;
1162 points[2].y = points[0].y;
1163 points[3].x = points[1].x;
1164 points[3].y = points[0].y - radius;
1165 if (butPtr->flags & SELECTED) {
1166 XFillPolygon(Tk_Display(tkwin), pixmap, butPtr->selectorGC,
1167 points, 4, Convex, CoordModeOrigin);
1168 } else {
1169 Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, butPtr->normalBorder,
1170 points, 4, butPtr->borderWidth, TK_RELIEF_FLAT);
1171 }
1172 Tk_Draw3DPolygon(Tk_Display(tkwin), pixmap, border,
1173 points, 4, butPtr->borderWidth, TK_RELIEF_RAISED);
1174 }
1175
1176 /*
1177 * If the button is disabled with a stipple rather than a special
1178 * foreground color, generate the stippled effect.
1179 */
1180
1181 if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg == NULL)) {
1182 XFillRectangle(Tk_Display(tkwin), pixmap, butPtr->disabledGC,
1183 butPtr->borderWidth, butPtr->borderWidth,
1184 (unsigned) (Tk_Width(tkwin) - 2*butPtr->borderWidth),
1185 (unsigned) (Tk_Height(tkwin) - 2*butPtr->borderWidth));
1186 }
1187
1188 /*
1189 * Draw the border last. This way, if the button's contents
1190 * overflow onto the border they'll be covered up by the border.
1191 */
1192
1193 if (butPtr->relief != TK_RELIEF_FLAT) {
1194 Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap, border,0, 0,
1195 Tk_Width(tkwin), Tk_Height(tkwin), butPtr->borderWidth,
1196 butPtr->relief);
1197 }
1198
1199 /*
1200 * Copy the information from the off-screen pixmap onto the screen,
1201 * then delete the pixmap.
1202 */
1203
1204 XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
1205 butPtr->normalTextGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
1206 XFreePixmap(Tk_Display(tkwin), pixmap);
1207 }
1208 \f
1209 /*
1210 *--------------------------------------------------------------
1211 *
1212 * ButtonEventProc --
1213 *
1214 * This procedure is invoked by the Tk dispatcher for various
1215 * events on buttons.
1216 *
1217 * Results:
1218 * None.
1219 *
1220 * Side effects:
1221 * When the window gets deleted, internal structures get
1222 * cleaned up. When it gets exposed, it is redisplayed.
1223 *
1224 *--------------------------------------------------------------
1225 */
1226
1227 static void
1228 ButtonEventProc (
1229 ClientData clientData, /* Information about window. */
1230 XEvent *eventPtr /* Information about event. */
1231 )
1232 {
1233 Button *butPtr = (Button *) clientData;
1234 if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
1235 if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
1236 //Tk_TimerToken last = butPtr->updateTimerToken;
1237 butPtr->flags |= REDRAW_PENDING;
1238 // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
1239 assert(butPtr->updateTimerToken == NULL);
1240 if (butPtr->updateTimerToken == NULL) {
1241 butPtr->updateTimerToken =
1242 Tk_CreateTimerHandler(
1243 ButtonUpdateTime,
1244 DisplayButton,
1245 (ClientData) butPtr);
1246 } // if
1247 //fprintf(stderr, "ButtonEventProc Expose Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
1248 }
1249 } else if (eventPtr->type == DestroyNotify) {
1250 Tcl_DeleteCommand(butPtr->interp, Tk_PathName(butPtr->tkwin));
1251 butPtr->tkwin = NULL;
1252 if (butPtr->flags & REDRAW_PENDING) {
1253 //fprintf(stderr, "ButtonEventProc Destroy Timer was %d now 0\n", butPtr->updateTimerToken);
1254 // Tk_CancelIdleCall(DisplayButton, (ClientData) butPtr);
1255 butPtr->flags &= ~REDRAW_PENDING;
1256 assert(butPtr->updateTimerToken != NULL);
1257 if (butPtr->updateTimerToken != NULL) {
1258 Tk_DeleteTimerHandler(butPtr->updateTimerToken);
1259 butPtr->updateTimerToken = 0;
1260 }
1261 }
1262 Tk_EventuallyFree((ClientData) butPtr, DestroyButton);
1263 }
1264 }
1265 \f
1266 /*
1267 *----------------------------------------------------------------------
1268 *
1269 * ComputeButtonGeometry --
1270 *
1271 * After changes in a button's text or bitmap, this procedure
1272 * recomputes the button's geometry and passes this information
1273 * along to the geometry manager for the window.
1274 *
1275 * Results:
1276 * None.
1277 *
1278 * Side effects:
1279 * The button's window may change size.
1280 *
1281 *----------------------------------------------------------------------
1282 */
1283
1284 static void
1285 ComputeButtonGeometry(
1286 register Button *butPtr /* Button whose geometry may have changed. */
1287 )
1288 {
1289 XCharStruct bbox;
1290 int dummy;
1291 unsigned int width, height;
1292
1293 butPtr->selectorSpace = 0;
1294 if (butPtr->bitmap != None) {
1295 #if defined(USE_XPM3)
1296 Tk_SizeOfPixmap(butPtr->bitmap, &width, &height);
1297 #else
1298 Tk_SizeOfBitmap(butPtr->bitmap, &width, &height);
1299 #endif
1300 if (butPtr->width > 0) {
1301 width = butPtr->width;
1302 }
1303 if (butPtr->height > 0) {
1304 height = butPtr->height;
1305 }
1306 if ((butPtr->type >= TYPE_CHECK_BUTTON)
1307 && (butPtr->selectorGC != None)) {
1308 butPtr->selectorSpace = (14*height)/10;
1309 if (butPtr->type == TYPE_CHECK_BUTTON) {
1310 butPtr->selectorDiameter = (65*height)/100;
1311 } else {
1312 butPtr->selectorDiameter = (75*height)/100;
1313 }
1314 }
1315 } else {
1316 butPtr->textLength = strlen(butPtr->text);
1317 XTextExtents(butPtr->fontPtr, butPtr->text, butPtr->textLength,
1318 &dummy, &dummy, &dummy, &bbox);
1319 butPtr->leftBearing = bbox.lbearing;
1320 butPtr->rightBearing = bbox.rbearing;
1321 width = bbox.lbearing + bbox.rbearing;
1322 height = butPtr->fontPtr->ascent + butPtr->fontPtr->descent;
1323 if (butPtr->width > 0) {
1324 width = butPtr->width * XTextWidth(butPtr->fontPtr, "0", 1);
1325 }
1326 if (butPtr->height > 0) {
1327 height *= butPtr->height;
1328 }
1329 if ((butPtr->type >= TYPE_CHECK_BUTTON)
1330 && (butPtr->selectorGC != None)) {
1331 butPtr->selectorDiameter = butPtr->fontPtr->ascent
1332 + butPtr->fontPtr->descent;
1333 if (butPtr->type == TYPE_CHECK_BUTTON) {
1334 butPtr->selectorDiameter = (80*butPtr->selectorDiameter)/100;
1335 }
1336 butPtr->selectorSpace = butPtr->selectorDiameter + butPtr->padX;
1337 }
1338 }
1339
1340 /*
1341 * When issuing the geometry request, add extra space for the selector,
1342 * if any, and for the border and padding, plus two extra pixels so the
1343 * display can be offset by 1 pixel in either direction for the raised
1344 * or lowered effect.
1345 */
1346
1347 width += 2*butPtr->padX;
1348 height += 2*butPtr->padY;
1349 Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->selectorSpace
1350 + 2*butPtr->borderWidth + 2),
1351 (int) (height + 2*butPtr->borderWidth + 2));
1352 Tk_SetInternalBorder(butPtr->tkwin, butPtr->borderWidth);
1353 }
1354 \f
1355 /*
1356 *----------------------------------------------------------------------
1357 *
1358 * InvokeButton --
1359 *
1360 * This procedure is called to carry out the actions associated
1361 * with a button, such as invoking a Tcl command or setting a
1362 * variable. This procedure is invoked, for example, when the
1363 * button is invoked via the mouse.
1364 *
1365 * Results:
1366 * A standard Tcl return value. Information is also left in
1367 * interp->result.
1368 *
1369 * Side effects:
1370 * Depends on the button and its associated command.
1371 *
1372 *----------------------------------------------------------------------
1373 */
1374
1375 static int
1376 InvokeButton(
1377 register Button *butPtr /* Information about button. */
1378 )
1379 {
1380 if (butPtr->type == TYPE_CHECK_BUTTON) {
1381 if (butPtr->flags & SELECTED) {
1382 Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue,
1383 TCL_GLOBAL_ONLY);
1384 } else {
1385 Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
1386 TCL_GLOBAL_ONLY);
1387 }
1388 } else if (butPtr->type == TYPE_RADIO_BUTTON) {
1389 Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
1390 TCL_GLOBAL_ONLY);
1391 }
1392 if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
1393 return Tcl_GlobalEval(butPtr->interp, butPtr->command);
1394 }
1395 return TCL_OK;
1396 }
1397 \f
1398 /*
1399 *--------------------------------------------------------------
1400 *
1401 * ButtonVarProc --
1402 *
1403 * This procedure is invoked when someone changes the
1404 * state variable associated with a radio button. Depending
1405 * on the new value of the button's variable, the button
1406 * may be selected or deselected.
1407 *
1408 * Results:
1409 * NULL is always returned.
1410 *
1411 * Side effects:
1412 * The button may become selected or deselected.
1413 *
1414 *--------------------------------------------------------------
1415 */
1416
1417 /* ARGSUSED */
1418 static char *
1419 ButtonVarProc (
1420 ClientData clientData, /* Information about button. */
1421 Tcl_Interp *interp, /* Interpreter containing variable. */
1422 char *name1, /* Name of variable. */
1423 char *name2, /* Second part of variable name. */
1424 int flags /* Information about what happened. */
1425 )
1426 {
1427 register Button *butPtr = (Button *) clientData;
1428 char *value;
1429
1430 /*
1431 * If the variable is being unset, then just re-establish the
1432 * trace unless the whole interpreter is going away.
1433 */
1434
1435 if (flags & TCL_TRACE_UNSETS) {
1436 butPtr->flags &= ~SELECTED;
1437 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1438 Tcl_TraceVar2(interp, name1, name2,
1439 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1440 ButtonVarProc, clientData);
1441 }
1442 goto redisplay;
1443 }
1444
1445 /*
1446 * Use the value of the variable to update the selected status of
1447 * the button.
1448 */
1449
1450 value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
1451 if (strcmp(value, butPtr->onValue) == 0) {
1452 if (butPtr->flags & SELECTED) {
1453 return (char *) NULL;
1454 }
1455 butPtr->flags |= SELECTED;
1456 } else if (butPtr->flags & SELECTED) {
1457 butPtr->flags &= ~SELECTED;
1458 } else {
1459 return (char *) NULL;
1460 }
1461
1462 redisplay:
1463 if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
1464 && !(butPtr->flags & REDRAW_PENDING)) {
1465 //Tk_TimerToken last = butPtr->updateTimerToken;
1466 butPtr->flags |= REDRAW_PENDING;
1467 // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
1468 assert(butPtr->updateTimerToken == NULL);
1469 if (butPtr->updateTimerToken == NULL) {
1470 butPtr->updateTimerToken =
1471 Tk_CreateTimerHandler(
1472 ButtonUpdateTime,
1473 DisplayButton,
1474 (ClientData) butPtr);
1475 }
1476 //fprintf(stderr, "ButtonVarProc Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
1477 }
1478 return (char *) NULL;
1479 }
1480 \f
1481 /*
1482 *--------------------------------------------------------------
1483 *
1484 * ButtonTextVarProc --
1485 *
1486 * This procedure is invoked when someone changes the variable
1487 * whose contents are to be displayed in a button.
1488 *
1489 * Results:
1490 * NULL is always returned.
1491 *
1492 * Side effects:
1493 * The text displayed in the button will change to match the
1494 * variable.
1495 *
1496 *--------------------------------------------------------------
1497 */
1498
1499 /* ARGSUSED */
1500 static char *
1501 ButtonTextVarProc (
1502 ClientData clientData, /* Information about button. */
1503 Tcl_Interp *interp, /* Interpreter containing variable. */
1504 char *name1, /* Name of variable. */
1505 char *name2, /* Second part of variable name. */
1506 int flags /* Information about what happened. */
1507 )
1508 {
1509 register Button *butPtr = (Button *) clientData;
1510 char *value;
1511
1512 /*
1513 * If the variable is unset, then immediately recreate it unless
1514 * the whole interpreter is going away.
1515 */
1516
1517 if (flags & TCL_TRACE_UNSETS) {
1518 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1519 Tcl_SetVar2(interp, name1, name2, butPtr->text,
1520 flags & TCL_GLOBAL_ONLY);
1521 Tcl_TraceVar2(interp, name1, name2,
1522 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1523 ButtonTextVarProc, clientData);
1524 }
1525 return (char *) NULL;
1526 }
1527
1528 value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
1529 if (value == NULL) {
1530 value = "";
1531 }
1532 if (butPtr->text != NULL) {
1533 ckfree(butPtr->text);
1534 }
1535 butPtr->text = ckalloc((unsigned) (strlen(value) + 1));
1536 strcpy(butPtr->text, value);
1537 ComputeButtonGeometry(butPtr);
1538
1539 if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
1540 && !(butPtr->flags & REDRAW_PENDING)) {
1541 //Tk_TimerToken last = butPtr->updateTimerToken;
1542 butPtr->flags |= REDRAW_PENDING;
1543 // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
1544 assert(butPtr->updateTimerToken == NULL);
1545 if (butPtr->updateTimerToken == NULL) {
1546 butPtr->updateTimerToken =
1547 Tk_CreateTimerHandler(
1548 ButtonUpdateTime,
1549 DisplayButton,
1550 (ClientData) butPtr);
1551 }
1552 //fprintf(stderr, "ButtonTextVarProc Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
1553 }
1554 return (char *) NULL;
1555 }
Impressum, Datenschutz