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
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.
20 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkButton.c,v 1.69 92/08/21 11:42:47 ouster Exp $ SPRITE (Berkeley)";
30 * A data structure of the following type is kept for each
31 * widget managed by this file:
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. */
43 * Information about what's in the button.
46 char *text
; /* Text to display in button (malloc'ed)
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. */
56 * Information used when displaying widget:
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
65 Tk_3DBorder activeBorder
; /* Structure used to draw 3-D
66 * border and background when window
67 * is active. NULL means no such
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
78 GC normalTextGC
; /* GC for drawing text in normal mode. Also
79 * used to copy from off-screen pixmap onto
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
91 int leftBearing
; /* Amount text sticks left from its origin,
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
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. */
111 * For check and radio buttons, the fields below are used
112 * to manage the variable indicating the button's state.
115 char *selVarName
; /* Name of variable used to control selected
116 * state of button. Malloc'ed (if
118 char *onValue
; /* Value to store in variable when
119 * this button is selected. Malloc'ed (if
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
127 * Miscellaneous information:
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
136 Tk_TimerToken updateTimerToken
; /* Added by Don to optimize rapid
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
148 #define TYPE_BUTTON 1
149 #define TYPE_CHECK_BUTTON 2
150 #define TYPE_RADIO_BUTTON 3
153 * Class names for buttons, indexed by one of the type values above.
156 static char *classNames
[] = {"Label", "Button", "CheckButton", "RadioButton"};
159 * Flag bits for buttons:
161 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
162 * has already been queued to redraw
164 * SELECTED: Non-zero means this button is selected,
165 * so special highlight should be drawn.
168 #define REDRAW_PENDING 1
172 * Mask values used to selectively enable entries in the
173 * configuration specs:
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)
183 static int configFlags
[] = {LABEL_MASK
, BUTTON_MASK
,
184 CHECK_BUTTON_MASK
, RADIO_BUTTON_MASK
};
186 * Information used for parsing configuration specs:
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
},
223 {TK_CONFIG_BITMAP
, "-bitmap", "bitmap", "Bitmap",
224 DEF_BUTTON_BITMAP
, Tk_Offset(Button
, bitmap
),
225 ALL_MASK
|TK_CONFIG_NULL_OK
},
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
),
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
),
255 {TK_CONFIG_STRING
, "-onvalue", "onValue", "Value",
256 DEF_BUTTON_ON_VALUE
, Tk_Offset(Button
, onValue
),
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
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
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
),
286 {TK_CONFIG_STRING
, "-variable", "variable", "Variable",
287 DEF_RADIOBUTTON_VARIABLE
, Tk_Offset(Button
, selVarName
),
289 {TK_CONFIG_STRING
, "-variable", "variable", "Variable",
290 DEF_CHECKBUTTON_VARIABLE
, Tk_Offset(Button
, selVarName
),
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
,
299 * String to print out in error messages, identifying options for
300 * widget commands for different types of labels or buttons:
303 static char *optionStrings
[] = {
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"
310 static int ButtonUpdateTime
= 200; // Added by Don.
313 * Forward declarations for procedures defined later in this file:
316 static void ButtonEventProc
_ANSI_ARGS_((ClientData clientData
,
318 static char * ButtonTextVarProc
_ANSI_ARGS_((ClientData clientData
,
319 Tcl_Interp
*interp
, char *name1
, char *name2
,
321 static char * ButtonVarProc
_ANSI_ARGS_((ClientData clientData
,
322 Tcl_Interp
*interp
, char *name1
, char *name2
,
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
,
330 static void DestroyButton
_ANSI_ARGS_((ClientData clientData
));
331 static void DisplayButton
_ANSI_ARGS_((ClientData clientData
));
332 static int InvokeButton
_ANSI_ARGS_((Button
*butPtr
));
335 *--------------------------------------------------------------
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.
344 * A standard Tcl result.
347 * See the user documentation.
349 *--------------------------------------------------------------
354 ClientData clientData
, /* Main window associated with
356 Tcl_Interp
*interp
, /* Current interpreter. */
357 int argc
, /* Number of arguments. */
358 char **argv
/* Argument strings. */
361 register Button
*butPtr
;
363 Tk_Window tkwin
= (Tk_Window
) clientData
;
367 Tcl_AppendResult(interp
, "wrong # args: should be \"",
368 argv
[0], " pathName ?options?\"", (char *) NULL
);
372 switch (argv
[0][0]) {
380 type
= TYPE_CHECK_BUTTON
;
383 type
= TYPE_RADIO_BUTTON
;
386 sprintf(interp
->result
,
387 "unknown button-creation command \"%.50s\"");
392 * Create the new window.
395 new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], (char *) NULL
);
401 * Initialize the data structure for the button.
404 butPtr
= (Button
*) ckalloc(sizeof(Button
));
406 butPtr
->interp
= interp
;
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
;
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
;
432 butPtr
->updateTimerToken
= 0;
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
);
446 interp
->result
= Tk_PathName(butPtr
->tkwin
);
451 *--------------------------------------------------------------
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.
460 * A standard Tcl result.
463 * See the user documentation.
465 *--------------------------------------------------------------
470 ClientData clientData
, /* Information about button widget. */
471 Tcl_Interp
*interp
, /* Current interpreter. */
472 int argc
, /* Number of arguments. */
473 char **argv
/* Argument strings. */
476 register Button
*butPtr
= (Button
*) clientData
;
482 sprintf(interp
->result
,
483 "wrong # args: should be \"%.50s option [arg arg ...]\"",
487 Tk_Preserve((ClientData
) butPtr
);
489 length
= strlen(argv
[1]);
490 if ((c
== 'a') && (strncmp(argv
[1], "activate", length
) == 0)
491 && (butPtr
->type
!= TYPE_LABEL
)) {
493 sprintf(interp
->result
,
494 "wrong # args: should be \"%.50s activate\"",
498 if (butPtr
->state
!= tkDisabledUid
) {
499 butPtr
->state
= tkActiveUid
;
500 Tk_SetBackgroundFromBorder(butPtr
->tkwin
, butPtr
->activeBorder
);
503 } else if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)) {
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
]);
512 result
= ConfigureButton(interp
, butPtr
, argc
-2, argv
+2,
513 configFlags
[butPtr
->type
] | TK_CONFIG_ARGV_ONLY
);
515 } else if ((c
== 'd') && (strncmp(argv
[1], "deactivate", length
) == 0)
516 && (length
> 2) && (butPtr
->type
!= TYPE_LABEL
)) {
518 sprintf(interp
->result
,
519 "wrong # args: should be \"%.50s deactivate\"",
523 if (butPtr
->state
!= tkDisabledUid
) {
524 butPtr
->state
= tkNormalUid
;
525 Tk_SetBackgroundFromBorder(butPtr
->tkwin
, butPtr
->normalBorder
);
528 } else if ((c
== 'd') && (strncmp(argv
[1], "deselect", length
) == 0)
529 && (length
> 2) && (butPtr
->type
>= TYPE_CHECK_BUTTON
)) {
531 sprintf(interp
->result
,
532 "wrong # args: should be \"%.50s deselect\"",
536 if (butPtr
->type
== TYPE_CHECK_BUTTON
) {
537 Tcl_SetVar(interp
, butPtr
->selVarName
, butPtr
->offValue
,
539 } else if (butPtr
->flags
& SELECTED
) {
540 Tcl_SetVar(interp
, butPtr
->selVarName
, "", TCL_GLOBAL_ONLY
);
542 } else if ((c
== 'f') && (strncmp(argv
[1], "flash", length
) == 0)
543 && (butPtr
->type
!= TYPE_LABEL
)) {
547 sprintf(interp
->result
,
548 "wrong # args: should be \"%.50s flash\"",
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
));
564 } else if ((c
== 'i') && (strncmp(argv
[1], "invoke", length
) == 0)
565 && (butPtr
->type
> TYPE_LABEL
)) {
567 sprintf(interp
->result
,
568 "wrong # args: should be \"%.50s invoke\"",
572 if (butPtr
->state
!= tkDisabledUid
) {
573 result
= InvokeButton(butPtr
);
575 } else if ((c
== 's') && (strncmp(argv
[1], "select", length
) == 0)
576 && (butPtr
->type
>= TYPE_CHECK_BUTTON
)) {
578 sprintf(interp
->result
,
579 "wrong # args: should be \"%.50s select\"",
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
)) {
587 sprintf(interp
->result
,
588 "wrong # args: should be \"%.50s select\"",
592 if (butPtr
->flags
& SELECTED
) {
593 Tcl_SetVar(interp
, butPtr
->selVarName
, butPtr
->offValue
, TCL_GLOBAL_ONLY
);
595 Tcl_SetVar(interp
, butPtr
->selVarName
, butPtr
->onValue
, TCL_GLOBAL_ONLY
);
598 sprintf(interp
->result
,
599 "bad option \"%.50s\": must be %s", argv
[1],
600 optionStrings
[butPtr
->type
]);
603 Tk_Release((ClientData
) butPtr
);
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(
617 (ClientData
) butPtr
);
619 //fprintf(stderr, "ButtonWidgetCmd Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
621 Tk_Release((ClientData
) butPtr
);
625 Tk_Release((ClientData
) butPtr
);
630 *----------------------------------------------------------------------
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).
642 * Everything associated with the widget is freed up.
644 *----------------------------------------------------------------------
649 ClientData clientData
/* Info about entry widget. */
652 register Button
*butPtr
= (Button
*) clientData
;
654 if (butPtr
->text
!= NULL
) {
655 ckfree(butPtr
->text
);
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
);
663 if (butPtr
->bitmap
!= None
) {
664 #if defined(USE_XPM3)
665 Tk_FreePixmap(butPtr
->bitmap
);
667 Tk_FreeBitmap(butPtr
->bitmap
);
670 if (butPtr
->normalBorder
!= NULL
) {
671 Tk_Free3DBorder(butPtr
->normalBorder
);
673 if (butPtr
->activeBorder
!= NULL
) {
674 Tk_Free3DBorder(butPtr
->activeBorder
);
676 if (butPtr
->fontPtr
!= NULL
) {
677 Tk_FreeFontStruct(butPtr
->fontPtr
);
679 if (butPtr
->normalFg
!= NULL
) {
680 Tk_FreeColor(butPtr
->normalFg
);
682 if (butPtr
->disabledFg
!= NULL
) {
683 Tk_FreeColor(butPtr
->disabledFg
);
685 if (butPtr
->activeFg
!= NULL
) {
686 Tk_FreeColor(butPtr
->activeFg
);
688 if (butPtr
->normalTextGC
!= None
) {
689 Tk_FreeGC(butPtr
->normalTextGC
);
691 if (butPtr
->activeTextGC
!= None
) {
692 Tk_FreeGC(butPtr
->activeTextGC
);
694 if (butPtr
->gray
!= None
) {
695 Tk_FreeBitmap(butPtr
->gray
);
697 if (butPtr
->disabledGC
!= None
) {
698 Tk_FreeGC(butPtr
->disabledGC
);
700 if (butPtr
->selectorFg
!= NULL
) {
701 Tk_FreeColor(butPtr
->selectorFg
);
703 if (butPtr
->selectorGC
!= None
) {
704 Tk_FreeGC(butPtr
->selectorGC
);
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
);
712 if (butPtr
->onValue
!= NULL
) {
713 ckfree(butPtr
->onValue
);
715 if (butPtr
->offValue
!= NULL
) {
716 ckfree(butPtr
->offValue
);
718 if (butPtr
->cursor
!= None
) {
719 Tk_FreeCursor(butPtr
->cursor
);
721 if (butPtr
->command
!= NULL
) {
722 ckfree(butPtr
->command
);
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;
730 ckfree((char *) butPtr
);
734 *----------------------------------------------------------------------
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.
743 * The return value is a standard Tcl result. If TCL_ERROR is
744 * returned, then interp->result contains an error message.
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.
751 *----------------------------------------------------------------------
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. */
769 * Eliminate any existing trace on variables monitored by the button.
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
);
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
);
783 if (Tk_ConfigureWidget(interp
, butPtr
->tkwin
, configSpecs
,
784 argc
, argv
, (char *) butPtr
, flags
) != TCL_OK
) {
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.
794 if (butPtr
->state
== tkActiveUid
) {
795 Tk_SetBackgroundFromBorder(butPtr
->tkwin
, butPtr
->activeBorder
);
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
;
807 gcValues
.font
= butPtr
->fontPtr
->fid
;
808 gcValues
.foreground
= butPtr
->normalFg
->pixel
;
809 gcValues
.background
= Tk_3DBorderColor(butPtr
->normalBorder
)->pixel
;
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).
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
);
823 butPtr
->normalTextGC
= newGC
;
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
,
831 if (butPtr
->activeTextGC
!= None
) {
832 Tk_FreeGC(butPtr
->activeTextGC
);
834 butPtr
->activeTextGC
= newGC
;
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
;
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
) {
851 gcValues
.fill_style
= FillStippled
;
852 gcValues
.stipple
= butPtr
->gray
;
853 mask
= GCForeground
|GCFillStyle
|GCStipple
;
855 newGC
= Tk_GetGC(butPtr
->tkwin
, mask
, &gcValues
);
856 if (butPtr
->disabledGC
!= None
) {
857 Tk_FreeGC(butPtr
->disabledGC
);
859 butPtr
->disabledGC
= newGC
;
861 if (butPtr
->padX
< 0) {
864 if (butPtr
->padY
< 0) {
868 if (butPtr
->type
>= TYPE_CHECK_BUTTON
) {
871 if (butPtr
->selectorFg
!= NULL
) {
872 gcValues
.foreground
= butPtr
->selectorFg
->pixel
;
873 newGC
= Tk_GetGC(butPtr
->tkwin
, GCForeground
, &gcValues
);
877 if (butPtr
->selectorGC
!= None
) {
878 Tk_FreeGC(butPtr
->selectorGC
);
880 butPtr
->selectorGC
= newGC
;
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
));
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
));
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.
900 value
= Tcl_GetVar(interp
, butPtr
->selVarName
, TCL_GLOBAL_ONLY
);
901 butPtr
->flags
&= ~SELECTED
;
903 if (strcmp(value
, butPtr
->onValue
) == 0) {
904 butPtr
->flags
|= SELECTED
;
907 Tcl_SetVar(interp
, butPtr
->selVarName
,
908 (butPtr
->type
== TYPE_CHECK_BUTTON
) ? butPtr
->offValue
: "",
911 Tcl_TraceVar(interp
, butPtr
->selVarName
,
912 TCL_GLOBAL_ONLY
|TCL_TRACE_WRITES
|TCL_TRACE_UNSETS
,
913 ButtonVarProc
, (ClientData
) butPtr
);
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.
922 if ((butPtr
->bitmap
== None
) && (butPtr
->textVarName
!= NULL
)) {
925 value
= Tcl_GetVar(interp
, butPtr
->textVarName
, TCL_GLOBAL_ONLY
);
927 Tcl_SetVar(interp
, butPtr
->textVarName
, butPtr
->text
,
930 if (butPtr
->text
!= NULL
) {
931 ckfree(butPtr
->text
);
933 butPtr
->text
= ckalloc((unsigned) (strlen(value
) + 1));
934 strcpy(butPtr
->text
, value
);
936 Tcl_TraceVar(interp
, butPtr
->textVarName
,
937 TCL_GLOBAL_ONLY
|TCL_TRACE_WRITES
|TCL_TRACE_UNSETS
,
938 ButtonTextVarProc
, (ClientData
) butPtr
);
941 ComputeButtonGeometry(butPtr
);
944 * Lastly, arrange for the button to be redisplayed.
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(
957 (ClientData
) butPtr
);
959 //fprintf(stderr, "ConfigureButton Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
966 *----------------------------------------------------------------------
970 * This procedure is invoked to display a button widget.
976 * Commands are output to X to display the button in its
979 *----------------------------------------------------------------------
984 ClientData clientData
/* Information about widget. */
987 register Button
*butPtr
= (Button
*) clientData
;
991 int x
= 0; /* Initialization only needed to stop
992 * compiler warning. */
994 register Tk_Window tkwin
= butPtr
->tkwin
;
996 //fprintf(stderr, "DisplayButton Handled Timer %s %s was %d now 0\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), butPtr->updateTimerToken);
998 assert(butPtr
->updateTimerToken
!= 0);
999 butPtr
->updateTimerToken
= 0;
1001 butPtr
->flags
&= ~REDRAW_PENDING
;
1002 if ((butPtr
->tkwin
== NULL
) || !Tk_IsMapped(tkwin
)) {
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
;
1013 gc
= butPtr
->normalTextGC
;
1014 border
= butPtr
->normalBorder
;
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.
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
);
1031 * Display bitmap or text for button.
1034 if (butPtr
->bitmap
!= None
) {
1035 unsigned int width
, height
;
1037 #if defined(USE_XPM3)
1038 Tk_SizeOfPixmap(butPtr
->bitmap
, &width
, &height
);
1040 Tk_SizeOfBitmap(butPtr
->bitmap
, &width
, &height
);
1042 switch (butPtr
->anchor
) {
1043 case TK_ANCHOR_NW
: case TK_ANCHOR_W
: case TK_ANCHOR_SW
:
1044 x
= butPtr
->borderWidth
+ butPtr
->selectorSpace
1047 case TK_ANCHOR_N
: case TK_ANCHOR_CENTER
: case TK_ANCHOR_S
:
1048 x
= (Tk_Width(tkwin
) + butPtr
->selectorSpace
- width
)/2;
1051 x
= Tk_Width(tkwin
) - butPtr
->borderWidth
- butPtr
->padX
1055 switch (butPtr
->anchor
) {
1056 case TK_ANCHOR_NW
: case TK_ANCHOR_N
: case TK_ANCHOR_NE
:
1057 y
= butPtr
->borderWidth
+ butPtr
->padY
+ 1;
1059 case TK_ANCHOR_W
: case TK_ANCHOR_CENTER
: case TK_ANCHOR_E
:
1060 y
= (Tk_Height(tkwin
) - height
)/2;
1063 y
= Tk_Height(tkwin
) - butPtr
->borderWidth
- butPtr
->padY
1067 if (butPtr
->relief
== TK_RELIEF_RAISED
) {
1070 } else if (butPtr
->relief
== TK_RELIEF_SUNKEN
) {
1074 #if defined(USE_XPM3)
1075 XCopyArea(Tk_Display(tkwin
), butPtr
->bitmap
, pixmap
,
1076 gc
, 0, 0, width
, height
, x
, y
);
1078 XCopyPlane(Tk_Display(tkwin
), butPtr
->bitmap
, pixmap
,
1079 gc
, 0, 0, width
, height
, x
, y
, 1);
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;
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;
1093 x
= Tk_Width(tkwin
) - butPtr
->borderWidth
- butPtr
->padX
1094 - butPtr
->rightBearing
- 1;
1097 switch (butPtr
->anchor
) {
1098 case TK_ANCHOR_NW
: case TK_ANCHOR_N
: case TK_ANCHOR_NE
:
1099 y
= butPtr
->borderWidth
+ butPtr
->fontPtr
->ascent
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;
1107 y
= Tk_Height(tkwin
) - butPtr
->borderWidth
- butPtr
->padY
1108 - butPtr
->fontPtr
->descent
- 1;
1111 if (butPtr
->relief
== TK_RELIEF_RAISED
) {
1114 } else if (butPtr
->relief
== TK_RELIEF_SUNKEN
) {
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
;
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.
1129 if ((butPtr
->type
== TYPE_CHECK_BUTTON
) && (butPtr
->selectorGC
!= None
)) {
1132 dim
= butPtr
->selectorDiameter
;
1133 x
-= (butPtr
->selectorSpace
+ butPtr
->padX
+ 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
;
1141 if (butPtr
->flags
& SELECTED
) {
1142 XFillRectangle(Tk_Display(tkwin
), pixmap
, butPtr
->selectorGC
,
1143 x
, y
, (unsigned int) dim
, (unsigned int) dim
);
1145 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
,
1146 butPtr
->normalBorder
, x
, y
, dim
, dim
,
1147 butPtr
->borderWidth
, TK_RELIEF_FLAT
);
1150 } else if ((butPtr
->type
== TYPE_RADIO_BUTTON
)
1151 && (butPtr
->selectorGC
!= None
)) {
1155 radius
= butPtr
->selectorDiameter
/2;
1156 points
[0].x
= x
- (butPtr
->selectorSpace
+ butPtr
->padX
1157 + butPtr
->selectorDiameter
)/2;
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
);
1169 Tk_Fill3DPolygon(Tk_Display(tkwin
), pixmap
, butPtr
->normalBorder
,
1170 points
, 4, butPtr
->borderWidth
, TK_RELIEF_FLAT
);
1172 Tk_Draw3DPolygon(Tk_Display(tkwin
), pixmap
, border
,
1173 points
, 4, butPtr
->borderWidth
, TK_RELIEF_RAISED
);
1177 * If the button is disabled with a stipple rather than a special
1178 * foreground color, generate the stippled effect.
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
));
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.
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
,
1200 * Copy the information from the off-screen pixmap onto the screen,
1201 * then delete the pixmap.
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
);
1210 *--------------------------------------------------------------
1212 * ButtonEventProc --
1214 * This procedure is invoked by the Tk dispatcher for various
1215 * events on buttons.
1221 * When the window gets deleted, internal structures get
1222 * cleaned up. When it gets exposed, it is redisplayed.
1224 *--------------------------------------------------------------
1229 ClientData clientData
, /* Information about window. */
1230 XEvent
*eventPtr
/* Information about event. */
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(
1245 (ClientData
) butPtr
);
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);
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;
1262 Tk_EventuallyFree((ClientData
) butPtr
, DestroyButton
);
1267 *----------------------------------------------------------------------
1269 * ComputeButtonGeometry --
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.
1279 * The button's window may change size.
1281 *----------------------------------------------------------------------
1285 ComputeButtonGeometry(
1286 register Button
*butPtr
/* Button whose geometry may have changed. */
1291 unsigned int width
, height
;
1293 butPtr
->selectorSpace
= 0;
1294 if (butPtr
->bitmap
!= None
) {
1295 #if defined(USE_XPM3)
1296 Tk_SizeOfPixmap(butPtr
->bitmap
, &width
, &height
);
1298 Tk_SizeOfBitmap(butPtr
->bitmap
, &width
, &height
);
1300 if (butPtr
->width
> 0) {
1301 width
= butPtr
->width
;
1303 if (butPtr
->height
> 0) {
1304 height
= butPtr
->height
;
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;
1312 butPtr
->selectorDiameter
= (75*height
)/100;
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);
1326 if (butPtr
->height
> 0) {
1327 height
*= butPtr
->height
;
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;
1336 butPtr
->selectorSpace
= butPtr
->selectorDiameter
+ butPtr
->padX
;
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.
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
);
1356 *----------------------------------------------------------------------
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.
1366 * A standard Tcl return value. Information is also left in
1370 * Depends on the button and its associated command.
1372 *----------------------------------------------------------------------
1377 register Button
*butPtr
/* Information about button. */
1380 if (butPtr
->type
== TYPE_CHECK_BUTTON
) {
1381 if (butPtr
->flags
& SELECTED
) {
1382 Tcl_SetVar(butPtr
->interp
, butPtr
->selVarName
, butPtr
->offValue
,
1385 Tcl_SetVar(butPtr
->interp
, butPtr
->selVarName
, butPtr
->onValue
,
1388 } else if (butPtr
->type
== TYPE_RADIO_BUTTON
) {
1389 Tcl_SetVar(butPtr
->interp
, butPtr
->selVarName
, butPtr
->onValue
,
1392 if ((butPtr
->type
!= TYPE_LABEL
) && (butPtr
->command
!= NULL
)) {
1393 return Tcl_GlobalEval(butPtr
->interp
, butPtr
->command
);
1399 *--------------------------------------------------------------
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.
1409 * NULL is always returned.
1412 * The button may become selected or deselected.
1414 *--------------------------------------------------------------
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. */
1427 register Button
*butPtr
= (Button
*) clientData
;
1431 * If the variable is being unset, then just re-establish the
1432 * trace unless the whole interpreter is going away.
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
);
1446 * Use the value of the variable to update the selected status of
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
;
1455 butPtr
->flags
|= SELECTED
;
1456 } else if (butPtr
->flags
& SELECTED
) {
1457 butPtr
->flags
&= ~SELECTED
;
1459 return (char *) NULL
;
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(
1474 (ClientData
) butPtr
);
1476 //fprintf(stderr, "ButtonVarProc Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
1478 return (char *) NULL
;
1482 *--------------------------------------------------------------
1484 * ButtonTextVarProc --
1486 * This procedure is invoked when someone changes the variable
1487 * whose contents are to be displayed in a button.
1490 * NULL is always returned.
1493 * The text displayed in the button will change to match the
1496 *--------------------------------------------------------------
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. */
1509 register Button
*butPtr
= (Button
*) clientData
;
1513 * If the variable is unset, then immediately recreate it unless
1514 * the whole interpreter is going away.
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
);
1525 return (char *) NULL
;
1528 value
= Tcl_GetVar2(interp
, name1
, name2
, flags
& TCL_GLOBAL_ONLY
);
1529 if (value
== NULL
) {
1532 if (butPtr
->text
!= NULL
) {
1533 ckfree(butPtr
->text
);
1535 butPtr
->text
= ckalloc((unsigned) (strlen(value
) + 1));
1536 strcpy(butPtr
->text
, value
);
1537 ComputeButtonGeometry(butPtr
);
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(
1550 (ClientData
) butPtr
);
1552 //fprintf(stderr, "ButtonTextVarProc Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
1554 return (char *) NULL
;