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 *--------------------------------------------------------------
353 Tk_ButtonCmd(clientData
, interp
, argc
, argv
)
354 ClientData clientData
; /* Main window associated with
356 Tcl_Interp
*interp
; /* Current interpreter. */
357 int argc
; /* Number of arguments. */
358 char **argv
; /* Argument strings. */
360 register Button
*butPtr
;
362 Tk_Window tkwin
= (Tk_Window
) clientData
;
366 Tcl_AppendResult(interp
, "wrong # args: should be \"",
367 argv
[0], " pathName ?options?\"", (char *) NULL
);
371 switch (argv
[0][0]) {
379 type
= TYPE_CHECK_BUTTON
;
382 type
= TYPE_RADIO_BUTTON
;
385 sprintf(interp
->result
,
386 "unknown button-creation command \"%.50s\"");
391 * Create the new window.
394 new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], (char *) NULL
);
400 * Initialize the data structure for the button.
403 butPtr
= (Button
*) ckalloc(sizeof(Button
));
405 butPtr
->interp
= interp
;
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
;
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
;
431 butPtr
->updateTimerToken
= 0;
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
);
445 interp
->result
= Tk_PathName(butPtr
->tkwin
);
450 *--------------------------------------------------------------
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.
459 * A standard Tcl result.
462 * See the user documentation.
464 *--------------------------------------------------------------
468 ButtonWidgetCmd(clientData
, interp
, argc
, argv
)
469 ClientData clientData
; /* Information about button widget. */
470 Tcl_Interp
*interp
; /* Current interpreter. */
471 int argc
; /* Number of arguments. */
472 char **argv
; /* Argument strings. */
474 register Button
*butPtr
= (Button
*) clientData
;
480 sprintf(interp
->result
,
481 "wrong # args: should be \"%.50s option [arg arg ...]\"",
485 Tk_Preserve((ClientData
) butPtr
);
487 length
= strlen(argv
[1]);
488 if ((c
== 'a') && (strncmp(argv
[1], "activate", length
) == 0)
489 && (butPtr
->type
!= TYPE_LABEL
)) {
491 sprintf(interp
->result
,
492 "wrong # args: should be \"%.50s activate\"",
496 if (butPtr
->state
!= tkDisabledUid
) {
497 butPtr
->state
= tkActiveUid
;
498 Tk_SetBackgroundFromBorder(butPtr
->tkwin
, butPtr
->activeBorder
);
501 } else if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)) {
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
]);
510 result
= ConfigureButton(interp
, butPtr
, argc
-2, argv
+2,
511 configFlags
[butPtr
->type
] | TK_CONFIG_ARGV_ONLY
);
513 } else if ((c
== 'd') && (strncmp(argv
[1], "deactivate", length
) == 0)
514 && (length
> 2) && (butPtr
->type
!= TYPE_LABEL
)) {
516 sprintf(interp
->result
,
517 "wrong # args: should be \"%.50s deactivate\"",
521 if (butPtr
->state
!= tkDisabledUid
) {
522 butPtr
->state
= tkNormalUid
;
523 Tk_SetBackgroundFromBorder(butPtr
->tkwin
, butPtr
->normalBorder
);
526 } else if ((c
== 'd') && (strncmp(argv
[1], "deselect", length
) == 0)
527 && (length
> 2) && (butPtr
->type
>= TYPE_CHECK_BUTTON
)) {
529 sprintf(interp
->result
,
530 "wrong # args: should be \"%.50s deselect\"",
534 if (butPtr
->type
== TYPE_CHECK_BUTTON
) {
535 Tcl_SetVar(interp
, butPtr
->selVarName
, butPtr
->offValue
,
537 } else if (butPtr
->flags
& SELECTED
) {
538 Tcl_SetVar(interp
, butPtr
->selVarName
, "", TCL_GLOBAL_ONLY
);
540 } else if ((c
== 'f') && (strncmp(argv
[1], "flash", length
) == 0)
541 && (butPtr
->type
!= TYPE_LABEL
)) {
545 sprintf(interp
->result
,
546 "wrong # args: should be \"%.50s flash\"",
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
));
562 } else if ((c
== 'i') && (strncmp(argv
[1], "invoke", length
) == 0)
563 && (butPtr
->type
> TYPE_LABEL
)) {
565 sprintf(interp
->result
,
566 "wrong # args: should be \"%.50s invoke\"",
570 if (butPtr
->state
!= tkDisabledUid
) {
571 result
= InvokeButton(butPtr
);
573 } else if ((c
== 's') && (strncmp(argv
[1], "select", length
) == 0)
574 && (butPtr
->type
>= TYPE_CHECK_BUTTON
)) {
576 sprintf(interp
->result
,
577 "wrong # args: should be \"%.50s select\"",
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
)) {
585 sprintf(interp
->result
,
586 "wrong # args: should be \"%.50s select\"",
590 if (butPtr
->flags
& SELECTED
) {
591 Tcl_SetVar(interp
, butPtr
->selVarName
, butPtr
->offValue
, TCL_GLOBAL_ONLY
);
593 Tcl_SetVar(interp
, butPtr
->selVarName
, butPtr
->onValue
, TCL_GLOBAL_ONLY
);
596 sprintf(interp
->result
,
597 "bad option \"%.50s\": must be %s", argv
[1],
598 optionStrings
[butPtr
->type
]);
601 Tk_Release((ClientData
) butPtr
);
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(
615 (ClientData
) butPtr
);
617 //fprintf(stderr, "ButtonWidgetCmd Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
619 Tk_Release((ClientData
) butPtr
);
623 Tk_Release((ClientData
) butPtr
);
628 *----------------------------------------------------------------------
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).
640 * Everything associated with the widget is freed up.
642 *----------------------------------------------------------------------
646 DestroyButton(clientData
)
647 ClientData clientData
; /* Info about entry widget. */
649 register Button
*butPtr
= (Button
*) clientData
;
651 if (butPtr
->text
!= NULL
) {
652 ckfree(butPtr
->text
);
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
);
660 if (butPtr
->bitmap
!= None
) {
661 #if defined(USE_XPM3)
662 Tk_FreePixmap(butPtr
->bitmap
);
664 Tk_FreeBitmap(butPtr
->bitmap
);
667 if (butPtr
->normalBorder
!= NULL
) {
668 Tk_Free3DBorder(butPtr
->normalBorder
);
670 if (butPtr
->activeBorder
!= NULL
) {
671 Tk_Free3DBorder(butPtr
->activeBorder
);
673 if (butPtr
->fontPtr
!= NULL
) {
674 Tk_FreeFontStruct(butPtr
->fontPtr
);
676 if (butPtr
->normalFg
!= NULL
) {
677 Tk_FreeColor(butPtr
->normalFg
);
679 if (butPtr
->disabledFg
!= NULL
) {
680 Tk_FreeColor(butPtr
->disabledFg
);
682 if (butPtr
->activeFg
!= NULL
) {
683 Tk_FreeColor(butPtr
->activeFg
);
685 if (butPtr
->normalTextGC
!= None
) {
686 Tk_FreeGC(butPtr
->normalTextGC
);
688 if (butPtr
->activeTextGC
!= None
) {
689 Tk_FreeGC(butPtr
->activeTextGC
);
691 if (butPtr
->gray
!= None
) {
692 Tk_FreeBitmap(butPtr
->gray
);
694 if (butPtr
->disabledGC
!= None
) {
695 Tk_FreeGC(butPtr
->disabledGC
);
697 if (butPtr
->selectorFg
!= NULL
) {
698 Tk_FreeColor(butPtr
->selectorFg
);
700 if (butPtr
->selectorGC
!= None
) {
701 Tk_FreeGC(butPtr
->selectorGC
);
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
);
709 if (butPtr
->onValue
!= NULL
) {
710 ckfree(butPtr
->onValue
);
712 if (butPtr
->offValue
!= NULL
) {
713 ckfree(butPtr
->offValue
);
715 if (butPtr
->cursor
!= None
) {
716 Tk_FreeCursor(butPtr
->cursor
);
718 if (butPtr
->command
!= NULL
) {
719 ckfree(butPtr
->command
);
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;
727 ckfree((char *) butPtr
);
731 *----------------------------------------------------------------------
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.
740 * The return value is a standard Tcl result. If TCL_ERROR is
741 * returned, then interp->result contains an error message.
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.
748 *----------------------------------------------------------------------
752 ConfigureButton(interp
, butPtr
, argc
, argv
, flags
)
753 Tcl_Interp
*interp
; /* Used for error reporting. */
754 register Button
*butPtr
; /* Information about widget; may or may
755 * not already have values for some fields. */
756 int argc
; /* Number of valid entries in argv. */
757 char **argv
; /* Arguments. */
758 int flags
; /* Flags to pass to Tk_ConfigureWidget. */
765 * Eliminate any existing trace on variables monitored by the button.
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
);
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
);
779 if (Tk_ConfigureWidget(interp
, butPtr
->tkwin
, configSpecs
,
780 argc
, argv
, (char *) butPtr
, flags
) != TCL_OK
) {
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.
790 if (butPtr
->state
== tkActiveUid
) {
791 Tk_SetBackgroundFromBorder(butPtr
->tkwin
, butPtr
->activeBorder
);
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
;
803 gcValues
.font
= butPtr
->fontPtr
->fid
;
804 gcValues
.foreground
= butPtr
->normalFg
->pixel
;
805 gcValues
.background
= Tk_3DBorderColor(butPtr
->normalBorder
)->pixel
;
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).
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
);
819 butPtr
->normalTextGC
= newGC
;
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
,
827 if (butPtr
->activeTextGC
!= None
) {
828 Tk_FreeGC(butPtr
->activeTextGC
);
830 butPtr
->activeTextGC
= newGC
;
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
;
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
) {
847 gcValues
.fill_style
= FillStippled
;
848 gcValues
.stipple
= butPtr
->gray
;
849 mask
= GCForeground
|GCFillStyle
|GCStipple
;
851 newGC
= Tk_GetGC(butPtr
->tkwin
, mask
, &gcValues
);
852 if (butPtr
->disabledGC
!= None
) {
853 Tk_FreeGC(butPtr
->disabledGC
);
855 butPtr
->disabledGC
= newGC
;
857 if (butPtr
->padX
< 0) {
860 if (butPtr
->padY
< 0) {
864 if (butPtr
->type
>= TYPE_CHECK_BUTTON
) {
867 if (butPtr
->selectorFg
!= NULL
) {
868 gcValues
.foreground
= butPtr
->selectorFg
->pixel
;
869 newGC
= Tk_GetGC(butPtr
->tkwin
, GCForeground
, &gcValues
);
873 if (butPtr
->selectorGC
!= None
) {
874 Tk_FreeGC(butPtr
->selectorGC
);
876 butPtr
->selectorGC
= newGC
;
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
));
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
));
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.
896 value
= Tcl_GetVar(interp
, butPtr
->selVarName
, TCL_GLOBAL_ONLY
);
897 butPtr
->flags
&= ~SELECTED
;
899 if (strcmp(value
, butPtr
->onValue
) == 0) {
900 butPtr
->flags
|= SELECTED
;
903 Tcl_SetVar(interp
, butPtr
->selVarName
,
904 (butPtr
->type
== TYPE_CHECK_BUTTON
) ? butPtr
->offValue
: "",
907 Tcl_TraceVar(interp
, butPtr
->selVarName
,
908 TCL_GLOBAL_ONLY
|TCL_TRACE_WRITES
|TCL_TRACE_UNSETS
,
909 ButtonVarProc
, (ClientData
) butPtr
);
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.
918 if ((butPtr
->bitmap
== None
) && (butPtr
->textVarName
!= NULL
)) {
921 value
= Tcl_GetVar(interp
, butPtr
->textVarName
, TCL_GLOBAL_ONLY
);
923 Tcl_SetVar(interp
, butPtr
->textVarName
, butPtr
->text
,
926 if (butPtr
->text
!= NULL
) {
927 ckfree(butPtr
->text
);
929 butPtr
->text
= ckalloc((unsigned) (strlen(value
) + 1));
930 strcpy(butPtr
->text
, value
);
932 Tcl_TraceVar(interp
, butPtr
->textVarName
,
933 TCL_GLOBAL_ONLY
|TCL_TRACE_WRITES
|TCL_TRACE_UNSETS
,
934 ButtonTextVarProc
, (ClientData
) butPtr
);
937 ComputeButtonGeometry(butPtr
);
940 * Lastly, arrange for the button to be redisplayed.
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(
953 (ClientData
) butPtr
);
955 //fprintf(stderr, "ConfigureButton Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
962 *----------------------------------------------------------------------
966 * This procedure is invoked to display a button widget.
972 * Commands are output to X to display the button in its
975 *----------------------------------------------------------------------
979 DisplayButton(clientData
)
980 ClientData clientData
; /* Information about widget. */
982 register Button
*butPtr
= (Button
*) clientData
;
986 int x
= 0; /* Initialization only needed to stop
987 * compiler warning. */
989 register Tk_Window tkwin
= butPtr
->tkwin
;
991 //fprintf(stderr, "DisplayButton Handled Timer %s %s was %d now 0\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), butPtr->updateTimerToken);
993 assert(butPtr
->updateTimerToken
!= 0);
994 butPtr
->updateTimerToken
= 0;
996 butPtr
->flags
&= ~REDRAW_PENDING
;
997 if ((butPtr
->tkwin
== NULL
) || !Tk_IsMapped(tkwin
)) {
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
;
1008 gc
= butPtr
->normalTextGC
;
1009 border
= butPtr
->normalBorder
;
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.
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
);
1026 * Display bitmap or text for button.
1029 if (butPtr
->bitmap
!= None
) {
1030 unsigned int width
, height
;
1032 #if defined(USE_XPM3)
1033 Tk_SizeOfPixmap(butPtr
->bitmap
, &width
, &height
);
1035 Tk_SizeOfBitmap(butPtr
->bitmap
, &width
, &height
);
1037 switch (butPtr
->anchor
) {
1038 case TK_ANCHOR_NW
: case TK_ANCHOR_W
: case TK_ANCHOR_SW
:
1039 x
= butPtr
->borderWidth
+ butPtr
->selectorSpace
1042 case TK_ANCHOR_N
: case TK_ANCHOR_CENTER
: case TK_ANCHOR_S
:
1043 x
= (Tk_Width(tkwin
) + butPtr
->selectorSpace
- width
)/2;
1046 x
= Tk_Width(tkwin
) - butPtr
->borderWidth
- butPtr
->padX
1050 switch (butPtr
->anchor
) {
1051 case TK_ANCHOR_NW
: case TK_ANCHOR_N
: case TK_ANCHOR_NE
:
1052 y
= butPtr
->borderWidth
+ butPtr
->padY
+ 1;
1054 case TK_ANCHOR_W
: case TK_ANCHOR_CENTER
: case TK_ANCHOR_E
:
1055 y
= (Tk_Height(tkwin
) - height
)/2;
1058 y
= Tk_Height(tkwin
) - butPtr
->borderWidth
- butPtr
->padY
1062 if (butPtr
->relief
== TK_RELIEF_RAISED
) {
1065 } else if (butPtr
->relief
== TK_RELIEF_SUNKEN
) {
1069 #if defined(USE_XPM3)
1070 XCopyArea(Tk_Display(tkwin
), butPtr
->bitmap
, pixmap
,
1071 gc
, 0, 0, width
, height
, x
, y
);
1073 XCopyPlane(Tk_Display(tkwin
), butPtr
->bitmap
, pixmap
,
1074 gc
, 0, 0, width
, height
, x
, y
, 1);
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;
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;
1088 x
= Tk_Width(tkwin
) - butPtr
->borderWidth
- butPtr
->padX
1089 - butPtr
->rightBearing
- 1;
1092 switch (butPtr
->anchor
) {
1093 case TK_ANCHOR_NW
: case TK_ANCHOR_N
: case TK_ANCHOR_NE
:
1094 y
= butPtr
->borderWidth
+ butPtr
->fontPtr
->ascent
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;
1102 y
= Tk_Height(tkwin
) - butPtr
->borderWidth
- butPtr
->padY
1103 - butPtr
->fontPtr
->descent
- 1;
1106 if (butPtr
->relief
== TK_RELIEF_RAISED
) {
1109 } else if (butPtr
->relief
== TK_RELIEF_SUNKEN
) {
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
;
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.
1124 if ((butPtr
->type
== TYPE_CHECK_BUTTON
) && (butPtr
->selectorGC
!= None
)) {
1127 dim
= butPtr
->selectorDiameter
;
1128 x
-= (butPtr
->selectorSpace
+ butPtr
->padX
+ 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
;
1136 if (butPtr
->flags
& SELECTED
) {
1137 XFillRectangle(Tk_Display(tkwin
), pixmap
, butPtr
->selectorGC
,
1138 x
, y
, (unsigned int) dim
, (unsigned int) dim
);
1140 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
,
1141 butPtr
->normalBorder
, x
, y
, dim
, dim
,
1142 butPtr
->borderWidth
, TK_RELIEF_FLAT
);
1145 } else if ((butPtr
->type
== TYPE_RADIO_BUTTON
)
1146 && (butPtr
->selectorGC
!= None
)) {
1150 radius
= butPtr
->selectorDiameter
/2;
1151 points
[0].x
= x
- (butPtr
->selectorSpace
+ butPtr
->padX
1152 + butPtr
->selectorDiameter
)/2;
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
);
1164 Tk_Fill3DPolygon(Tk_Display(tkwin
), pixmap
, butPtr
->normalBorder
,
1165 points
, 4, butPtr
->borderWidth
, TK_RELIEF_FLAT
);
1167 Tk_Draw3DPolygon(Tk_Display(tkwin
), pixmap
, border
,
1168 points
, 4, butPtr
->borderWidth
, TK_RELIEF_RAISED
);
1172 * If the button is disabled with a stipple rather than a special
1173 * foreground color, generate the stippled effect.
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
));
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.
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
,
1195 * Copy the information from the off-screen pixmap onto the screen,
1196 * then delete the pixmap.
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
);
1205 *--------------------------------------------------------------
1207 * ButtonEventProc --
1209 * This procedure is invoked by the Tk dispatcher for various
1210 * events on buttons.
1216 * When the window gets deleted, internal structures get
1217 * cleaned up. When it gets exposed, it is redisplayed.
1219 *--------------------------------------------------------------
1223 ButtonEventProc(clientData
, eventPtr
)
1224 ClientData clientData
; /* Information about window. */
1225 XEvent
*eventPtr
; /* Information about event. */
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(
1239 (ClientData
) butPtr
);
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);
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;
1256 Tk_EventuallyFree((ClientData
) butPtr
, DestroyButton
);
1261 *----------------------------------------------------------------------
1263 * ComputeButtonGeometry --
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.
1273 * The button's window may change size.
1275 *----------------------------------------------------------------------
1279 ComputeButtonGeometry(butPtr
)
1280 register Button
*butPtr
; /* Button whose geometry may have changed. */
1284 unsigned int width
, height
;
1286 butPtr
->selectorSpace
= 0;
1287 if (butPtr
->bitmap
!= None
) {
1288 #if defined(USE_XPM3)
1289 Tk_SizeOfPixmap(butPtr
->bitmap
, &width
, &height
);
1291 Tk_SizeOfBitmap(butPtr
->bitmap
, &width
, &height
);
1293 if (butPtr
->width
> 0) {
1294 width
= butPtr
->width
;
1296 if (butPtr
->height
> 0) {
1297 height
= butPtr
->height
;
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;
1305 butPtr
->selectorDiameter
= (75*height
)/100;
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);
1319 if (butPtr
->height
> 0) {
1320 height
*= butPtr
->height
;
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;
1329 butPtr
->selectorSpace
= butPtr
->selectorDiameter
+ butPtr
->padX
;
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.
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
);
1349 *----------------------------------------------------------------------
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.
1359 * A standard Tcl return value. Information is also left in
1363 * Depends on the button and its associated command.
1365 *----------------------------------------------------------------------
1369 InvokeButton(butPtr
)
1370 register Button
*butPtr
; /* Information about button. */
1372 if (butPtr
->type
== TYPE_CHECK_BUTTON
) {
1373 if (butPtr
->flags
& SELECTED
) {
1374 Tcl_SetVar(butPtr
->interp
, butPtr
->selVarName
, butPtr
->offValue
,
1377 Tcl_SetVar(butPtr
->interp
, butPtr
->selVarName
, butPtr
->onValue
,
1380 } else if (butPtr
->type
== TYPE_RADIO_BUTTON
) {
1381 Tcl_SetVar(butPtr
->interp
, butPtr
->selVarName
, butPtr
->onValue
,
1384 if ((butPtr
->type
!= TYPE_LABEL
) && (butPtr
->command
!= NULL
)) {
1385 return Tcl_GlobalEval(butPtr
->interp
, butPtr
->command
);
1391 *--------------------------------------------------------------
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.
1401 * NULL is always returned.
1404 * The button may become selected or deselected.
1406 *--------------------------------------------------------------
1411 ButtonVarProc(clientData
, interp
, name1
, name2
, flags
)
1412 ClientData clientData
; /* Information about button. */
1413 Tcl_Interp
*interp
; /* Interpreter containing variable. */
1414 char *name1
; /* Name of variable. */
1415 char *name2
; /* Second part of variable name. */
1416 int flags
; /* Information about what happened. */
1418 register Button
*butPtr
= (Button
*) clientData
;
1422 * If the variable is being unset, then just re-establish the
1423 * trace unless the whole interpreter is going away.
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
);
1437 * Use the value of the variable to update the selected status of
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
;
1446 butPtr
->flags
|= SELECTED
;
1447 } else if (butPtr
->flags
& SELECTED
) {
1448 butPtr
->flags
&= ~SELECTED
;
1450 return (char *) NULL
;
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(
1465 (ClientData
) butPtr
);
1467 //fprintf(stderr, "ButtonVarProc Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
1469 return (char *) NULL
;
1473 *--------------------------------------------------------------
1475 * ButtonTextVarProc --
1477 * This procedure is invoked when someone changes the variable
1478 * whose contents are to be displayed in a button.
1481 * NULL is always returned.
1484 * The text displayed in the button will change to match the
1487 *--------------------------------------------------------------
1492 ButtonTextVarProc(clientData
, interp
, name1
, name2
, flags
)
1493 ClientData clientData
; /* Information about button. */
1494 Tcl_Interp
*interp
; /* Interpreter containing variable. */
1495 char *name1
; /* Name of variable. */
1496 char *name2
; /* Second part of variable name. */
1497 int flags
; /* Information about what happened. */
1499 register Button
*butPtr
= (Button
*) clientData
;
1503 * If the variable is unset, then immediately recreate it unless
1504 * the whole interpreter is going away.
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
);
1515 return (char *) NULL
;
1518 value
= Tcl_GetVar2(interp
, name1
, name2
, flags
& TCL_GLOBAL_ONLY
);
1519 if (value
== NULL
) {
1522 if (butPtr
->text
!= NULL
) {
1523 ckfree(butPtr
->text
);
1525 butPtr
->text
= ckalloc((unsigned) (strlen(value
) + 1));
1526 strcpy(butPtr
->text
, value
);
1527 ComputeButtonGeometry(butPtr
);
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(
1540 (ClientData
) butPtr
);
1542 //fprintf(stderr, "ButtonTextVarProc Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
1544 return (char *) NULL
;