]> git.zerfleddert.de Git - micropolis/blame - src/tk/tkbutton.c
show mini-map when hovering over the empty mini-map frame
[micropolis] / src / tk / tkbutton.c
CommitLineData
6a5fa4e0
MG
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
20static 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
34typedef 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
156static 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
183static int configFlags[] = {LABEL_MASK, BUTTON_MASK,
184 CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
185/*
186 * Information used for parsing configuration specs:
187 */
188
189static 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
303static 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
310static int ButtonUpdateTime = 200; // Added by Don.
311
312/*
313 * Forward declarations for procedures defined later in this file:
314 */
315
316static void ButtonEventProc _ANSI_ARGS_((ClientData clientData,
317 XEvent *eventPtr));
318static char * ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
319 Tcl_Interp *interp, char *name1, char *name2,
320 int flags));
321static char * ButtonVarProc _ANSI_ARGS_((ClientData clientData,
322 Tcl_Interp *interp, char *name1, char *name2,
323 int flags));
324static int ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
325 Tcl_Interp *interp, int argc, char **argv));
326static void ComputeButtonGeometry _ANSI_ARGS_((Button *butPtr));
327static int ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
328 Button *butPtr, int argc, char **argv,
329 int flags));
330static void DestroyButton _ANSI_ARGS_((ClientData clientData));
331static void DisplayButton _ANSI_ARGS_((ClientData clientData));
332static 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
352int
353Tk_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
467static int
468ButtonWidgetCmd(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
645static void
646DestroyButton(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
751static int
752ConfigureButton(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
978static void
979DisplayButton(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
1222static void
1223ButtonEventProc(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
1278static void
1279ComputeButtonGeometry(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
1368static int
1369InvokeButton(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 */
1410static char *
1411ButtonVarProc(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 */
1491static char *
1492ButtonTextVarProc(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}
Impressum, Datenschutz