1 /*
2  * tkButton.c --
3  *
4  *	This module implements a collection of button-like
5  *	widgets for the Tk toolkit.  The widgets implemented
6  *	include labels, buttons, check buttons, and radio
7  *	buttons.
8  *
9  * Copyright (c) 1990-1994 The Regents of the University of California.
10  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
11  *
12  * See the file "license.terms" for information on usage and redistribution
13  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14  *
15  * SCCS: @(#) tkButton.c 1.128 96/03/01 17:34:49
16  */
17 
18 #include "bltInt.h"
19 #include "bltTree.h"
20 
21 #ifndef NO_TILEBUTTON
22 
23 #define INDICATOR_WIDTH		28
24 #define INDICATOR_HEIGHT	17
25 
26 
27 #include "bltTile.h"
28 #include "bltImage.h"
29 
30 extern Tk_CustomOption bltTileOption;
31 /*
32  * The definitions below provide symbolic names for the default colors.
33  * NORMAL_BG -		Normal background color.
34  * ACTIVE_BG -		Background color when widget is active.
35  * SELECT_BG -		Background color for selected text.
36  * TROUGH -		Background color for troughs in scales and scrollbars.
37  * INDICATOR -		Color for indicator when button is selected.
38  * DISABLED -		Foreground color when widget is disabled.
39  */
40 
41 #define NORMAL_BG	"#d9d9d9"
42 #define ACTIVE_BG	"#d9d9d9"
43 #define SELECT_BG	"#c3c3c3"
44 #define TROUGH		"#c3c3c3"
45 #define INDICATOR	"#b03060"
46 #define DISABLED	"#a3a3a3"
47 
48 static Tk_Uid tkNormalUid, tkActiveUid, tkDisabledUid;
49 
50 #define DEF_BUTTON_ANCHOR		"center"
51 #define DEF_BUTTON_ACTIVE_BACKGROUND	"#ececec"
52 #define DEF_BUTTON_ACTIVE_BG_MONO	RGB_BLACK
53 #define DEF_BUTTON_ACTIVE_FOREGROUND	RGB_BLACK
54 #define DEF_BUTTON_ACTIVE_FG_MONO	RGB_WHITE
55 #define DEF_BUTTON_BACKGROUND		STD_NORMAL_BACKGROUND
56 #define DEF_BUTTON_BG_MONO		RGB_WHITE
57 #define DEF_BUTTON_BITMAP		""
58 #define DEF_BUTTON_BORDERWIDTH		"1"
59 #define DEF_BUTTON_CURSOR		""
60 #define DEF_BUTTON_COMMAND		""
61 #define DEF_BUTTON_COMPOUND		"none"
62 #define DEF_DIRECTION_DEFAULT "below"
63 #define DEF_BUTTON_DEFAULT              "disabled"
64 #define DEF_BUTTON_DISABLED_FOREGROUND	STD_DISABLE_FOREGROUND
65 #define DEF_BUTTON_DISABLED_FG_MONO	""
66 #define DEF_BUTTON_FG			RGB_BLACK
67 #define DEF_BUTTON_FONT			"TkDefaultFont"
68 #define DEF_BUTTON_HEIGHT		"0"
69 #define DEF_BUTTON_HIGHLIGHT_BG		STD_NORMAL_BACKGROUND
70 #define DEF_BUTTON_HIGHLIGHT		RGB_BLACK
71 #define DEF_LABEL_HIGHLIGHT_WIDTH	"0"
72 #define DEF_BUTTON_HIGHLIGHT_WIDTH	"1"
73 #define DEF_BUTTON_IMAGE		(char *) NULL
74 #define DEF_BUTTON_INDICATOR		"1"
75 #define DEF_BUTTON_INDICATORSIZE		"10"
76 #define DEF_BUTTON_JUSTIFY		"center"
77 #define DEF_BUTTON_OFF_VALUE		"0"
78 #define DEF_BUTTON_ON_VALUE		"1"
79 #define DEF_BUTTON_OVER_RELIEF		"raised"
80 #define DEF_BUTTON_PADX			"3m"
81 #define DEF_LABCHKRAD_PADX		"1"
82 #define DEF_BUTTON_PADY			"1m"
83 #define DEF_LABCHKRAD_PADY		"1"
84 #define DEF_BUTTON_RELIEF		"raised"
85 #define DEF_BUTTON_REPEAT_DELAY		"0"
86 #define DEF_LABCHKRAD_RELIEF		"flat"
87 #define DEF_BUTTON_SELECT_COLOR		STD_INDICATOR_COLOR
88 #define DEF_BUTTON_SELECT_MONO		RGB_BLACK
89 #define DEF_BUTTON_SELECT_IMAGE		(char *) NULL
90 #define DEF_BUTTON_STATE		"normal"
91 #define DEF_LABEL_TAKE_FOCUS		"0"
92 #define DEF_BUTTON_TAKE_FOCUS		""
93 #define DEF_BUTTON_TEXT			""
94 #define DEF_BUTTON_TEXT_VARIABLE	""
95 #define DEF_BUTTON_UNDERLINE		"-1"
96 #define DEF_BUTTON_VALUE		""
97 #define DEF_BUTTON_WIDTH		"0"
98 #define DEF_BUTTON_WRAP_LENGTH		"0"
99 #define DEF_RADIOBUTTON_VARIABLE	"selectedButton"
100 #define DEF_CHECKBUTTON_VARIABLE	""
101 #define DEF_SHADOW		RGB_BLACK
102 #define DEF_ROTATE		"0.0"
103 
104 /*
105  * A data structure of the following type is kept for each
106  * widget managed by this file:
107  */
108 
109 typedef struct {
110     Tk_Window tkwin;		/* Window that embodies the button.  NULL
111 				 * means that the window has been destroyed. */
112     Display *display;		/* Display containing widget.  Needed to
113 				 * free up resources after tkwin is gone. */
114     Tcl_Interp *interp;		/* Interpreter associated with button. */
115     Tcl_Command widgetCmd;	/* Token for button's widget command. */
116     int type;			/* Type of widget:  restricts operations
117 				 * that may be performed on widget.  See
118 				 * below for possible values. */
119 
120     /*
121      * Information about what's in the button.
122      */
123 
124     char *textPtr;			/* Text to display in button (malloc'ed)
125 				 * or NULL. */
126     int numChars;		/* # of characters in text. */
127     int underline;		/* Index of character to underline.  < 0 means
128 				 * don't underline anything. */
129     char *textVarName;		/* Name of variable (malloc'ed) or NULL.
130 				 * If non-NULL, button displays the contents
131 				 * of this variable. */
132     Pixmap bitmap;		/* Bitmap to display or None.  If not None
133 				 * then text and textVar are ignored. */
134     char *imageString;		/* Name of image to display (malloc'ed), or
135 				 * NULL.  If non-NULL, bitmap, text, and
136 				 * textVarName are ignored. */
137     char *activeImageString;
138     char *disabledImageString;
139     Tk_Image image;		/* Image to display in window, or NULL if
140 				 * none. */
141     Tk_Image activeImage;
142     Tk_Image disabledImage;
143     Tk_Image tristateImage;
144     char * tristateImageString;
145     char *selectImageString;	/* Name of image to display when selected
146 				 * (malloc'ed), or NULL. */
147     Tk_Image selectImage;	/* Image to display in window when selected,
148 				 * or NULL if none.  Ignored if image is
149 				 * NULL. */
150 
151     /*
152      * Information used when displaying widget:
153      */
154 
155     int state;		/* State of button for display purposes:
156 				 * normal, active, or disabled. */
157     Tk_3DBorder normalBorder;	/* Structure used to draw 3-D
158 				 * border and background when window
159 				 * isn't active.  NULL means no such
160 				 * border exists. */
161     Tk_3DBorder activeBorder;	/* Structure used to draw 3-D
162 				 * border and background when window
163 				 * is active.  NULL means no such
164 				 * border exists. */
165     int borderWidth;		/* Width of border. */
166     int relief;			/* 3-d effect: TK_RELIEF_RAISED, etc. */
167     int overRelief;		/* Value of -overrelief option: specifies a 3-d
168 				 * effect for the border, such as
169 				 * TK_RELIEF_RAISED, to be used when the mouse
170 				 * is over the button. */
171     int offRelief;
172     int highlightWidth;		/* Width in pixels of highlight to draw
173 				 * around widget when it has the focus.
174 				 * <= 0 means don't draw a highlight. */
175     XColor *highlightBgColorPtr;
176     /* Color for drawing traversal highlight
177 				 * area when highlight is off. */
178     XColor *highlightColorPtr;	/* Color for drawing traversal highlight. */
179     int inset;			/* Total width of all borders, including
180 				 * traversal highlight and 3-D border.
181 				 * Indicates how much interior stuff must
182 				 * be offset from outside edges to leave
183 				 * room for borders. */
184     Tk_Font tkfont;		/* Information about text font, or NULL. */
185     XColor *normalFg;		/* Foreground color in normal mode. */
186     XColor *activeFg;		/* Foreground color in active mode.  NULL
187 				 * means use normalFg instead. */
188     XColor *disabledFg;		/* Foreground color when disabled.  NULL
189 				 * means use normalFg with a 50% stipple
190 				 * instead. */
191     GC normalTextGC;		/* GC for drawing text in normal mode.  Also
192 				 * used to copy from off-screen pixmap onto
193 				 * screen. */
194     GC activeTextGC;		/* GC for drawing text in active mode (NULL
195 				 * means use normalTextGC). */
196     Pixmap gray; 		/* Pixmap for displaying disabled text if
197 				 * disabledFg is NULL. */
198     GC disabledGC;		/* Used to produce disabled effect.  If
199 				 * disabledFg isn't NULL, this GC is used to
200 				 * draw button text or icon.  Otherwise
201 				 * text or icon is drawn with normalGC and
202 				 * this GC is used to stipple background
203 				 * across it.  For labels this is None. */
204     GC copyGC;			/* Used for copying information from an
205 				 * off-screen pixmap to the screen. */
206     char *widthString;		/* Value of -width option.  Malloc'ed. */
207     char *heightString;		/* Value of -height option.  Malloc'ed. */
208     int width, height;		/* If > 0, these specify dimensions to request
209 				 * for window, in characters for text and in
210 				 * pixels for bitmaps.  In this case the actual
211 				 * size of the text string or bitmap is
212 				 * ignored in computing desired window size. */
213     int wrapLength;		/* Line length (in pixels) at which to wrap
214 				 * onto next line.  <= 0 means don't wrap
215 				 * except at newlines. */
216     int padX, padY;		/* Extra space around text (pixels to leave
217 				 * on each side).  Ignored for bitmaps and
218 				 * images. */
219     Tk_Anchor anchor;		/* Where text/bitmap should be displayed
220 				 * inside button region. */
221     Tk_Justify justify;		/* Justification to use for multi-line text. */
222     int indicatorOn;		/* True means draw indicator, false means
223 				 * don't draw it. */
224     Tk_3DBorder selectBorder;	/* For drawing indicator background, or perhaps
225 				 * widget background, when selected. */
226     int textWidth;		/* Width needed to display text as requested,
227 				 * in pixels. */
228     int textHeight;		/* Height needed to display text as requested,
229 				 * in pixels. */
230 #if (TK_MAJOR_VERSION > 4)
231     Tk_TextLayout textLayout;	/* Saved text layout information. */
232 #endif
233     int indicatorSpace;		/* Horizontal space (in pixels) allocated for
234 				 * display of indicator. */
235     int indicatorDiameter;	/* Diameter of indicator, in pixels. */
236     int indicatorSize;    /* Fixed the size of indicator. */
237 
238     int defaultState;	/* Used in 8.0 (not here)  */
239 
240     /*
241      * For check and radio buttons, the fields below are used
242      * to manage the variable indicating the button's state.
243      */
244 
245     char *selVarName;		/* Name of variable used to control selected
246 				 * state of button.  Malloc'ed (if
247 				 * not NULL). */
248     char *onValue;		/* Value to store in variable when
249 				 * this button is selected.  Malloc'ed (if
250 				 * not NULL). */
251     char *offValue;		/* Value to store in variable when this
252 				 * button isn't selected.  Malloc'ed
253 				 * (if not NULL).  Valid only for check
254 				 * buttons. */
255 
256     /*
257      * Miscellaneous information:
258      */
259 
260     Tk_Cursor cursor;		/* Current cursor for window, or None. */
261     char *takeFocus;		/* Value of -takefocus option;  not used in
262 				 * the C code, but used by keyboard traversal
263 				 * scripts.  Malloc'ed, but may be NULL. */
264     char *command;		/* Command to execute when button is
265 				 * invoked; valid for buttons only.
266 				 * If not NULL, it's malloc-ed. */
267     int compound;		/* Value of -compound option; specifies whether
268 				 * the button should show both an image and
269 				 * text, and, if so, how. */
270     int repeatDelay;		/* Value of -repeatdelay option; specifies
271 				 * the number of ms after which the button will
272 				 * start to auto-repeat its command. */
273     int repeatInterval;		/* Value of -repeatinterval option; specifies
274 				 * the number of ms between auto-repeat
275 				 * invocataions of the button command. */
276     int flags;			/* Various flags;  see below for
277 				 * definitions. */
278     Blt_Tile innerTile, disabledTile;
279     Blt_Tile tile, activeTile, activeInnerTile, disabledInnerTile;
280     Shadow shadow;
281     Shadow activeShadow;
282     double rotate;
283     GC focusGC;
284     char *tristateValue;
285     Tk_Image icons[9];
286     char *iconStr;
287     int iconCnt;
288     Tk_3DBorder innerBorder;
289     char *bdImageString;		/* Name of image for border. */
290     char *activeBdImageString;		/* Name of image for border. */
291     char *disabledBdImageString;		/* Name of image for border. */
292     Tk_Image bdImage;
293     Tk_Image activeBdImage;
294     Tk_Image disabledBdImage;
295     Tk_Image bdImageSized;
296     int bdHalo;
297     Blt_Tree tree;
298     int node;
299     Blt_TreeTrace textTrace;
300     Blt_TreeTrace selTrace;
301     Blt_Tile inBdTile;
302     int xOffset, yOffset;
303     XColor *bdMaskColor;		/* Mask color for bdImage */
304     char *menuName;
305     int direction;
306     int indicatorWidth;
307     int indicatorHeight;
308     Blt_Dashes dashes;
309     int bgTileTop;
310     char *classString;
311 } Button;
312 
313 /*
314  * Possible "type" values for buttons.  These are the kinds of
315  * widgets supported by this file.  The ordering of the type
316  * numbers is significant:  greater means more features and is
317  * used in the code.
318  */
319 
320 #define TYPE_FRAME		0
321 #define TYPE_LABEL		1
322 #define TYPE_BUTTON		2
323 #define TYPE_CHECK_BUTTON	3
324 #define TYPE_RADIO_BUTTON	4
325 #define TYPE_MENU_BUTTON	5
326 #define CHOOSE(default, override)	\
327 	(((override) == NULL) ? (default) : (override))
328 
329 extern Tk_CustomOption bltShadowOption;
330 extern Tk_CustomOption bltGradientOption;
331 
332 /* static char *defaultStrings[] = {
333     "active", "disabled", "normal", NULL
334 }; */
335 
336 /* enum { STATE_NORMAL, STATE_ACTIVE, STATE_DISABLED } StateEnum; */
337 static char *stateStrings[] = {
338     "normal", "active", "disabled", NULL
339 };
340 
341 static char *directionStrings[] = {
342     "above", "below", "flush", "left", "right", NULL
343 };
344 
345 enum { COMPOUND_NONE, COMPOUND_BOTTOM, COMPOUND_CENTER, COMPOUND_LEFT, COMPOUND_RIGHT, COMPOUND_TOP } CompoundEnum;
346 static char *compoundStrings[] = {
347     "none", "bottom", "center", "left", "right", "top", NULL
348 };
349 
350 static int
StringToName(ClientData clientData,Tcl_Interp * interp,Tk_Window parent,char * string,char * widgRec,int offset,char * strList[],char * label)351 StringToName( ClientData clientData, Tcl_Interp *interp, Tk_Window parent,
352     char *string, char *widgRec, int offset, char *strList[], char *label )
353 {
354     int *namePtr = (int *)(widgRec + offset);
355     char c;
356     int i;
357 
358     for (i=0; strList[i]; i++) {
359         c = string[0];
360         if (strcmp(string, strList[i]) == 0) {
361             *namePtr = i;
362             return TCL_OK;
363         }
364     }
365      Tcl_AppendResult(interp, "bad ", label, " \"", string,
366 	    "\": should be one of: ", (char *)NULL);
367     for (i=0; strList[i]; i++) {
368         if (i>0) Tcl_AppendResult(interp, ", ", (char *)NULL);
369  	Tcl_AppendResult(interp, strList[i], (char *)NULL);
370     }
371     return TCL_ERROR;
372 }
373 
374 static
NameToString(ClientData clientData,Tk_Window parent,char * widgRec,int offset,Tcl_FreeProc ** freeProcPtr,char * strList[],char * msg)375 char * NameToString( ClientData clientData, Tk_Window parent,
376     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr,
377     char *strList[], char *msg )
378 {
379     int value = *(int *)(widgRec + offset);
380     int i;
381 
382     for (i=0; strList[i]; i++) ;
383     if (value < i) {
384         return strList[value];
385     }
386     return msg;
387 }
388 
389 static int
StringToState(ClientData clientData,Tcl_Interp * interp,Tk_Window parent,char * string,char * widgRec,int offset)390 StringToState( ClientData clientData, Tcl_Interp *interp, Tk_Window parent,
391     char *string, char *widgRec, int offset)
392 {
393     return StringToName( clientData, interp, parent,
394         string, widgRec, offset, stateStrings, "state" );
395 }
396 
397 static char *
StateToString(ClientData clientData,Tk_Window parent,char * widgRec,int offset,Tcl_FreeProc ** freeProcPtr)398 StateToString( ClientData clientData, Tk_Window parent,
399     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)
400 {
401     return NameToString(clientData, parent,
402         widgRec, offset, freeProcPtr, stateStrings, "unknown state value");
403 }
404 
405 static int
StringToDirection(ClientData clientData,Tcl_Interp * interp,Tk_Window parent,char * string,char * widgRec,int offset)406 StringToDirection( ClientData clientData, Tcl_Interp *interp, Tk_Window parent,
407     char *string, char *widgRec, int offset)
408 {
409     return StringToName( clientData, interp, parent,
410         string, widgRec, offset, directionStrings, "direction" );
411 }
412 
413 static char *
DirectionToString(ClientData clientData,Tk_Window parent,char * widgRec,int offset,Tcl_FreeProc ** freeProcPtr)414 DirectionToString( ClientData clientData, Tk_Window parent,
415     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)
416 {
417     return NameToString(clientData, parent,
418         widgRec, offset, freeProcPtr, directionStrings, "unknown direction value");
419 }
420 
421 
422 static Tk_CustomOption directionOption =
423 {
424     StringToDirection, DirectionToString, (ClientData)0,
425 };
426 
427 static Tk_CustomOption stateOption =
428 {
429     StringToState, StateToString, (ClientData)0,
430 };
431 
432 static int
StringToCompound(ClientData clientData,Tcl_Interp * interp,Tk_Window parent,char * string,char * widgRec,int offset)433 StringToCompound( ClientData clientData, Tcl_Interp *interp, Tk_Window parent,
434     char *string, char *widgRec, int offset)
435 {
436     return StringToName( clientData, interp, parent,
437         string, widgRec, offset, compoundStrings, "compound" );
438 }
439 
440 static char *
CompoundToString(ClientData clientData,Tk_Window parent,char * widgRec,int offset,Tcl_FreeProc ** freeProcPtr)441 CompoundToString( ClientData clientData, Tk_Window parent,
442     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)
443 {
444     return NameToString(clientData, parent,
445     widgRec, offset, freeProcPtr, compoundStrings, "unknown compound value");
446 }
447 
448 
449 static Tk_CustomOption compoundOption =
450 {
451     StringToCompound, CompoundToString, (ClientData)0,
452 };
453 
454 
455 static char *
IconsToString(ClientData clientData,Tk_Window parent,char * widgRec,int offset,Tcl_FreeProc ** freeProcPtr)456 IconsToString( ClientData clientData, Tk_Window parent,
457     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)
458 {
459     char *imgStr = *(char **)(widgRec + offset);
460     return imgStr;
461 }
462 
463 static int
464 StringToIcons( ClientData clientData, Tcl_Interp *interp, Tk_Window parent,
465     char *string, char *widgRec, int offset);
466 
467 static char *
TreeToString(ClientData clientData,Tk_Window parent,char * widgRec,int offset,Tcl_FreeProc ** freeProcPtr)468 TreeToString( ClientData clientData, Tk_Window parent,
469     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)
470 {
471     Blt_Tree *treePtr = (Blt_Tree *)(widgRec + offset);
472     if (*treePtr == NULL) {
473         return NULL;
474     }
475     return Blt_TreeName(*treePtr);
476 }
477 
478 static int
StringToTree(ClientData clientData,Tcl_Interp * interp,Tk_Window parent,char * string,char * widgRec,int offset)479 StringToTree( ClientData clientData, Tcl_Interp *interp, Tk_Window parent,
480     char *string, char *widgRec, int offset)
481 {
482     Blt_Tree *treePtr = (Blt_Tree *)(widgRec + offset);
483     if (string[0] == 0) {
484         if (*treePtr) Blt_TreeReleaseToken(*treePtr);
485         *treePtr = NULL;
486         return TCL_OK;
487     }
488     return Blt_TreeGetToken(interp, string, treePtr);
489 }
490 
491 
492 /* WARNING: hardcoded to butPtr->icons. */
493 static Tk_CustomOption iconsOption =
494 {
495     StringToIcons, IconsToString, (ClientData)0,
496 };
497 
498 Tk_CustomOption bltTreeOption =
499 {
500     StringToTree, TreeToString, (ClientData)0,
501 };
502 
503 
504 /*
505  * Class names for buttons, indexed by one of the type values above.
506  */
507 
508 #define BBOFS 6
509 
510 static char *classNames[] =
511 {"Frame", "Label", "Button", "Checkbutton", "Radiobutton", "Menubutton",
512  "BFrame", "BLabel", "BButton", "BCheckbutton", "BRadiobutton", "BMenubutton"
513 };
514 
515 static void widgetWorldChanged(ClientData clientData);
516 
517 static Tk_ClassProcs butClass = {
518     sizeof(Tk_ClassProcs),	/* size */
519     widgetWorldChanged,		/* worldChangedProc */
520 };
521 
522 /*
523  * Flag bits for buttons:
524  *
525  * REDRAW_PENDING:		Non-zero means a DoWhenIdle handler
526  *				has already been queued to redraw
527  *				this window.
528  * SELECTED:			Non-zero means this button is selected,
529  *				so special highlight should be drawn.
530  * GOT_FOCUS:			Non-zero means this button currently
531  *				has the input focus.
532  */
533 
534 #define REDRAW_PENDING          (1 << 0)
535 #define SELECTED                (1 << 1)
536 #define GOT_FOCUS               (1 << 2)
537 #define BUTTON_DELETED          (1 << 3)
538 #define TRISTATED               (1 << 4)
539 #define HAS_ICONS               (1 << 5)
540 #define BUTTON_LAYOUT           (1 << 6)
541 #define BUTTON_DIRTYBD          (1 << 7)
542 
543 
544 /*
545  * Mask values used to selectively enable entries in the
546  * configuration specs:
547  */
548 
549 #define FRAME_MASK		TK_CONFIG_USER_BIT
550 #define LABEL_MASK		TK_CONFIG_USER_BIT << 1
551 #define BUTTON_MASK		TK_CONFIG_USER_BIT << 2
552 #define CHECK_BUTTON_MASK	TK_CONFIG_USER_BIT << 3
553 #define RADIO_BUTTON_MASK	TK_CONFIG_USER_BIT << 4
554 #define MENU_BUTTON_MASK	TK_CONFIG_USER_BIT << 5
555 #define NALL_MASK		(LABEL_MASK | BUTTON_MASK \
556 	| CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | MENU_BUTTON_MASK)
557 #define ALL_MASK		(FRAME_MASK | NALL_MASK )
558 #define NONLABEL_MASK		(BUTTON_MASK \
559          | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | MENU_BUTTON_MASK)
560 
561 static int configFlags[] =
562 {FRAME_MASK, LABEL_MASK, BUTTON_MASK,
563     CHECK_BUTTON_MASK, RADIO_BUTTON_MASK, MENU_BUTTON_MASK};
564 
565 extern Tk_CustomOption bltDashesOption;
566 
567 /*
568  * Information used for parsing configuration specs:
569  */
570 
571 static Tk_ConfigSpec configSpecs[] =
572 {
573     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
574 	DEF_BUTTON_ACTIVE_BACKGROUND, Tk_Offset(Button, activeBorder),
575 	ALL_MASK | TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
576     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
577 	DEF_BUTTON_ACTIVE_BG_MONO, Tk_Offset(Button, activeBorder),
578 	ALL_MASK | TK_CONFIG_MONO_ONLY  | TK_CONFIG_NULL_OK},
579     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
580 	DEF_BUTTON_ACTIVE_FOREGROUND, Tk_Offset(Button, activeFg),
581 	ALL_MASK | TK_CONFIG_COLOR_ONLY},
582     {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
583 	DEF_BUTTON_ACTIVE_FG_MONO, Tk_Offset(Button, activeFg),
584 	ALL_MASK | TK_CONFIG_MONO_ONLY},
585     {TK_CONFIG_CUSTOM, "-activeshadow", "activeShadow", "ActiveShadow",
586 	NULL, Tk_Offset(Button, activeShadow),
587         ALL_MASK | TK_CONFIG_NULL_OK, &bltShadowOption},
588     {TK_CONFIG_CUSTOM, "-activetile", "activeTile", "Tile",
589 	(char *)NULL, Tk_Offset(Button, activeTile),
590 	ALL_MASK | TK_CONFIG_NULL_OK, &bltTileOption},
591     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
592 	DEF_BUTTON_ANCHOR, Tk_Offset(Button, anchor), NALL_MASK},
593     {TK_CONFIG_BORDER, "-background", "background", "Background",
594 	DEF_BUTTON_BACKGROUND, Tk_Offset(Button, normalBorder),
595 	ALL_MASK | TK_CONFIG_COLOR_ONLY},
596     {TK_CONFIG_BORDER, "-background", "background", "Background",
597 	DEF_BUTTON_BG_MONO, Tk_Offset(Button, normalBorder),
598 	ALL_MASK | TK_CONFIG_MONO_ONLY},
599     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
600 	(char *)NULL, 0, ALL_MASK},
601     {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
602 	(char *)NULL, 0, ALL_MASK},
603    {TK_CONFIG_INT, "-bdflags", "bdFlags", "BdFlags",
604 	"0", Tk_Offset(Button, bdHalo),
605 	ALL_MASK | TK_CONFIG_NULL_OK},
606    {TK_CONFIG_STRING, "-bdimage", "bdImage", "Image",
607 	DEF_BUTTON_IMAGE, Tk_Offset(Button, bdImageString),
608 	ALL_MASK | TK_CONFIG_NULL_OK},
609    {TK_CONFIG_STRING, "-activebdimage", "activeBdImage", "Image",
610 	DEF_BUTTON_IMAGE, Tk_Offset(Button, activeBdImageString),
611 	ALL_MASK | TK_CONFIG_NULL_OK},
612    {TK_CONFIG_STRING, "-class", "class", "Class",
613 	NULL, Tk_Offset(Button, classString),
614 	FRAME_MASK | TK_CONFIG_NULL_OK},
615    {TK_CONFIG_CUSTOM, "-dashes", "dashes", "Dashes",
616 	"dot", Tk_Offset(Button, dashes),
617 	TK_CONFIG_NULL_OK | NONLABEL_MASK, &bltDashesOption},
618    {TK_CONFIG_STRING, "-disabledbdimage", "disabledBdImage", "Image",
619 	DEF_BUTTON_IMAGE, Tk_Offset(Button, disabledBdImageString),
620 	ALL_MASK | TK_CONFIG_NULL_OK},
621     /*{TK_CONFIG_COLOR, "-bdmaskcolor", "bdMaskColor", "BdMaskColor",
622 	(char *)NULL, Tk_Offset(Button, bdMaskColor),
623 	ALL_MASK| TK_CONFIG_NULL_OK}, */
624     {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
625 	DEF_BUTTON_BITMAP, Tk_Offset(Button, bitmap),
626 	NALL_MASK | TK_CONFIG_NULL_OK},
627     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
628 	DEF_BUTTON_BORDERWIDTH, Tk_Offset(Button, borderWidth), ALL_MASK},
629     {TK_CONFIG_STRING, "-command", "command", "Command",
630 	DEF_BUTTON_COMMAND, Tk_Offset(Button, command),
631 	BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | TK_CONFIG_NULL_OK},
632     {TK_CONFIG_CUSTOM, "-compound", "compound", "Compound",
633 	DEF_BUTTON_COMPOUND, Tk_Offset(Button, compound),
634 	ALL_MASK | TK_CONFIG_NULL_OK, &compoundOption},
635     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
636 	DEF_BUTTON_CURSOR, Tk_Offset(Button, cursor),
637 	ALL_MASK | TK_CONFIG_NULL_OK},
638     {TK_CONFIG_CUSTOM, "-default", "default", "Default",
639 	DEF_BUTTON_DEFAULT, Tk_Offset(Button, defaultState), BUTTON_MASK, &stateOption},
640     {TK_CONFIG_CUSTOM, "-direction", "direction", "Direction",
641 	DEF_DIRECTION_DEFAULT, Tk_Offset(Button, direction), MENU_BUTTON_MASK, &directionOption},
642     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
643 	"DisabledForeground", DEF_BUTTON_DISABLED_FOREGROUND,
644 	Tk_Offset(Button, disabledFg), ALL_MASK | TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
645     {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
646 	"DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO,
647 	Tk_Offset(Button, disabledFg), ALL_MASK | TK_CONFIG_MONO_ONLY | TK_CONFIG_NULL_OK},
648     {TK_CONFIG_CUSTOM, "-disabledtile", "disabledTile", "Tile",
649 	(char *)NULL, Tk_Offset(Button, disabledTile),
650 	ALL_MASK | TK_CONFIG_NULL_OK, &bltTileOption},
651     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *)NULL,
652 	(char *)NULL, 0, NALL_MASK},
653     {TK_CONFIG_FONT, "-font", "font", "Font",
654 	DEF_BUTTON_FONT, Tk_Offset(Button, tkfont),
655 	NALL_MASK},
656     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
657 	DEF_BUTTON_FG, Tk_Offset(Button, normalFg), NALL_MASK},
658     {TK_CONFIG_STRING, "-height", "height", "Height",
659 	DEF_BUTTON_HEIGHT, Tk_Offset(Button, heightString), ALL_MASK},
660     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
661 	"HighlightBackground", DEF_BUTTON_HIGHLIGHT_BG,
662 	Tk_Offset(Button, highlightBgColorPtr), NALL_MASK | TK_CONFIG_NULL_OK},
663     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
664 	DEF_BUTTON_HIGHLIGHT, Tk_Offset(Button, highlightColorPtr),
665 	NALL_MASK},
666     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
667 	"HighlightThickness",
668 	DEF_LABEL_HIGHLIGHT_WIDTH, Tk_Offset(Button, highlightWidth),
669 	LABEL_MASK| MENU_BUTTON_MASK},
670     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
671 	"HighlightThickness",
672 	DEF_BUTTON_HIGHLIGHT_WIDTH, Tk_Offset(Button, highlightWidth),
673 	BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK  },
674     {TK_CONFIG_CUSTOM, "-icons", "icons", "icons",
675 	NULL, Tk_Offset(Button, iconStr),
676          TK_CONFIG_NULL_OK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | MENU_BUTTON_MASK, &iconsOption},
677     {TK_CONFIG_STRING, "-image", "image", "Image",
678 	DEF_BUTTON_IMAGE, Tk_Offset(Button, imageString),
679 	NALL_MASK | TK_CONFIG_NULL_OK},
680     {TK_CONFIG_STRING, "-activeimage", "activeImage", "Image",
681 	DEF_BUTTON_IMAGE, Tk_Offset(Button, activeImageString),
682 	ALL_MASK | TK_CONFIG_NULL_OK},
683     {TK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
684 	DEF_BUTTON_INDICATOR, Tk_Offset(Button, indicatorOn),
685 	CHECK_BUTTON_MASK | RADIO_BUTTON_MASK },
686     {TK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
687 	"0", Tk_Offset(Button, indicatorOn),
688         MENU_BUTTON_MASK },
689     {TK_CONFIG_INT, "-checksize", "checkSize", "CheckSize",
690 	DEF_BUTTON_INDICATORSIZE, Tk_Offset(Button, indicatorSize),
691 	CHECK_BUTTON_MASK | RADIO_BUTTON_MASK},
692     {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
693 	DEF_BUTTON_JUSTIFY, Tk_Offset(Button, justify), NALL_MASK},
694    /* {TK_CONFIG_BORDER, "-innerbg", "innerBg", "Background",
695 	NULL, Tk_Offset(Button, innerBorder),
696 	ALL_MASK |TK_CONFIG_NULL_OK},
697     {TK_CONFIG_CUSTOM, "-innertile", "innerTile", "InnerTile",
698 	(char *)NULL, Tk_Offset(Button, innerTile),
699 	ALL_MASK | TK_CONFIG_NULL_OK, &bltTileOption},
700     {TK_CONFIG_CUSTOM, "-activeinnertile", "activeInnerTile", "ActiveInnerTile",
701 	(char *)NULL, Tk_Offset(Button, activeInnerTile),
702 	ALL_MASK | TK_CONFIG_NULL_OK, &bltTileOption},
703     {TK_CONFIG_CUSTOM, "-disabledinnertile", "disabledInnerTile", "DisabledInnerTile",
704 	(char *)NULL, Tk_Offset(Button, disabledInnerTile),
705 	ALL_MASK | TK_CONFIG_NULL_OK, &bltTileOption}, */
706     {TK_CONFIG_STRING, "-menu", "menu", "Menu",
707 	(char *)NULL, Tk_Offset(Button, menuName),
708 	MENU_BUTTON_MASK},
709     {TK_CONFIG_STRING, "-offvalue", "offValue", "Value",
710 	DEF_BUTTON_OFF_VALUE, Tk_Offset(Button, offValue),
711 	CHECK_BUTTON_MASK},
712     {TK_CONFIG_STRING, "-onvalue", "onValue", "Value",
713 	DEF_BUTTON_ON_VALUE, Tk_Offset(Button, onValue),
714 	CHECK_BUTTON_MASK},
715     {TK_CONFIG_RELIEF, "-overrelief", "overRelief", "OverRelief",
716 	DEF_BUTTON_OVER_RELIEF, Tk_Offset(Button, overRelief),
717 	BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | TK_CONFIG_DONT_SET_DEFAULT},
718     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
719 	DEF_BUTTON_PADX, Tk_Offset(Button, padX), BUTTON_MASK | MENU_BUTTON_MASK},
720     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
721 	DEF_LABCHKRAD_PADX, Tk_Offset(Button, padX),
722 	FRAME_MASK | LABEL_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK},
723     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
724 	DEF_BUTTON_PADY, Tk_Offset(Button, padY), BUTTON_MASK | MENU_BUTTON_MASK},
725     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
726 	DEF_LABCHKRAD_PADY, Tk_Offset(Button, padY),
727 	FRAME_MASK | LABEL_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK},
728     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
729 	DEF_BUTTON_RELIEF, Tk_Offset(Button, relief), BUTTON_MASK},
730     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
731 	DEF_LABCHKRAD_RELIEF, Tk_Offset(Button, relief),
732 	FRAME_MASK | LABEL_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | MENU_BUTTON_MASK},
733     {TK_CONFIG_RELIEF, "-offrelief", "offRelief", "Relief",
734 	DEF_BUTTON_RELIEF, Tk_Offset(Button, offRelief), CHECK_BUTTON_MASK | RADIO_BUTTON_MASK},
735     {TK_CONFIG_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
736 	DEF_BUTTON_REPEAT_DELAY, Tk_Offset(Button, repeatDelay),
737  	BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK},
738     {TK_CONFIG_DOUBLE, "-rotate", "rotate", "Rotate",
739 	DEF_ROTATE, Tk_Offset(Button, rotate),
740 	TK_CONFIG_DONT_SET_DEFAULT|ALL_MASK},
741     {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
742 	DEF_BUTTON_SELECT_COLOR, Tk_Offset(Button, selectBorder),
743 	CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | TK_CONFIG_COLOR_ONLY
744 	| TK_CONFIG_NULL_OK},
745     {TK_CONFIG_BORDER, "-selectcolor", "selectColor", "Background",
746 	DEF_BUTTON_SELECT_MONO, Tk_Offset(Button, selectBorder),
747 	CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | TK_CONFIG_MONO_ONLY
748 	| TK_CONFIG_NULL_OK},
749     {TK_CONFIG_STRING, "-selectimage", "selectImage", "Image",
750 	DEF_BUTTON_SELECT_IMAGE, Tk_Offset(Button, selectImageString),
751 	CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | TK_CONFIG_NULL_OK},
752     {TK_CONFIG_CUSTOM, "-shadow", "shadow", "Shadow",
753 	NULL, Tk_Offset(Button, shadow),
754         ALL_MASK | TK_CONFIG_NULL_OK, &bltShadowOption},
755     {TK_CONFIG_CUSTOM, "-state", "state", "State",
756 	DEF_BUTTON_STATE, Tk_Offset(Button, state),
757 	ALL_MASK, &stateOption},
758     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
759 	DEF_LABEL_TAKE_FOCUS, Tk_Offset(Button, takeFocus),
760 	LABEL_MASK | TK_CONFIG_NULL_OK},
761     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
762 	DEF_BUTTON_TAKE_FOCUS, Tk_Offset(Button, takeFocus),
763 	BUTTON_MASK | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK | MENU_BUTTON_MASK | TK_CONFIG_NULL_OK},
764     {TK_CONFIG_STRING, "-text", "text", "Text",
765 	DEF_BUTTON_TEXT, Tk_Offset(Button, textPtr), NALL_MASK},
766     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
767 	DEF_BUTTON_TEXT_VARIABLE, Tk_Offset(Button, textVarName),
768 	NALL_MASK | TK_CONFIG_NULL_OK},
769     {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
770 	(char *)NULL, Tk_Offset(Button, tile),
771 	ALL_MASK | TK_CONFIG_NULL_OK, &bltTileOption},
772     {TK_CONFIG_CUSTOM, "-tree", "tree", "Tree",
773 	(char *)NULL, Tk_Offset(Button, tree), NALL_MASK | TK_CONFIG_NULL_OK,
774 	&bltTreeOption},
775     {TK_CONFIG_INT, "-node", "node", "Node",
776 	"0", Tk_Offset(Button, node), NALL_MASK},
777     {TK_CONFIG_INT, "-tiletop", "tileTop", "TileTop",
778 	"0", Tk_Offset(Button, bgTileTop),
779 	FRAME_MASK | LABEL_MASK | TK_CONFIG_NULL_OK},
780     {TK_CONFIG_STRING, "-tristateimage", "tristateImage", "Image",
781 	DEF_BUTTON_IMAGE, Tk_Offset(Button, tristateImageString),
782         CHECK_BUTTON_MASK | TK_CONFIG_NULL_OK},
783     {TK_CONFIG_STRING, "-tristatevalue", "tristateValue", "TristateValue",
784 	DEF_BUTTON_TEXT_VARIABLE, Tk_Offset(Button, tristateValue),
785          CHECK_BUTTON_MASK | TK_CONFIG_NULL_OK},
786     {TK_CONFIG_INT, "-underline", "underline", "Underline",
787 	DEF_BUTTON_UNDERLINE, Tk_Offset(Button, underline), NALL_MASK},
788     {TK_CONFIG_STRING, "-value", "value", "Value",
789 	DEF_BUTTON_VALUE, Tk_Offset(Button, onValue),
790 	RADIO_BUTTON_MASK},
791     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
792 	DEF_RADIOBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
793 	RADIO_BUTTON_MASK},
794     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
795 	DEF_CHECKBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
796 	CHECK_BUTTON_MASK | TK_CONFIG_NULL_OK},
797     {TK_CONFIG_STRING, "-width", "width", "Width",
798 	DEF_BUTTON_WIDTH, Tk_Offset(Button, widthString), ALL_MASK},
799     {TK_CONFIG_PIXELS, "-wraplength", "wrapLength", "WrapLength",
800 	DEF_BUTTON_WRAP_LENGTH, Tk_Offset(Button, wrapLength), NALL_MASK},
801     {TK_CONFIG_PIXELS, "-xoffset", "xOffset", "XOffset",
802 	DEF_BUTTON_WRAP_LENGTH, Tk_Offset(Button, xOffset), NALL_MASK},
803     {TK_CONFIG_PIXELS, "-yoffset", "yOffset", "YOffset",
804 	DEF_BUTTON_WRAP_LENGTH, Tk_Offset(Button, yOffset), NALL_MASK},
805     {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
806 	(char *)NULL, 0, 0}
807 };
808 
809 /*
810  * String to print out in error messages, identifying options for
811  * widget commands for different types of labels or buttons:
812  */
813 
814 static char *optionStrings[] =
815 {
816     "cget or configure",
817     "cget, configure, flash, or invoke",
818     "cget, configure, deselect, flash, invoke, select, or toggle",
819     "cget, configure, deselect, flash, invoke, or select"
820 };
821 
822 /*
823  * Forward declarations for procedures defined later in this file:
824  */
825 
826 static void ButtonCmdDeletedProc _ANSI_ARGS_((
827 	ClientData clientData));
828 static int ButtonCreate _ANSI_ARGS_((ClientData clientData,
829 	Tcl_Interp *interp, int argc, char **argv,
830 	int type));
831 static void ButtonEventProc _ANSI_ARGS_((ClientData clientData,
832 	XEvent *eventPtr));
833 static void ButtonImageProc _ANSI_ARGS_((ClientData clientData,
834 	int x, int y, int width, int height,
835 	int imgWidth, int imgHeight));
836 static void ButtonImageBdProc _ANSI_ARGS_((ClientData clientData,
837 	int x, int y, int width, int height,
838 	int imgWidth, int imgHeight));
839 static void ButtonSelectImageProc _ANSI_ARGS_((
840 	ClientData clientData, int x, int y, int width,
841 	int height, int imgWidth, int imgHeight));
842 static char *ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
843 	Tcl_Interp *interp, char *name1, char *name2,
844 	int flags));
845 static char *ButtonVarProc _ANSI_ARGS_((ClientData clientData,
846 	Tcl_Interp *interp, char *name1, char *name2,
847 	int flags));
848 static int TreeTraceProc(ClientData clientData,
849     Tcl_Interp *interp, Blt_TreeNode node, Blt_TreeKey key,
850     unsigned int flags);
851 static int TreeTextTraceProc(ClientData clientData,
852     Tcl_Interp *interp, Blt_TreeNode node, Blt_TreeKey key,
853     unsigned int flags);
854 static int ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
855 	Tcl_Interp *interp, int argc, char **argv));
856 static void ComputeButtonGeometry _ANSI_ARGS_((Button *butPtr));
857 static int ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
858 	Button *butPtr, int argc, char **argv,
859 	int flags));
860 static void DestroyButton _ANSI_ARGS_((Button *butPtr));
861 static void DisplayButton _ANSI_ARGS_((ClientData clientData));
862 static int InvokeButton _ANSI_ARGS_((Button *butPtr));
863 
864 static Blt_TileChangedProc TileChangedProc;
865 static Tcl_CmdProc ButtonCmd, LabelCmd, CheckbuttonCmd, RadiobuttonCmd;
866 
867 #if (TK_MAJOR_VERSION > 4)
868 EXTERN void TkComputeAnchor _ANSI_ARGS_((Tk_Anchor anchor, Tk_Window tkwin,
869 	int padX, int padY, int innerWidth, int innerHeight, int *xPtr,
870 	int *yPtr));
871 #endif
872 
873 static void
EventuallyRedraw(butPtr)874 EventuallyRedraw(butPtr)
875     Button *butPtr;
876 {
877     if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
878         butPtr->flags |= REDRAW_PENDING;
879 	Tcl_DoWhenIdle(DisplayButton, butPtr);
880     }
881 }
882 
883 
884 static void
widgetWorldChanged(ClientData clientData)885 widgetWorldChanged(ClientData clientData)
886 {
887     Button *butPtr = (Button *)clientData;
888     butPtr->flags |= (BUTTON_LAYOUT);
889 
890     EventuallyRedraw(butPtr);
891 }
892 
893 
894 /* TODO: make this a hash table to avoid allocating images for each button???. */
895 static int
StringToIcons(ClientData clientData,Tcl_Interp * interp,Tk_Window parent,char * string,char * widgRec,int offset)896 StringToIcons( ClientData clientData, Tcl_Interp *interp, Tk_Window parent,
897     char *string, char *widgRec, int offset)
898 {
899     Button *butPtr = (Button*)widgRec;;
900     int argc = 0;
901     int result,  i;
902     char **argv;
903     Tk_Image imgs[9], image;
904 
905     result = TCL_OK;
906     if (string && Tcl_SplitList(interp, string, &argc, &argv) != TCL_OK) {
907         return TCL_ERROR;
908     }
909     if (argc == 0) {
910         ckfree( (char*)argv);
911         for (i = 0; i < 9; i++) {
912             if (butPtr->icons[i]) Tk_FreeImage(butPtr->icons[i]);
913             butPtr->icons[i] = NULL;
914         }
915         if (butPtr->iconStr) {
916             ckfree(butPtr->iconStr);
917         }
918         butPtr->iconStr = NULL;
919         butPtr->iconCnt = 0;
920         return TCL_OK;
921     }
922     if (argc <2 || argc >9) {
923         ckfree( (char*)argv);
924         Tcl_AppendResult(interp, "expected 0, or 2-9 icons", 0);
925         return TCL_ERROR;
926     }
927     for (i = 0; i < 9; i++) {
928         imgs[i] = NULL;
929     }
930     for (i = 0; i < argc; i++) {
931         image = Tk_GetImage(interp, butPtr->tkwin, argv[i], ButtonImageProc, (ClientData)butPtr);
932         if (image == NULL) {
933             for (i = i-1; i >=0; i--) {
934                 Tk_FreeImage(imgs[i]);
935             }
936             ckfree( (char*)argv);
937             return TCL_ERROR;
938         }
939         imgs[i] = image;
940     }
941     ckfree( (char*)argv);
942     butPtr->iconCnt = argc;
943     for (i = 0; i < 9; i++) {
944         if (butPtr->icons[i]) {  Tk_FreeImage(butPtr->icons[i]); }
945         butPtr->icons[i] = imgs[i];
946     }
947     if (butPtr->iconStr) {
948         ckfree(butPtr->iconStr);
949     }
950     butPtr->iconStr = strdup(string);
951     return TCL_OK;
952 }
953 
954 static void
ComputeAnchor(Tk_Anchor anchor,Tk_Window tkwin,int padX,int padY,int innerWidth,int innerHeight,int * xPtr,int * yPtr,int xAdj,int yAdj)955 ComputeAnchor( Tk_Anchor anchor, Tk_Window tkwin, int padX, int padY,
956     int innerWidth, int innerHeight, int *xPtr, int *yPtr, int xAdj, int yAdj)
957 {
958     int width, height;
959 
960     width = Tk_Width(tkwin)-xAdj;
961     height = Tk_Height(tkwin)-yAdj;
962     /*
963      * Handle the horizontal parts.
964      */
965 
966     switch (anchor) {
967     case TK_ANCHOR_NW:
968     case TK_ANCHOR_W:
969     case TK_ANCHOR_SW:
970 	*xPtr = Tk_InternalBorderLeft(tkwin) + padX;
971 	break;
972 
973     case TK_ANCHOR_N:
974     case TK_ANCHOR_CENTER:
975     case TK_ANCHOR_S:
976 	*xPtr = (width - innerWidth - Tk_InternalBorderLeft(tkwin) -
977 		Tk_InternalBorderRight(tkwin)) / 2 +
978 		Tk_InternalBorderLeft(tkwin);
979 	break;
980 
981     default:
982 	*xPtr = width - Tk_InternalBorderRight(tkwin) - padX
983 		- innerWidth;
984 	break;
985     }
986 
987     /*
988      * Handle the vertical parts.
989      */
990 
991     switch (anchor) {
992     case TK_ANCHOR_NW:
993     case TK_ANCHOR_N:
994     case TK_ANCHOR_NE:
995 	*yPtr = Tk_InternalBorderTop(tkwin) + padY;
996 	break;
997 
998     case TK_ANCHOR_W:
999     case TK_ANCHOR_CENTER:
1000     case TK_ANCHOR_E:
1001 	*yPtr = (height - innerHeight- Tk_InternalBorderTop(tkwin) -
1002 		Tk_InternalBorderBottom(tkwin)) / 2 +
1003 		Tk_InternalBorderTop(tkwin);
1004 	break;
1005 
1006     default:
1007 	*yPtr = height - Tk_InternalBorderBottom(tkwin) - padY
1008 		- innerHeight;
1009 	break;
1010     }
1011 }
1012 
1013 /*
1014  *--------------------------------------------------------------
1015  *
1016  * Tk_ButtonCmd, Tk_CheckbuttonCmd, Tk_LabelCmd, Tk_RadiobuttonCmd --
1017  *
1018  *	These procedures are invoked to process the "button", "label",
1019  *	"radiobutton", and "checkbutton" Tcl commands.  See the
1020  *	user documentation for details on what they do.
1021  *
1022  * Results:
1023  *	A standard Tcl result.
1024  *
1025  * Side effects:
1026  *	See the user documentation.  These procedures are just wrappers;
1027  *	they call ButtonCreate to do all of the real work.
1028  *
1029  *--------------------------------------------------------------
1030  */
1031 
1032 static int
ButtonCmd(clientData,interp,argc,argv)1033 ButtonCmd(clientData, interp, argc, argv)
1034     ClientData clientData;	/* Main window associated with
1035 				 * interpreter. */
1036     Tcl_Interp *interp;		/* Current interpreter. */
1037     int argc;			/* Number of arguments. */
1038     char **argv;		/* Argument strings. */
1039 {
1040     return ButtonCreate(clientData, interp, argc, argv, TYPE_BUTTON);
1041 }
1042 
1043 static int
CheckbuttonCmd(clientData,interp,argc,argv)1044 CheckbuttonCmd(clientData, interp, argc, argv)
1045     ClientData clientData;	/* Main window associated with
1046 				 * interpreter. */
1047     Tcl_Interp *interp;		/* Current interpreter. */
1048     int argc;			/* Number of arguments. */
1049     char **argv;		/* Argument strings. */
1050 {
1051     return ButtonCreate(clientData, interp, argc, argv, TYPE_CHECK_BUTTON);
1052 }
1053 
1054 static int
LabelCmd(clientData,interp,argc,argv)1055 LabelCmd(clientData, interp, argc, argv)
1056     ClientData clientData;	/* Main window associated with
1057 				 * interpreter. */
1058     Tcl_Interp *interp;		/* Current interpreter. */
1059     int argc;			/* Number of arguments. */
1060     char **argv;		/* Argument strings. */
1061 {
1062     return ButtonCreate(clientData, interp, argc, argv, TYPE_LABEL);
1063 }
1064 
1065 static int
BFrameCmd(clientData,interp,argc,argv)1066 BFrameCmd(clientData, interp, argc, argv)
1067     ClientData clientData;	/* Main window associated with
1068 				 * interpreter. */
1069     Tcl_Interp *interp;		/* Current interpreter. */
1070     int argc;			/* Number of arguments. */
1071     char **argv;		/* Argument strings. */
1072 {
1073     return ButtonCreate(clientData, interp, argc, argv, TYPE_FRAME+BBOFS);
1074 }
1075 
1076 static int
FrameCmd(clientData,interp,argc,argv)1077 FrameCmd(clientData, interp, argc, argv)
1078     ClientData clientData;	/* Main window associated with
1079 				 * interpreter. */
1080     Tcl_Interp *interp;		/* Current interpreter. */
1081     int argc;			/* Number of arguments. */
1082     char **argv;		/* Argument strings. */
1083 {
1084     return ButtonCreate(clientData, interp, argc, argv, TYPE_FRAME);
1085 }
1086 
1087 static int
MenubuttonCmd(clientData,interp,argc,argv)1088 MenubuttonCmd(clientData, interp, argc, argv)
1089     ClientData clientData;	/* Main window associated with
1090 				 * interpreter. */
1091     Tcl_Interp *interp;		/* Current interpreter. */
1092     int argc;			/* Number of arguments. */
1093     char **argv;		/* Argument strings. */
1094 {
1095     return ButtonCreate(clientData, interp, argc, argv, TYPE_MENU_BUTTON);
1096 }
1097 
1098 static int
RadiobuttonCmd(clientData,interp,argc,argv)1099 RadiobuttonCmd(clientData, interp, argc, argv)
1100     ClientData clientData;	/* Main window associated with
1101 				 * interpreter. */
1102     Tcl_Interp *interp;		/* Current interpreter. */
1103     int argc;			/* Number of arguments. */
1104     char **argv;		/* Argument strings. */
1105 {
1106     return ButtonCreate(clientData, interp, argc, argv, TYPE_RADIO_BUTTON);
1107 }
1108 static int
BButtonCmd(clientData,interp,argc,argv)1109 BButtonCmd(clientData, interp, argc, argv)
1110     ClientData clientData;	/* Main window associated with
1111 				 * interpreter. */
1112     Tcl_Interp *interp;		/* Current interpreter. */
1113     int argc;			/* Number of arguments. */
1114     char **argv;		/* Argument strings. */
1115 {
1116     return ButtonCreate(clientData, interp, argc, argv, TYPE_BUTTON+BBOFS);
1117 }
1118 
1119 static int
BCheckbuttonCmd(clientData,interp,argc,argv)1120 BCheckbuttonCmd(clientData, interp, argc, argv)
1121     ClientData clientData;	/* Main window associated with
1122 				 * interpreter. */
1123     Tcl_Interp *interp;		/* Current interpreter. */
1124     int argc;			/* Number of arguments. */
1125     char **argv;		/* Argument strings. */
1126 {
1127     return ButtonCreate(clientData, interp, argc, argv, TYPE_CHECK_BUTTON+BBOFS);
1128 }
1129 
1130 static int
BLabelCmd(clientData,interp,argc,argv)1131 BLabelCmd(clientData, interp, argc, argv)
1132     ClientData clientData;	/* Main window associated with
1133 				 * interpreter. */
1134     Tcl_Interp *interp;		/* Current interpreter. */
1135     int argc;			/* Number of arguments. */
1136     char **argv;		/* Argument strings. */
1137 {
1138     return ButtonCreate(clientData, interp, argc, argv, TYPE_LABEL+BBOFS);
1139 }
1140 
1141 static int
BMenubuttonCmd(clientData,interp,argc,argv)1142 BMenubuttonCmd(clientData, interp, argc, argv)
1143     ClientData clientData;	/* Main window associated with
1144 				 * interpreter. */
1145     Tcl_Interp *interp;		/* Current interpreter. */
1146     int argc;			/* Number of arguments. */
1147     char **argv;		/* Argument strings. */
1148 {
1149     return ButtonCreate(clientData, interp, argc, argv, TYPE_MENU_BUTTON+BBOFS);
1150 }
1151 
1152 static int
BRadiobuttonCmd(clientData,interp,argc,argv)1153 BRadiobuttonCmd(clientData, interp, argc, argv)
1154     ClientData clientData;	/* Main window associated with
1155 				 * interpreter. */
1156     Tcl_Interp *interp;		/* Current interpreter. */
1157     int argc;			/* Number of arguments. */
1158     char **argv;		/* Argument strings. */
1159 {
1160     return ButtonCreate(clientData, interp, argc, argv, TYPE_RADIO_BUTTON+BBOFS);
1161 }
1162 
1163 /*
1164  *--------------------------------------------------------------
1165  *
1166  * ButtonCreate --
1167  *
1168  *	This procedure does all the real work of implementing the
1169  *	"button", "label", "radiobutton", and "checkbutton" Tcl
1170  *	commands.  See the user documentation for details on what it does.
1171  *
1172  * Results:
1173  *	A standard Tcl result.
1174  *
1175  * Side effects:
1176  *	See the user documentation.
1177  *
1178  *--------------------------------------------------------------
1179  */
1180 
1181 /*ARGSUSED*/
1182 static int
ButtonCreate(clientData,interp,argc,argv,type)1183 ButtonCreate(clientData, interp, argc, argv, type)
1184     ClientData clientData;	/* Main window associated with
1185 				 * interpreter. */
1186     Tcl_Interp *interp;		/* Current interpreter. */
1187     int argc;			/* Number of arguments. */
1188     char **argv;		/* Argument strings. */
1189     int type;			/* Type of button to create: TYPE_LABEL,
1190 				 * TYPE_BUTTON, TYPE_CHECK_BUTTON, or
1191 				 * TYPE_RADIO_BUTTON. */
1192 {
1193     register Button *butPtr;
1194     Tk_Window tkwin;
1195     int oType = type;
1196     char *className;
1197 
1198     if (type >= BBOFS) type -= BBOFS;
1199     if (argc < 2) {
1200 	Tcl_AppendResult(interp, "wrong # args: should be \"",
1201 	    argv[0], " pathName ?options?\"", (char *)NULL);
1202 	return TCL_ERROR;
1203     }
1204     /*
1205      * Create the new window.
1206      */
1207 
1208     tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
1209 	(char *)NULL);
1210     if (tkwin == NULL) {
1211 	return TCL_ERROR;
1212     }
1213     /*
1214      * Initialize the data structure for the button.
1215      */
1216 
1217     butPtr = Blt_Calloc(1, sizeof(Button));
1218     butPtr->tkwin = tkwin;
1219     butPtr->display = Tk_Display(tkwin);
1220     butPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin),
1221 	ButtonWidgetCmd, butPtr, ButtonCmdDeletedProc);
1222 #ifdef ITCL_NAMESPACES
1223     Itk_SetWidgetCommand(butPtr->tkwin, butPtr->widgetCmd);
1224 #endif /* ITCL_NAMESPACES */
1225 
1226     /* Since calloc was used, could skip most of the following ... */
1227     butPtr->interp = interp;
1228     butPtr->type = type;
1229     butPtr->textPtr = NULL;
1230     butPtr->numChars = 0;
1231     butPtr->underline = -1;
1232     butPtr->textVarName = NULL;
1233     butPtr->bitmap = None;
1234     butPtr->imageString = NULL;
1235     butPtr->image = NULL;
1236     butPtr->tristateImageString = NULL;
1237     butPtr->selectImageString = NULL;
1238     butPtr->selectImage = NULL;
1239     butPtr->state = STATE_NORMAL;
1240     butPtr->normalBorder = NULL;
1241     butPtr->activeBorder = NULL;
1242     butPtr->borderWidth = 0;
1243     butPtr->relief = TK_RELIEF_FLAT;
1244     butPtr->highlightWidth = 0;
1245     butPtr->highlightBgColorPtr = NULL;
1246     butPtr->highlightColorPtr = NULL;
1247     butPtr->inset = 0;
1248     butPtr->tkfont = NULL;
1249     butPtr->normalFg = NULL;
1250     butPtr->activeFg = NULL;
1251     butPtr->disabledFg = NULL;
1252     butPtr->normalTextGC = None;
1253     butPtr->activeTextGC = None;
1254     /*butPtr->gray = None;*/
1255     butPtr->disabledGC = None;
1256     butPtr->copyGC = None;
1257     butPtr->widthString = NULL;
1258     butPtr->heightString = NULL;
1259     butPtr->width = 0;
1260     butPtr->height = 0;
1261     butPtr->wrapLength = 0;
1262     butPtr->padX = 0;
1263     butPtr->padY = 0;
1264     butPtr->anchor = TK_ANCHOR_CENTER;
1265     butPtr->justify = TK_JUSTIFY_CENTER;
1266 #if (TK_MAJOR_VERSION > 4)
1267     butPtr->textLayout = NULL;
1268 #endif
1269     butPtr->indicatorOn = 0;
1270     butPtr->selectBorder = NULL;
1271     butPtr->indicatorSpace = 0;
1272     butPtr->indicatorDiameter = 0;
1273     butPtr->selVarName = NULL;
1274     butPtr->onValue = NULL;
1275     butPtr->offValue = NULL;
1276     butPtr->cursor = None;
1277     butPtr->command = NULL;
1278     butPtr->takeFocus = NULL;
1279     butPtr->flags = 0;
1280     butPtr->tile = butPtr->activeTile = butPtr->innerTile = butPtr->disabledTile = NULL;
1281     butPtr->defaultState = STATE_DISABLED;
1282     butPtr->compound = 0;
1283     butPtr->repeatDelay = 0;
1284     butPtr->overRelief = TK_RELIEF_RAISED;
1285 
1286 
1287     className = classNames[oType];
1288     if (type == TYPE_FRAME) {
1289         int length, i;
1290         for (i = 2; i < argc; i += 2) {
1291             length = strlen(argv[i]);
1292             if (length < 2) {
1293                 continue;
1294             }
1295             if ((argv[i][1] == 'c') && (length >= 3)
1296                 && (strncmp(argv[i], "-class", (unsigned) length) == 0)) {
1297                 className = argv[i+1];
1298             }
1299         }
1300     }
1301 
1302 
1303     Tk_SetClass(tkwin, className);
1304     Tk_SetClassProcs(tkwin, &butClass, (ClientData)butPtr);
1305     Tk_CreateEventHandler(butPtr->tkwin,
1306 	ExposureMask | StructureNotifyMask | FocusChangeMask,
1307 	ButtonEventProc, butPtr);
1308     if (ConfigureButton(interp, butPtr, argc - 2, argv + 2,
1309 	    configFlags[type]) != TCL_OK) {
1310 	Tk_DestroyWindow(butPtr->tkwin);
1311 	return TCL_ERROR;
1312     }
1313     Tcl_SetResult(interp, Tk_PathName(butPtr->tkwin), TCL_VOLATILE);
1314     return TCL_OK;
1315 }
1316 
ButtonGetValue(Button * butPtr)1317 static char *ButtonGetValue(Button *butPtr) {
1318     Blt_TreeNode nodePtr;
1319     Tcl_Obj *valuePtr;
1320     CONST char *value;
1321     Tcl_Interp *interp = butPtr->interp;
1322 
1323     value = NULL;
1324 
1325     if (butPtr->tree == NULL) {
1326         value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
1327     } else {
1328         nodePtr = Blt_TreeGetNode(butPtr->tree, butPtr->node);
1329         if (nodePtr == NULL) {
1330             nodePtr = Blt_TreeCreateNode(butPtr->tree, Blt_TreeGetNode(butPtr->tree, 0), NULL, -1);
1331         }
1332         if (nodePtr == NULL) {
1333             return NULL;
1334         }
1335         if (Blt_TreeGetValue(NULL, butPtr->tree,
1336             nodePtr, butPtr->selVarName, &valuePtr) == TCL_OK && valuePtr != NULL) {
1337                 value = Tcl_GetString(valuePtr);
1338         }
1339     }
1340     return value;
1341 }
1342 
ButtonSetValue(Button * butPtr,char * value,int warn)1343 static int ButtonSetValue(Button *butPtr, char *value, int warn) {
1344     Blt_TreeNode nodePtr;
1345     Tcl_Obj *valuePtr;
1346     Tcl_Interp *interp = butPtr->interp;
1347 
1348     if (butPtr->tree == NULL) {
1349         if (Tcl_SetVar(interp, butPtr->selVarName, value,
1350             TCL_GLOBAL_ONLY | (warn?TCL_LEAVE_ERR_MSG:0)) == NULL) {
1351             return TCL_ERROR;
1352         }
1353         return TCL_OK;
1354     }
1355     nodePtr = Blt_TreeGetNode(butPtr->tree, butPtr->node);
1356     if (nodePtr == NULL) {
1357         nodePtr = Blt_TreeCreateNode(butPtr->tree, Blt_TreeGetNode(butPtr->tree, 0), NULL, -1);
1358     }
1359     if (nodePtr == NULL) {
1360         return TCL_ERROR;
1361     }
1362     valuePtr = Tcl_NewStringObj(value, -1);
1363     return Blt_TreeSetValue(interp, butPtr->tree,
1364         nodePtr, butPtr->selVarName, valuePtr);
1365 }
1366 
1367 
1368 /*
1369  *--------------------------------------------------------------
1370  *
1371  * ButtonWidgetCmd --
1372  *
1373  *	This procedure is invoked to process the Tcl command
1374  *	that corresponds to a widget managed by this module.
1375  *	See the user documentation for details on what it does.
1376  *
1377  * Results:
1378  *	A standard Tcl result.
1379  *
1380  * Side effects:
1381  *	See the user documentation.
1382  *
1383  *--------------------------------------------------------------
1384  */
1385 
1386 static int
ButtonWidgetCmd(clientData,interp,argc,argv)1387 ButtonWidgetCmd(clientData, interp, argc, argv)
1388     ClientData clientData;	/* Information about button widget. */
1389     Tcl_Interp *interp;		/* Current interpreter. */
1390     int argc;			/* Number of arguments. */
1391     char **argv;		/* Argument strings. */
1392 {
1393     register Button *butPtr = clientData;
1394     int result = TCL_OK;
1395     size_t length;
1396     int c;
1397 
1398     if ((butPtr->flags & BUTTON_DELETED)) {
1399         return TCL_OK;
1400     }
1401 
1402     if (argc < 2) {
1403 	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1404 	    " option ?arg arg ...?\"", (char *)NULL);
1405 	return TCL_ERROR;
1406     }
1407     Tcl_Preserve(butPtr);
1408     c = argv[1][0];
1409     length = strlen(argv[1]);
1410     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
1411 	&& (length >= 2)) {
1412 	if (argc != 3) {
1413 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
1414 		argv[0], " cget option\"",
1415 		(char *)NULL);
1416 	    goto error;
1417 	}
1418 	result = Tk_ConfigureValue(interp, butPtr->tkwin, configSpecs,
1419 	    (char *)butPtr, argv[2], configFlags[butPtr->type]);
1420     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
1421 	&& (length >= 2)) {
1422 	if (argc == 2) {
1423 	    result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
1424 		(char *)butPtr, (char *)NULL, configFlags[butPtr->type]);
1425 	} else if (argc == 3) {
1426 	    result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
1427 		(char *)butPtr, argv[2],
1428 		configFlags[butPtr->type]);
1429 	} else {
1430 	    result = ConfigureButton(interp, butPtr, argc - 2, argv + 2,
1431 		configFlags[butPtr->type] | TK_CONFIG_ARGV_ONLY);
1432 	}
1433     } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
1434 	&& (butPtr->type >= TYPE_CHECK_BUTTON) && (butPtr->type < TYPE_MENU_BUTTON)) {
1435 	if (argc > 2) {
1436 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1437 		" deselect\"", (char *)NULL);
1438 	    goto error;
1439 	}
1440 	if (butPtr->type == TYPE_CHECK_BUTTON) {
1441 	    result = ButtonSetValue(butPtr, butPtr->offValue, 1);
1442 	} else if (butPtr->flags & SELECTED) {
1443 	    result = ButtonSetValue(butPtr, "", 1);
1444 	}
1445     } else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0)
1446 	&& (butPtr->type > TYPE_LABEL)) {
1447 	int i;
1448 
1449 	if (argc > 2) {
1450 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1451 		" flash\"", (char *)NULL);
1452 	    goto error;
1453 	}
1454 	if (butPtr->state != STATE_DISABLED) {
1455 	    for (i = 0; i < 4; i++) {
1456 		butPtr->state = (butPtr->state == STATE_NORMAL)
1457 		    ? STATE_ACTIVE : STATE_NORMAL;
1458 		Tk_SetBackgroundFromBorder(butPtr->tkwin,
1459 		    (butPtr->state == STATE_ACTIVE && butPtr->activeBorder) ? butPtr->activeBorder
1460 		    : butPtr->normalBorder);
1461 		DisplayButton(butPtr);
1462 
1463 		/*
1464 		 * Special note: must cancel any existing idle handler
1465 		 * for DisplayButton;  it's no longer needed, and DisplayButton
1466 		 * cleared the REDRAW_PENDING flag.
1467 		 */
1468 
1469 		Tcl_CancelIdleCall(DisplayButton, butPtr);
1470 #ifndef WIN32
1471 		XFlush(butPtr->display);
1472 #endif
1473 		Tcl_Sleep(50);
1474 	    }
1475 	}
1476     } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
1477 	&& (butPtr->type > TYPE_LABEL)) {
1478 	if (argc > 2) {
1479 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
1480 		" invoke\"", (char *)NULL);
1481 	    goto error;
1482 	}
1483 	if (butPtr->state != STATE_DISABLED) {
1484 	    result = InvokeButton(butPtr);
1485 	}
1486     } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
1487 	&& (butPtr->type >= TYPE_CHECK_BUTTON) && (butPtr->type < TYPE_MENU_BUTTON)) {
1488 	if (argc > 2) {
1489 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1490 		" select\"", (char *)NULL);
1491 	    goto error;
1492 	}
1493          result = ButtonSetValue(butPtr,  butPtr->onValue, 1);
1494     } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
1495 	&& (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
1496 	if (argc > 2) {
1497 	    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1498 		" toggle\"", (char *)NULL);
1499 	    goto error;
1500 	}
1501 	if (butPtr->flags & SELECTED) {
1502              result = ButtonSetValue(butPtr,  butPtr->offValue, 0);
1503 	} else {
1504              result = ButtonSetValue(butPtr,  butPtr->onValue, 0);
1505 	}
1506     } else {
1507 	Tcl_AppendResult(interp, "bad option \"", argv[1], "\": must be ",
1508 	    optionStrings[butPtr->type], (char *)NULL);
1509 	goto error;
1510     }
1511     Tcl_Release(butPtr);
1512     return result;
1513 
1514   error:
1515     Tcl_Release(butPtr);
1516     return TCL_ERROR;
1517 }
1518 
1519 /*
1520  *----------------------------------------------------------------------
1521  *
1522  * DestroyButton --
1523  *
1524  *	This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
1525  *	to clean up the internal structure of a button at a safe time
1526  *	(when no-one is using it anymore).
1527  *
1528  * Results:
1529  *	None.
1530  *
1531  * Side effects:
1532  *	Everything associated with the widget is freed up.
1533  *
1534  *----------------------------------------------------------------------
1535  */
1536 
1537 static void
DestroyButton(butPtr)1538 DestroyButton(butPtr)
1539     Button *butPtr;		/* Info about button widget. */
1540 {
1541     int i;
1542     /*
1543      * Free up all the stuff that requires special handling, then
1544      * let Tk_FreeOptions handle all the standard option-related
1545      * stuff.
1546      */
1547     butPtr->flags = BUTTON_DELETED;
1548     if (butPtr->image != NULL) {
1549 	Tk_FreeImage(butPtr->image);
1550     }
1551     if (butPtr->activeImage != NULL) {
1552         Tk_FreeImage(butPtr->activeImage);
1553     }
1554     if (butPtr->disabledImage != NULL) {
1555         Tk_FreeImage(butPtr->disabledImage);
1556     }
1557     if (butPtr->bdImage != NULL) {
1558         Tk_FreeImage(butPtr->bdImage);
1559     }
1560     if (butPtr->activeBdImage != NULL) {
1561         Tk_FreeImage(butPtr->activeBdImage);
1562     }
1563     if (butPtr->disabledBdImage != NULL) {
1564         Tk_FreeImage(butPtr->disabledBdImage);
1565     }
1566     if (butPtr->bdImageSized != NULL) {
1567         Tk_FreeImage(butPtr->bdImageSized);
1568     }
1569     if (butPtr->selectImage != NULL) {
1570 	Tk_FreeImage(butPtr->selectImage);
1571     }
1572     if (butPtr->normalTextGC != None) {
1573 	Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
1574     }
1575     if (butPtr->focusGC != None) {
1576         Blt_FreePrivateGC(butPtr->display, butPtr->focusGC);
1577     }
1578     if (butPtr->activeTextGC != None) {
1579         Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
1580     }
1581     if (butPtr->gray != None) {
1582         Tk_FreeBitmap(butPtr->display, butPtr->gray);
1583     }
1584     if (butPtr->disabledGC != None) {
1585 	Tk_FreeGC(butPtr->display, butPtr->disabledGC);
1586     }
1587     if (butPtr->copyGC != None) {
1588 	Tk_FreeGC(butPtr->display, butPtr->copyGC);
1589     }
1590     if (butPtr->selVarName != NULL && butPtr->tree == NULL) {
1591         Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
1592             TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
1593             ButtonVarProc, (ClientData)butPtr);
1594     }
1595     if (butPtr->textVarName != NULL && butPtr->tree == NULL) {
1596         Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
1597             TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
1598             ButtonTextVarProc, butPtr);
1599     }
1600     if (butPtr->selTrace != NULL) {
1601         Blt_TreeDeleteTrace(butPtr->selTrace);
1602     }
1603     if (butPtr->textTrace != NULL) {
1604         Blt_TreeDeleteTrace(butPtr->textTrace);
1605     }
1606     if (butPtr->tree) {
1607         Blt_TreeReleaseToken(butPtr->tree);
1608     }
1609     if (butPtr->inBdTile != NULL) {
1610         Blt_FreeTile(butPtr->inBdTile);
1611     }
1612     if (butPtr->activeTile != NULL) {
1613 	Blt_FreeTile(butPtr->activeTile);
1614     }
1615     if (butPtr->disabledTile != NULL) {
1616         Blt_FreeTile(butPtr->disabledTile);
1617     }
1618     if (butPtr->innerTile != NULL) {
1619         Blt_FreeTile(butPtr->innerTile);
1620     }
1621     if (butPtr->activeInnerTile != NULL) {
1622         Blt_FreeTile(butPtr->activeInnerTile);
1623     }
1624     if (butPtr->disabledInnerTile != NULL) {
1625         Blt_FreeTile(butPtr->disabledInnerTile);
1626     }
1627     if (butPtr->tile != NULL) {
1628 	Blt_FreeTile(butPtr->tile);
1629     }
1630     if (butPtr->shadow.color != NULL) {
1631         Tk_FreeColor(butPtr->shadow.color);
1632     }
1633     if (butPtr->activeShadow.color != NULL) {
1634         Tk_FreeColor(butPtr->activeShadow.color);
1635     }
1636 
1637     for (i = 0; i < 9; i++) {
1638         if (butPtr->icons[i]) Tk_FreeImage(butPtr->icons[i]);
1639         butPtr->icons[i] = NULL;
1640     }
1641 #if (TK_MAJOR_VERSION > 4)
1642     if (butPtr->textLayout) {
1643         Tk_FreeTextLayout(butPtr->textLayout);
1644     }
1645 #endif
1646     Tk_FreeOptions(configSpecs, (char *)butPtr, butPtr->display,
1647 	configFlags[butPtr->type]);
1648     Tcl_EventuallyFree((ClientData)butPtr, TCL_DYNAMIC);
1649 }
1650 
1651 /*ARGSUSED*/
1652 static void
TileChangedProc(clientData,tile)1653 TileChangedProc(clientData, tile)
1654     ClientData clientData;
1655     Blt_Tile tile;		/* Not used. */
1656 {
1657     Button *butPtr = clientData;
1658 
1659     if (butPtr->tkwin != NULL) {
1660 	/*
1661 	 * Arrange for the button to be redisplayed.
1662 	 */
1663 	if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
1664 	    Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
1665 	    butPtr->flags |= REDRAW_PENDING;
1666 	}
1667     }
1668 }
1669 
1670 /*
1671  *----------------------------------------------------------------------
1672  *
1673  * ConfigureButton --
1674  *
1675  *	This procedure is called to process an argv/argc list, plus
1676  *	the Tk option database, in order to configure (or
1677  *	reconfigure) a button widget.
1678  *
1679  * Results:
1680  *	The return value is a standard Tcl result.  If TCL_ERROR is
1681  *	returned, then interp->result contains an error message.
1682  *
1683  * Side effects:
1684  *	Configuration information, such as text string, colors, font,
1685  *	etc. get set for butPtr;  old resources get freed, if there
1686  *	were any.  The button is redisplayed.
1687  *
1688  *----------------------------------------------------------------------
1689  */
1690 
1691 static int
ConfigureButton(interp,butPtr,argc,argv,flags)1692 ConfigureButton(interp, butPtr, argc, argv, flags)
1693     Tcl_Interp *interp;		/* Used for error reporting. */
1694     register Button *butPtr;	/* Information about widget;  may or may
1695 				 * not already have values for some fields. */
1696     int argc;			/* Number of valid entries in argv. */
1697     char **argv;		/* Arguments. */
1698     int flags;			/* Flags to pass to Tk_ConfigureWidget. */
1699 {
1700     XGCValues gcValues;
1701     GC newGC;
1702     unsigned long mask;
1703     Tk_Image image;
1704     char *oldTextVar, *oldSelVar;
1705     Blt_Tree oldTree;
1706     int oldNode, result = TCL_OK;
1707     char * oldABdStr = butPtr->activeBdImageString;
1708     char * oldDBStr = butPtr->disabledBdImageString;
1709     char * oldBDStr = butPtr->bdImageString;
1710     int oldState = butPtr->state, fmask;
1711     Tk_FakeWin *winPtr;
1712 
1713     winPtr = (Tk_FakeWin *) (butPtr->tkwin);
1714     oldTree = butPtr->tree;
1715     oldNode = butPtr->node;
1716 
1717     if (oldTree == NULL) {
1718         oldTextVar = (butPtr->textVarName?strdup(butPtr->textVarName):NULL);
1719         oldSelVar = (butPtr->selVarName?strdup(butPtr->selVarName):NULL);
1720     }
1721 
1722     if (Tk_ConfigureWidget(interp, butPtr->tkwin, configSpecs,
1723 	    argc, argv, (char *)butPtr, flags) != TCL_OK) {
1724         if (oldTextVar) ckfree(oldTextVar);
1725         if (oldTextVar) ckfree(oldSelVar);
1726 	return TCL_ERROR;
1727     }
1728     /*
1729      * Eliminate any existing trace on variables monitored by the button.
1730      */
1731 
1732      if (oldTree == NULL) {
1733          if (oldTextVar != NULL) {
1734              Tcl_UntraceVar(interp, oldTextVar,
1735                  TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
1736                  ButtonTextVarProc, (ClientData)butPtr);
1737                  ckfree(oldTextVar);
1738          }
1739          if (oldSelVar != NULL) {
1740              Tcl_UntraceVar(interp, oldSelVar,
1741                  TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
1742                  ButtonVarProc, (ClientData)butPtr);
1743                  ckfree(oldSelVar);
1744          }
1745      } else {
1746          if (butPtr->textTrace != NULL) {
1747              Blt_TreeDeleteTrace(butPtr->textTrace);
1748              butPtr->textTrace = NULL;
1749          }
1750          if (butPtr->selTrace != NULL) {
1751              Blt_TreeDeleteTrace(butPtr->selTrace);
1752              butPtr->selTrace = NULL;
1753          }
1754 
1755 
1756      }
1757     /*
1758      * A few options need special processing, such as setting the
1759      * background from a 3-D border, or filling in complicated
1760      * defaults that couldn't be specified to Tk_ConfigureWidget.
1761      */
1762 
1763 
1764     if (butPtr->bgTileTop) {
1765         winPtr->flags |= TK_BGTILE_TOP;
1766     } else {
1767         winPtr->flags &= ~TK_BGTILE_TOP;
1768     }
1769 
1770     if ((butPtr->state == STATE_ACTIVE) && !Tk_StrictMotif(butPtr->tkwin)) {
1771         Tk_SetBackgroundFromBorder(butPtr->tkwin, CHOOSE(butPtr->normalBorder,butPtr->activeBorder));
1772     }
1773 
1774     if (butPtr->highlightWidth < 0) {
1775 	butPtr->highlightWidth = 0;
1776     }
1777     fmask = 0;
1778     if (butPtr->tkfont) {
1779         fmask = (GCForeground | GCBackground | GCFont);
1780         gcValues.font = Tk_FontId(butPtr->tkfont);
1781         gcValues.foreground = butPtr->normalFg->pixel;
1782         gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
1783     }
1784     if (butPtr->activeTile != NULL) {
1785 	Blt_SetTileChangedProc(butPtr->activeTile, TileChangedProc,
1786 		(ClientData)butPtr);
1787     }
1788     if (butPtr->disabledTile != NULL) {
1789         Blt_SetTileChangedProc(butPtr->disabledTile, TileChangedProc,
1790             (ClientData)butPtr);
1791     }
1792     if (butPtr->innerTile != NULL) {
1793         Blt_SetTileChangedProc(butPtr->innerTile, TileChangedProc,
1794             (ClientData)butPtr);
1795     }
1796     if (butPtr->activeInnerTile != NULL) {
1797         Blt_SetTileChangedProc(butPtr->activeInnerTile, TileChangedProc,
1798             (ClientData)butPtr);
1799     }
1800     if (butPtr->disabledInnerTile != NULL) {
1801         Blt_SetTileChangedProc(butPtr->disabledInnerTile, TileChangedProc,
1802             (ClientData)butPtr);
1803     }
1804     if (butPtr->tile != NULL) {
1805         Blt_SetTileChangedProc(butPtr->tile, TileChangedProc,
1806             (ClientData)butPtr);
1807     }
1808     /*
1809      * Note: GraphicsExpose events are disabled in normalTextGC because it's
1810      * used to copy stuff from an off-screen pixmap onto the screen (we know
1811      * that there's no problem with obscured areas).
1812      */
1813     gcValues.graphics_exposures = False;
1814     newGC = Tk_GetGC(butPtr->tkwin,
1815 	fmask | GCGraphicsExposures,
1816 	&gcValues);
1817     if (butPtr->normalTextGC != None) {
1818 	Tk_FreeGC(butPtr->display, butPtr->normalTextGC);
1819     }
1820     butPtr->normalTextGC = newGC;
1821 
1822     mask = (GCForeground | GCLineWidth );
1823     gcValues.line_width = 1;
1824     if (LineIsDashed(butPtr->dashes)) {
1825         mask |= GCLineStyle;
1826         gcValues.line_style = LineOnOffDash;
1827     }
1828     newGC = Blt_GetPrivateGC(butPtr->tkwin, mask, &gcValues);
1829     if (LineIsDashed(butPtr->dashes)) {
1830         butPtr->dashes.offset = 2;
1831         Blt_SetDashes(butPtr->display, newGC, &(butPtr->dashes));
1832     }
1833     if (butPtr->focusGC != None) {
1834         Blt_FreePrivateGC(butPtr->display, butPtr->focusGC);
1835     }
1836     butPtr->focusGC = newGC;
1837 
1838     if (butPtr->activeFg != NULL && butPtr->tkfont) {
1839 	gcValues.font = Tk_FontId(butPtr->tkfont);
1840 	gcValues.foreground = butPtr->activeFg->pixel;
1841          gcValues.background = Tk_3DBorderColor(CHOOSE(butPtr->normalBorder,butPtr->activeBorder))->pixel;
1842 	newGC = Tk_GetGC(butPtr->tkwin,
1843 	    fmask, &gcValues);
1844 	if (butPtr->activeTextGC != None) {
1845 	    Tk_FreeGC(butPtr->display, butPtr->activeTextGC);
1846 	}
1847 	butPtr->activeTextGC = newGC;
1848     }
1849 
1850     if (butPtr->type > TYPE_LABEL) {
1851 	gcValues.font = Tk_FontId(butPtr->tkfont);
1852 	gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
1853          if ((butPtr->disabledFg != NULL) && ((butPtr->imageString == NULL) || butPtr->compound != COMPOUND_NONE)) {
1854 	    gcValues.foreground = butPtr->disabledFg->pixel;
1855 	    mask = GCForeground | GCBackground | GCFont;
1856          } else {
1857              gcValues.foreground = gcValues.background;
1858              mask = GCForeground;
1859              if (butPtr->gray == None) {
1860                  butPtr->gray = Tk_GetBitmap(NULL, butPtr->tkwin, "gray50");
1861              }
1862              if (butPtr->gray != None) {
1863                  gcValues.fill_style = FillStippled;
1864                  gcValues.stipple = butPtr->gray;
1865                  mask |= GCFillStyle | GCStipple;
1866              }
1867         }
1868 	newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
1869 	if (butPtr->disabledGC != None) {
1870 	    Tk_FreeGC(butPtr->display, butPtr->disabledGC);
1871 	}
1872 	butPtr->disabledGC = newGC;
1873     }
1874     if (butPtr->copyGC == None) {
1875 	butPtr->copyGC = Tk_GetGC(butPtr->tkwin, 0, &gcValues);
1876     }
1877     if (butPtr->padX < 0) {
1878 	butPtr->padX = 0;
1879     }
1880     if (butPtr->padY < 0) {
1881 	butPtr->padY = 0;
1882     }
1883     if (butPtr->tree && butPtr->textVarName != NULL && strchr(butPtr->textVarName,'(') != NULL) {
1884         Tcl_AppendResult(interp, "can not use array", 0);
1885         ckfree(butPtr->textVarName);
1886         butPtr->textVarName = NULL;
1887         result = TCL_ERROR;
1888     }
1889 
1890     if (butPtr->type >= TYPE_CHECK_BUTTON && (butPtr->type < TYPE_MENU_BUTTON)) {
1891 	CONST char *value;
1892 
1893          if (butPtr->tree && butPtr->selVarName != NULL && strchr(butPtr->selVarName,'(') != NULL) {
1894              Tcl_AppendResult(interp, "can not use array", 0);
1895              ckfree(butPtr->selVarName);
1896              butPtr->selVarName = Blt_Malloc(strlen(Tk_Name(butPtr->tkwin)) + 1);
1897              strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin));
1898              return TCL_ERROR;
1899          }
1900 
1901 	if (butPtr->selVarName == NULL) {
1902 	    butPtr->selVarName = Blt_Malloc(strlen(Tk_Name(butPtr->tkwin)) + 1);
1903 	    strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin));
1904 	}
1905 	/*
1906 	 * Select the button if the associated variable has the
1907 	 * appropriate value, initialize the variable if it doesn't
1908 	 * exist, then set a trace on the variable to monitor future
1909 	 * changes to its value.
1910 	 */
1911 
1912         butPtr->flags &= ~SELECTED;
1913         if (butPtr->tree == NULL) {
1914             value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
1915             if (value != NULL) {
1916                 if (strcmp(value, butPtr->onValue) == 0) {
1917                     butPtr->flags |= SELECTED;
1918                 }
1919             } else {
1920                 if (Tcl_SetVar(interp, butPtr->selVarName,
1921                     (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
1922                     TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
1923                         return TCL_ERROR;
1924                 }
1925             }
1926             Tcl_TraceVar(interp, butPtr->selVarName,
1927                 TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
1928                 ButtonVarProc, (ClientData)butPtr);
1929         } else {
1930             Tcl_Obj *valuePtr;
1931             Blt_TreeNode nodePtr;
1932 
1933             nodePtr = Blt_TreeGetNode(butPtr->tree, butPtr->node);
1934             if (nodePtr == NULL) {
1935                 nodePtr = Blt_TreeCreateNode(butPtr->tree, Blt_TreeGetNode(butPtr->tree, 0), NULL, -1);
1936             }
1937             if (nodePtr == NULL) {
1938                 return TCL_ERROR;
1939             }
1940             if (Blt_TreeGetValue(NULL, butPtr->tree,
1941                 nodePtr, butPtr->selVarName, &valuePtr) == TCL_OK) {
1942             } else {
1943                 valuePtr = Tcl_NewStringObj(((butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : ""),-1);
1944                 Tcl_IncrRefCount(valuePtr);
1945                 if (Blt_TreeSetValue(interp, butPtr->tree,
1946                     nodePtr, butPtr->selVarName, valuePtr) != TCL_OK) {
1947                     Tcl_DecrRefCount(valuePtr);
1948                     return TCL_ERROR;
1949                 }
1950                 Tcl_DecrRefCount(valuePtr);
1951             }
1952             value = (valuePtr?Tcl_GetString(valuePtr):"");
1953             if (strcmp(value, butPtr->onValue) == 0) {
1954                 butPtr->flags |= SELECTED;
1955             }
1956             butPtr->selTrace = Blt_TreeCreateTrace(butPtr->tree,
1957                 nodePtr, butPtr->selVarName, NULL,
1958                 TREE_TRACE_UNSET|TREE_TRACE_WRITE, TreeTraceProc, (ClientData)butPtr);
1959         }
1960     }
1961     /*
1962      * Get the images for the widget, if there are any.  Allocate the
1963      * new images before freeing the old ones, so that the reference
1964      * counts don't go to zero and cause image data to be discarded.
1965      */
1966 
1967     if (oldABdStr != butPtr->activeBdImageString ||
1968          oldDBStr != butPtr->disabledBdImageString ||
1969          oldBDStr != butPtr->bdImageString ||
1970          oldState != butPtr->state) {
1971         butPtr->flags |= BUTTON_DIRTYBD;
1972     }
1973 
1974     if (butPtr->imageString != NULL) {
1975 	image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
1976 	    butPtr->imageString, ButtonImageProc, (ClientData)butPtr);
1977 	if (image == NULL) {
1978 	    return TCL_ERROR;
1979 	}
1980     } else {
1981 	image = NULL;
1982     }
1983     if (butPtr->image != NULL) {
1984 	Tk_FreeImage(butPtr->image);
1985     }
1986     butPtr->image = image;
1987 
1988     if (butPtr->disabledImageString != NULL) {
1989         image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
1990             butPtr->disabledImageString, ButtonImageProc, (ClientData)butPtr);
1991             if (image == NULL) {
1992             return TCL_ERROR;
1993         }
1994     } else {
1995         image = NULL;
1996     }
1997     if (butPtr->disabledImage != NULL) {
1998         Tk_FreeImage(butPtr->disabledImage);
1999     }
2000     butPtr->disabledImage = image;
2001 
2002     if (butPtr->activeImageString != NULL) {
2003         image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
2004             butPtr->activeImageString, ButtonImageProc, (ClientData)butPtr);
2005             if (image == NULL) {
2006             return TCL_ERROR;
2007         }
2008     } else {
2009         image = NULL;
2010     }
2011     if (butPtr->activeImage != NULL) {
2012         Tk_FreeImage(butPtr->activeImage);
2013     }
2014     butPtr->activeImage = image;
2015 
2016     if (butPtr->bdImageString != NULL) {
2017         image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
2018             butPtr->bdImageString, ButtonImageBdProc, (ClientData)butPtr);
2019             if (image == NULL) {
2020             return TCL_ERROR;
2021         }
2022     } else {
2023         image = NULL;
2024     }
2025     if (butPtr->bdImage != NULL) {
2026         Tk_FreeImage(butPtr->bdImage);
2027     }
2028     butPtr->bdImage = image;
2029 
2030     if (butPtr->activeBdImageString != NULL) {
2031         image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
2032             butPtr->activeBdImageString, ButtonImageBdProc, (ClientData)butPtr);
2033             if (image == NULL) {
2034             return TCL_ERROR;
2035         }
2036     } else {
2037         image = NULL;
2038     }
2039     if (butPtr->activeBdImage != NULL) {
2040         Tk_FreeImage(butPtr->activeBdImage);
2041     }
2042     butPtr->activeBdImage = image;
2043 
2044     if (butPtr->disabledBdImageString != NULL) {
2045         image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
2046             butPtr->disabledBdImageString, ButtonImageBdProc, (ClientData)butPtr);
2047             if (image == NULL) {
2048             return TCL_ERROR;
2049         }
2050     } else {
2051         image = NULL;
2052     }
2053     if (butPtr->disabledBdImage != NULL) {
2054         Tk_FreeImage(butPtr->disabledBdImage);
2055     }
2056     butPtr->disabledBdImage = image;
2057 
2058     if (butPtr->selectImageString != NULL) {
2059 	image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
2060 	    butPtr->selectImageString, ButtonSelectImageProc,
2061 	    (ClientData)butPtr);
2062 	if (image == NULL) {
2063 	    return TCL_ERROR;
2064 	}
2065     } else {
2066 	image = NULL;
2067     }
2068     if (butPtr->selectImage != NULL) {
2069 	Tk_FreeImage(butPtr->selectImage);
2070     }
2071     butPtr->selectImage = image;
2072 
2073     if (butPtr->tristateImageString != NULL) {
2074         image = Tk_GetImage(butPtr->interp, butPtr->tkwin,
2075             butPtr->tristateImageString, ButtonSelectImageProc,
2076             (ClientData)butPtr);
2077         if (image == NULL) {
2078             return TCL_ERROR;
2079         }
2080     } else {
2081         image = NULL;
2082     }
2083     if (butPtr->tristateImage != NULL) {
2084         Tk_FreeImage(butPtr->tristateImage);
2085     }
2086     butPtr->tristateImage = image;
2087 
2088     if ((butPtr->textVarName != NULL)) {
2089 	/*
2090 	 * The button must display the value of a variable: set up a trace
2091 	 * on the variable's value, create the variable if it doesn't
2092 	 * exist, and fetch its current value.
2093 	 */
2094 
2095 	CONST char *value;
2096 
2097          if (butPtr->tree == NULL) {
2098              value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
2099              if (value == NULL) {
2100                  if (Tcl_SetVar(interp, butPtr->textVarName, butPtr->textPtr,
2101                      TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
2102                          return TCL_ERROR;
2103                  }
2104              } else {
2105                  if (butPtr->textPtr != NULL) {
2106                      Blt_Free(butPtr->textPtr);
2107                  }
2108                  butPtr->textPtr = Blt_Malloc(strlen(value) + 1);
2109                  strcpy(butPtr->textPtr, value);
2110              }
2111              Tcl_TraceVar(interp, butPtr->textVarName,
2112                  TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
2113                  ButtonTextVarProc, (ClientData)butPtr);
2114          } else {
2115              Tcl_Obj *valuePtr;
2116              Blt_TreeNode nodePtr;
2117 
2118              nodePtr = Blt_TreeGetNode(butPtr->tree, butPtr->node);
2119              if (nodePtr == NULL) {
2120                  nodePtr = Blt_TreeCreateNode(butPtr->tree, Blt_TreeGetNode(butPtr->tree, 0), NULL, -1);
2121              }
2122              if (nodePtr == NULL) {
2123                  return TCL_ERROR;
2124              }
2125              if (Blt_TreeGetValue(NULL, butPtr->tree,
2126                  nodePtr, butPtr->textVarName, &valuePtr) == TCL_OK) {
2127              } else {
2128                  valuePtr = Tcl_NewStringObj("",-1);
2129                  Tcl_IncrRefCount(valuePtr);
2130                  if (Blt_TreeSetValue(interp, butPtr->tree,
2131                      nodePtr, butPtr->textVarName, valuePtr) != TCL_OK) {
2132                          Tcl_DecrRefCount(valuePtr);
2133                          return TCL_ERROR;
2134                  }
2135                  Tcl_DecrRefCount(valuePtr);
2136              }
2137 
2138              value = (valuePtr? Tcl_GetString(valuePtr):"");
2139              if (butPtr->textPtr != NULL) {
2140                  Blt_Free(butPtr->textPtr);
2141              }
2142              butPtr->textPtr = Blt_Malloc(strlen(value) + 1);
2143              strcpy(butPtr->textPtr, value);
2144 
2145              butPtr->textTrace = Blt_TreeCreateTrace(butPtr->tree,
2146                  nodePtr, butPtr->textVarName, NULL,
2147                  TREE_TRACE_UNSET|TREE_TRACE_WRITE, TreeTextTraceProc, (ClientData)butPtr);
2148          }
2149     }
2150 
2151     if ((butPtr->bitmap != None) || (butPtr->image != NULL)) {
2152 	if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->widthString,
2153 		&butPtr->width) != TCL_OK) {
2154 	  widthError:
2155 	    Tcl_AddErrorInfo(interp, "\n    (processing -width option)");
2156 	    return TCL_ERROR;
2157 	}
2158 	if (Tk_GetPixels(interp, butPtr->tkwin, butPtr->heightString,
2159 		&butPtr->height) != TCL_OK) {
2160 	  heightError:
2161 	    Tcl_AddErrorInfo(interp, "\n    (processing -height option)");
2162 	    return TCL_ERROR;
2163 	}
2164     } else {
2165 	if (Tcl_GetInt(interp, butPtr->widthString, &butPtr->width)
2166 	    != TCL_OK) {
2167 	    goto widthError;
2168 	}
2169 	if (Tcl_GetInt(interp, butPtr->heightString, &butPtr->height)
2170 	    != TCL_OK) {
2171 	    goto heightError;
2172 	}
2173     }
2174     ComputeButtonGeometry(butPtr);
2175 
2176     /*
2177      * Lastly, arrange for the button to be redisplayed.
2178      */
2179 
2180     if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
2181 	Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
2182 	butPtr->flags |= REDRAW_PENDING;
2183     }
2184     return result;
2185 }
2186 
2187 /*
2188  *----------------------------------------------------------------------
2189  *
2190  * DisplayButton --
2191  *
2192  *	This procedure is invoked to display a button widget.  It is
2193  *	normally invoked as an idle handler.
2194  *
2195  * Results:
2196  *	None.
2197  *
2198  * Side effects:
2199  *	Commands are output to X to display the button in its
2200  *	current mode.  The REDRAW_PENDING flag is cleared.
2201  *
2202  *----------------------------------------------------------------------
2203  */
2204 
2205 static void
DisplayButton(clientData)2206 DisplayButton(clientData)
2207     ClientData clientData;	/* Information about widget. */
2208 {
2209     register Button *butPtr = clientData;
2210     GC gc;
2211     Tk_3DBorder border;
2212     Pixmap pixmap;
2213     int x = 0;			/* Initialization only needed to stop
2214 				 * compiler warning. */
2215     int y, relief;
2216     register Tk_Window tkwin = butPtr->tkwin;
2217     int offset = 0;			/* 0 means this is a label widget.  1 means
2218 				 * it is a flavor of button, so we offset
2219 				 * the text to make the button appear to
2220 				 * move up and down as the relief changes. */
2221     Blt_Tile tile, bgTile, inTile;
2222     int borderWidth, drawBorder;
2223     int textXOffset, textYOffset;
2224     int haveImage = 0, haveText = 0;
2225     int imageWidth, imageHeight;
2226     int imageXOffset = 0, imageYOffset = 0;
2227     int fullWidth, fullHeight;
2228     int incFull = 0, xAdj = 0, yAdj = 0, inMirror = 0, dirty = 0, iSpc;
2229     Tk_Image image, bdImage, curImage;
2230 
2231     bdImage = butPtr->bdImage;
2232     if (butPtr->state == STATE_ACTIVE && butPtr->activeBdImage != NULL) {
2233         bdImage = butPtr->activeBdImage;
2234     } else if (butPtr->state == STATE_DISABLED && butPtr->disabledBdImage != NULL) {
2235         bdImage = butPtr->disabledBdImage;
2236     }
2237     image = butPtr->image;
2238     curImage = butPtr->image;
2239     if (butPtr->state == STATE_DISABLED && butPtr->disabledImage != NULL) {
2240         curImage = butPtr->disabledImage;
2241     }
2242     if (butPtr->state == STATE_ACTIVE && butPtr->activeImage != NULL) {
2243         curImage = butPtr->activeImage;
2244     }
2245     if ((butPtr->flags & BUTTON_DELETED)) {
2246         return;
2247     }
2248     if ((butPtr->flags & BUTTON_DIRTYBD)) {
2249         butPtr->flags &= ~BUTTON_DIRTYBD;
2250         dirty = 1;
2251     }
2252 
2253     if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
2254          image = butPtr->selectImage;
2255     } else if ((butPtr->tristateImage != NULL) && (butPtr->flags & TRISTATED)) {
2256         image = butPtr->tristateImage;
2257     } else {
2258         image = curImage;
2259     }
2260     butPtr->flags &= ~REDRAW_PENDING;
2261     if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
2262 	return;
2263     }
2264 
2265     if (butPtr->flags & BUTTON_LAYOUT) {
2266         butPtr->flags &= ~BUTTON_LAYOUT;
2267         ComputeButtonGeometry(butPtr);
2268     }
2269 
2270     iSpc = butPtr->indicatorSpace;
2271     if (butPtr->type == TYPE_MENU_BUTTON) {
2272         iSpc = 0;
2273         xAdj = butPtr->indicatorSpace;
2274     }
2275 
2276     drawBorder = 1;
2277 
2278     if (image != NULL) {
2279         Tk_SizeOfImage(image, &imageWidth, &imageHeight);
2280         haveImage = 1;
2281     } else if (butPtr->bitmap != None) {
2282         Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &imageWidth, &imageHeight);
2283         haveImage = 1;
2284     }
2285     haveText = (butPtr->textPtr != NULL && butPtr->textPtr[0]);
2286     relief = butPtr->relief;
2287     if ((butPtr->type >= TYPE_CHECK_BUTTON)  && (butPtr->type < TYPE_MENU_BUTTON) && !butPtr->indicatorOn) {
2288         if (butPtr->flags & SELECTED) {
2289             relief = TK_RELIEF_SUNKEN;
2290         } else if (butPtr->overRelief != relief) {
2291             relief = butPtr->offRelief;
2292         }
2293     }
2294 
2295     if ((butPtr->type == TYPE_BUTTON) && (bdImage == NULL) && !Tk_StrictMotif(butPtr->tkwin)) {
2296         offset = 1;
2297     }
2298 
2299 #define TILECHOOSE(t2,t1) (Blt_HasTile(t1) ? t1 : (Blt_HasTile(t2) ? t2 : NULL))
2300 
2301     tile = inTile = butPtr->innerTile;
2302     bgTile =( Blt_HasTile(butPtr->tile) ? butPtr->tile : NULL);
2303     if (butPtr->bdMaskColor == NULL) {
2304         tile = TILECHOOSE(tile,butPtr->innerTile);
2305         bgTile = TILECHOOSE(bgTile,butPtr->innerTile);
2306     }
2307     border = butPtr->normalBorder;
2308     borderWidth = butPtr->borderWidth;
2309 
2310     gc = butPtr->normalTextGC;
2311 
2312     if (butPtr->state == STATE_DISABLED) {
2313         if (Blt_HasTile(butPtr->disabledTile)) {
2314             tile = butPtr->disabledTile;
2315             bgTile = butPtr->disabledTile;
2316         }
2317         if (butPtr->disabledFg != NULL && butPtr->disabledGC) {
2318             gc = butPtr->disabledGC;
2319         }
2320         if (Blt_HasTile(butPtr->disabledInnerTile)) {
2321             inTile = butPtr->disabledInnerTile;
2322         }
2323     } else if ((butPtr->state == STATE_ACTIVE) && butPtr->activeFg != NULL
2324 	&& !Tk_StrictMotif(butPtr->tkwin)) {
2325 	gc = butPtr->activeTextGC;
2326         border = CHOOSE(butPtr->normalBorder,butPtr->activeBorder);
2327 	tile = TILECHOOSE(tile,butPtr->activeTile);
2328         bgTile = TILECHOOSE(bgTile,butPtr->activeTile);
2329         if (Blt_HasTile(butPtr->activeInnerTile)) {
2330             inTile = butPtr->activeInnerTile;
2331         }
2332     }
2333     if ((butPtr->flags & SELECTED) && (butPtr->state != STATE_ACTIVE)
2334 	&& (butPtr->selectBorder != NULL) && !butPtr->indicatorOn
2335 	&& butPtr->type != TYPE_MENU_BUTTON) {
2336 	border = butPtr->selectBorder;
2337     }
2338     /*
2339      * Override the relief specified for the button if this is a
2340      * checkbutton or radiobutton and there's no indicator.
2341      */
2342 
2343     relief = butPtr->relief;
2344     if ((butPtr->type >= TYPE_CHECK_BUTTON) && (butPtr->type < TYPE_MENU_BUTTON) && !butPtr->indicatorOn) {
2345 	relief = (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN
2346 	    : TK_RELIEF_RAISED;
2347     }
2348 
2349     /*
2350      * In order to avoid screen flashes, this procedure redraws
2351      * the button in a pixmap, then copies the pixmap to the
2352      * screen in a single operation.  This means that there's no
2353      * point in time where the on-sreen image has been cleared.
2354      */
2355 
2356      pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
2357 	   Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
2358 
2359     if (Blt_HasTile(bgTile)) {
2360         Blt_SetTileOrigin(tkwin, bgTile, 0, 0);
2361         Blt_TileRectangle(tkwin, pixmap, bgTile, 0, 0, Tk_Width(tkwin),
2362             Tk_Height(tkwin));
2363     } else {
2364         Tk_3DBorder curBorder = border;
2365         Blt_Fill3DRectangle(tkwin, pixmap, curBorder, 0, 0, Tk_Width(tkwin),
2366             Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
2367     }
2368     if (bdImage) {
2369         if (butPtr->bdImageSized == NULL) {
2370             /* TODO: would be nice if we could get rid of Tcl visible image for photo */
2371             butPtr->bdImageSized = Blt_CreateTemporaryImage(butPtr->interp, tkwin, butPtr);
2372         }
2373         if (butPtr->bdImageSized != NULL) {
2374             int width = 0, height = 0, bdWid = butPtr->borderWidth + butPtr->highlightWidth;
2375             int wWidth = Tk_Width(tkwin)-bdWid*2, wHeight = Tk_Height(tkwin)-bdWid*2;
2376             int result = TCL_OK;
2377             Tk_SizeOfImage(butPtr->bdImageSized, &width, &height);
2378             /*if (butPtr->bdHalo>=0 && butPtr->state != STATE_ACTIVE) { drawBorder = 0; } */
2379             if (width != wWidth || height != wHeight || dirty) {
2380                 Tk_PhotoHandle destPhoto;
2381                 char *dName, *sName;
2382                 dName = Blt_NameOfImage(butPtr->bdImageSized);
2383                 if (dName == NULL) {
2384                     Tk_FreeImage(butPtr->bdImageSized);
2385                     butPtr->bdImageSized = Blt_CreateTemporaryImage(butPtr->interp, tkwin, butPtr);
2386                     dName = Blt_NameOfImage(butPtr->bdImageSized);
2387                 }
2388                 sName = Blt_NameOfImage(bdImage);
2389                 destPhoto = Tk_FindPhoto(butPtr->interp, dName);
2390                 if (sName && dName && destPhoto) {
2391                     int nWidth, nHeight;
2392                     nWidth = ((wWidth+1) & ~0x1);
2393                     nHeight = ((wHeight+1) & ~0x1);
2394                     Tk_PhotoSetSize(destPhoto, nWidth, nHeight);
2395                     result = Blt_ImageMirror(butPtr->interp, sName, dName, butPtr->bdHalo<0?MIRROR_INNER:MIRROR_OUTER, butPtr->bdHalo<0?0:butPtr->bdHalo);
2396                     if (result == TCL_OK && butPtr->bdMaskColor && inTile) {
2397                         char *tName = Blt_NameOfTile(inTile);
2398                         result = Blt_ImageMergeInner(butPtr->interp, dName, tName, dName, butPtr->bdMaskColor, 0);
2399                     }
2400                 }
2401             }
2402             if (butPtr->bdHalo<0 && result == TCL_OK) {
2403                 inMirror = 1;
2404             }
2405             if (result == TCL_OK && inMirror == 0) {
2406                 Tk_RedrawImage(butPtr->bdImageSized, 0, 0, wWidth, wHeight, pixmap, bdWid, bdWid);
2407             }
2408         }
2409     }
2410     textXOffset = 0;
2411     textYOffset = 0;
2412 
2413     if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) {
2414         fullWidth = 0;
2415         fullHeight = 0;
2416 
2417         switch (butPtr->compound) {
2418         case COMPOUND_TOP:
2419         case COMPOUND_BOTTOM:
2420             /*
2421             * Image is above or below text.
2422             */
2423 
2424             if (butPtr->compound == COMPOUND_TOP) {
2425                 textYOffset = imageHeight + butPtr->padY + butPtr->yOffset;
2426             } else {
2427                 imageYOffset = butPtr->textHeight + butPtr->padY + butPtr->yOffset;
2428             }
2429             fullHeight = imageHeight + butPtr->textHeight + butPtr->padY;
2430             fullWidth = (imageWidth > butPtr->textWidth ? imageWidth : butPtr->textWidth);
2431             textXOffset = (fullWidth - butPtr->textWidth)/2 + butPtr->xOffset;
2432             imageXOffset = (fullWidth - imageWidth)/2 + butPtr->xOffset;
2433             break;
2434         case COMPOUND_LEFT:
2435         case COMPOUND_RIGHT:
2436             /*
2437             * Image is left or right of text.
2438             */
2439 
2440             if (butPtr->compound == COMPOUND_LEFT) {
2441                 textXOffset = imageWidth + butPtr->padX + butPtr->xOffset;
2442             } else {
2443                 imageXOffset = butPtr->textWidth + butPtr->padX  + butPtr->xOffset;
2444             }
2445             fullWidth = butPtr->textWidth + butPtr->padX + imageWidth;
2446             fullHeight = (imageHeight > butPtr->textHeight ? imageHeight : butPtr->textHeight);
2447             textYOffset = (fullHeight - butPtr->textHeight)/2  + butPtr->yOffset;
2448             imageYOffset = (fullHeight - imageHeight)/2 + butPtr->yOffset;
2449             break;
2450         case COMPOUND_CENTER:
2451             /*
2452             * Image and text are superimposed.
2453             */
2454 
2455             fullWidth = (imageWidth > butPtr->textWidth ? imageWidth :
2456             butPtr->textWidth);
2457             fullHeight = (imageHeight > butPtr->textHeight ? imageHeight :
2458             butPtr->textHeight);
2459             textXOffset = (fullWidth - butPtr->textWidth)/2 + butPtr->xOffset;
2460             imageXOffset = (fullWidth - imageWidth)/2 + butPtr->xOffset;
2461             textYOffset = (fullHeight - butPtr->textHeight)/2 + butPtr->yOffset;
2462             imageYOffset = (fullHeight - imageHeight)/2 + butPtr->yOffset;
2463             break;
2464         case COMPOUND_NONE:
2465             imageXOffset += butPtr->xOffset;
2466             imageYOffset += butPtr->yOffset;
2467             break;
2468         }
2469 
2470         ComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
2471             iSpc + fullWidth, fullHeight, &x, &y, xAdj, yAdj);
2472 
2473             x += iSpc;
2474 
2475             x += offset;
2476             y += offset;
2477             if (relief == TK_RELIEF_RAISED) {
2478             x -= offset;
2479             y -= offset;
2480         } else if (relief == TK_RELIEF_SUNKEN) {
2481             x += offset;
2482             y += offset;
2483         }
2484 
2485         imageXOffset += x;
2486         imageYOffset += y;
2487 
2488         if (image != NULL) {
2489             /*
2490             * Do boundary clipping, so that Tk_RedrawImage is passed valid
2491             * coordinates. [Bug 979239]
2492             */
2493 
2494             if (imageXOffset < 0) {
2495                 imageXOffset = 0;
2496             }
2497             if (imageYOffset < 0) {
2498                 imageYOffset = 0;
2499             }
2500             if (imageWidth > Tk_Width(tkwin)) {
2501                 imageWidth = Tk_Width(tkwin);
2502             }
2503             if (imageHeight > Tk_Height(tkwin)) {
2504                 imageHeight = Tk_Height(tkwin);
2505             }
2506             if ((imageWidth + imageXOffset) > Tk_Width(tkwin)) {
2507                 imageXOffset = Tk_Width(tkwin) - imageWidth;
2508             }
2509             if ((imageHeight + imageYOffset) > Tk_Height(tkwin)) {
2510                 imageYOffset = Tk_Height(tkwin) - imageHeight;
2511             }
2512 
2513             Tk_RedrawImage(image, 0, 0, imageWidth, imageHeight, pixmap, imageXOffset,
2514                 imageYOffset);
2515         } else {
2516             XSetClipOrigin(butPtr->display, gc, imageXOffset, imageYOffset);
2517             XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc,
2518                 0, 0, (unsigned int) imageWidth, (unsigned int) imageHeight,
2519                 imageXOffset, imageYOffset, 1);
2520                 XSetClipOrigin(butPtr->display, gc, 0, 0);
2521         }
2522         incFull = 1;
2523         goto drawtext;
2524     } else {
2525         if (haveImage) {
2526             ComputeAnchor(butPtr->anchor, tkwin, 0, 0,
2527                 iSpc + imageWidth, imageHeight, &x, &y, xAdj, yAdj);
2528                 x += iSpc;
2529 
2530                 x += offset;
2531                 y += offset;
2532                 if (relief == TK_RELIEF_RAISED) {
2533                     x -= offset;
2534                     y -= offset;
2535                 } else if (relief == TK_RELIEF_SUNKEN) {
2536                     x += offset;
2537                     y += offset;
2538                 }
2539                 imageXOffset += (x + butPtr->xOffset);
2540                 imageYOffset += (y + butPtr->yOffset);
2541                 if (curImage != NULL) {
2542                     /*
2543                     * Do boundary clipping, so that Tk_RedrawImage is passed
2544                     * valid coordinates. [Bug 979239]
2545                     */
2546 
2547                     if (imageXOffset < 0) {
2548                         imageXOffset = 0;
2549                     }
2550                     if (imageYOffset < 0) {
2551                         imageYOffset = 0;
2552                     }
2553                     if (imageWidth > Tk_Width(tkwin)) {
2554                         imageWidth = Tk_Width(tkwin);
2555                     }
2556                     if (imageHeight > Tk_Height(tkwin)) {
2557                         imageHeight = Tk_Height(tkwin);
2558                     }
2559                     if ((imageWidth + imageXOffset) > Tk_Width(tkwin)) {
2560                         imageXOffset = Tk_Width(tkwin) - imageWidth;
2561                     }
2562                     if ((imageHeight + imageYOffset) > Tk_Height(tkwin)) {
2563                         imageYOffset = Tk_Height(tkwin) - imageHeight;
2564                     }
2565 
2566                     Tk_RedrawImage(image, 0, 0, imageWidth, imageHeight, pixmap,
2567                         imageXOffset, imageYOffset);
2568                 } else {
2569                     XSetClipOrigin(butPtr->display, gc, x, y);
2570                     XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
2571                         (unsigned int) imageWidth, (unsigned int) imageHeight, x, y, 1);
2572                         XSetClipOrigin(butPtr->display, gc, 0, 0);
2573                 }
2574                 y += imageHeight/2;
2575             } else {
2576                 ComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
2577                     iSpc + butPtr->textWidth,
2578                     butPtr->textHeight, &x, &y, xAdj, yAdj);
2579 
2580                     x += iSpc;
2581 
2582                     x += offset;
2583                     y += offset;
2584                     if (relief == TK_RELIEF_RAISED) {
2585                     x -= offset;
2586                     y -= offset;
2587                 } else if (relief == TK_RELIEF_SUNKEN) {
2588                     x += offset;
2589                     y += offset;
2590                 }
2591                 if ((butPtr->type <= TYPE_LABEL)) {
2592                     x -= 2;
2593                 }
2594 #if 1
2595 drawtext:
2596          {
2597              TextStyle ts;
2598              Shadow *shadowPtr;
2599              int selected = 0, active = 0;
2600              x += butPtr->xOffset;
2601              y += butPtr->yOffset;
2602              if (butPtr->state == STATE_ACTIVE) {
2603                  shadowPtr = &butPtr->activeShadow;
2604              } else {
2605                  shadowPtr = &butPtr->shadow;
2606              }
2607              Blt_SetDrawTextStyle(&ts, butPtr->tkfont, gc ? gc : butPtr->normalTextGC,
2608                 (butPtr->state == STATE_DISABLED && butPtr->disabledFg) ?
2609                  butPtr->disabledFg: butPtr->normalFg, butPtr->activeFg,
2610                  shadowPtr->color, butPtr->rotate, TK_ANCHOR_NW, butPtr->justify,
2611                  0, shadowPtr->offset);
2612              ts.underline = butPtr->underline;
2613              ts.state = (butPtr->state==STATE_ACTIVE?STATE_ACTIVE:0);
2614              ts.border = border;
2615              ts.padX.side1 = ts.padX.side2 = 2;
2616              if ((selected) || (active)) {
2617                  ts.state |= STATE_ACTIVE;
2618              }
2619              Blt_DrawText(butPtr->tkwin, pixmap, butPtr->textPtr, &ts, x + textXOffset, y + textYOffset);
2620              if (incFull) {
2621                  y += fullHeight/2;
2622              } else {
2623                  y = Tk_Height(tkwin)/2;
2624              }
2625 
2626          }
2627 #else
2628             Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout,
2629                 x, y, 0, -1);
2630                 Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
2631                 butPtr->textLayout, x, y, butPtr->underline);
2632                 y += butPtr->textHeight/2;
2633 #endif
2634 
2635     }
2636 
2637     }
2638 
2639     /*
2640      * Draw the indicator for check buttons and radio buttons.  At this
2641      * point x and y refer to the top-left corner of the text or image
2642      * or bitmap.
2643      */
2644 
2645     if ((butPtr->type == TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
2646 	int dim;
2647         int bd = 1; /* butPtr->borderWidth */
2648 
2649         if (butPtr->icons[0] && (butPtr->flags & HAS_ICONS)) {
2650             goto drawInd;
2651         }
2652         dim = butPtr->indicatorDiameter;
2653 	x -= butPtr->indicatorSpace;
2654 	y -= dim / 2;
2655 	if (y < 2) { y = 2; }
2656 	if (dim > 2 * butPtr->borderWidth) {
2657 	    Blt_Draw3DRectangle(tkwin, pixmap, border, x, y, dim, dim,
2658 		bd,
2659 		(butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
2660 		TK_RELIEF_RAISED);
2661 	    x += bd;
2662 	    y += bd;
2663 	    dim -= 2 * bd;
2664 	    if (butPtr->flags & SELECTED) {
2665 		GC borderGC;
2666 
2667 		borderGC = Tk_3DBorderGC(tkwin, (butPtr->selectBorder != NULL)
2668 		    ? butPtr->selectBorder : butPtr->normalBorder,
2669 		    TK_3D_FLAT_GC);
2670 		XFillRectangle(butPtr->display, pixmap, borderGC, x, y,
2671 		    (unsigned int)dim, (unsigned int)dim);
2672 	    } else {
2673 		Blt_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, x, y,
2674 		    dim, dim, butPtr->borderWidth, TK_RELIEF_FLAT);
2675 	    }
2676 	}
2677     } else if ((butPtr->type == TYPE_RADIO_BUTTON) && butPtr->indicatorOn) {
2678 	XPoint points[4];
2679 	int radius;
2680         int bd = 1; /* butPtr->borderWidth */
2681 
2682         if (butPtr->icons[0] && (butPtr->flags & HAS_ICONS)) {
2683             goto drawInd;
2684         }
2685         radius = butPtr->indicatorDiameter / 2;
2686 	points[0].x = x - butPtr->indicatorSpace;
2687 	points[0].y = y;
2688 	points[1].x = points[0].x + radius;
2689 	points[1].y = points[0].y + radius;
2690 	points[2].x = points[1].x + radius;
2691 	points[2].y = points[0].y;
2692 	points[3].x = points[1].x;
2693 	points[3].y = points[0].y - radius;
2694 	if (butPtr->flags & SELECTED) {
2695 	    GC borderGC;
2696 
2697 	    borderGC = Tk_3DBorderGC(tkwin, (butPtr->selectBorder != NULL)
2698 		? butPtr->selectBorder : butPtr->normalBorder,
2699 		TK_3D_FLAT_GC);
2700 	    XFillPolygon(butPtr->display, pixmap, borderGC, points, 4, Convex,
2701 		CoordModeOrigin);
2702 	} else {
2703 	    Tk_Fill3DPolygon(tkwin, pixmap, butPtr->normalBorder, points,
2704 		4, bd, TK_RELIEF_FLAT);
2705 	}
2706 	Tk_Draw3DPolygon(tkwin, pixmap, border, points, 4, bd,
2707 	    (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
2708 	    TK_RELIEF_RAISED);
2709     }
2710 
2711     if (butPtr->type == TYPE_MENU_BUTTON && butPtr->indicatorOn) {
2712         int mborderWidth, bsub = 0;
2713 
2714         if (butPtr->icons[0] && (butPtr->flags & HAS_ICONS)) {
2715             goto drawInd;
2716         }
2717         if (bdImage) {
2718             bsub = 7;
2719         }
2720         mborderWidth = (butPtr->indicatorHeight + 1) / 3;
2721         if (mborderWidth < 1) {
2722             mborderWidth = 1;
2723         }
2724 
2725         Blt_Fill3DRectangle(tkwin, pixmap, border,
2726             Tk_Width(tkwin) - butPtr->inset - butPtr->indicatorWidth + 2 - bsub,
2727             butPtr->inset + (Tk_Height(tkwin) - butPtr->highlightWidth -
2728             butPtr->indicatorHeight)/2,
2729             butPtr->indicatorWidth - mborderWidth - 2,
2730             butPtr->indicatorHeight,
2731             mborderWidth, TK_RELIEF_RAISED);
2732     }
2733 
2734     goto goon;
2735 
2736     /* Handle drawing the indicator for Check/Radio/Menu buttons. */
2737     {
2738         Tk_Image icon;
2739         int w1, h1, sind;
2740 drawInd:
2741 
2742         sind = 0;
2743 
2744         if (butPtr->state == STATE_ACTIVE) {
2745             sind = 3;
2746         } else if (butPtr->state == STATE_DISABLED) {
2747             sind = 6;
2748         }
2749 #define STATEICON(n) (butPtr->icons[sind+n] ? butPtr->icons[sind+n] : butPtr->icons[n])
2750         icon = NULL;
2751         if ((butPtr->flags & SELECTED)) {
2752             icon = 	STATEICON(1);
2753         } else if (((butPtr->flags & TRISTATED))) {
2754             icon = STATEICON(2);
2755         }
2756         if (icon == NULL) {
2757             icon = 	STATEICON(0);
2758         }
2759         Tk_SizeOfImage(icon, &w1, &h1);
2760         x -= butPtr->indicatorSpace;
2761         y -= butPtr->indicatorSpace/2;
2762 
2763         Tk_RedrawImage(icon, 0, 0, w1, h1, pixmap, x, y);
2764     }
2765 
2766 goon:
2767 
2768     /*
2769      * If the button is disabled with a stipple rather than a special
2770      * foreground color, generate the stippled effect.  If the widget
2771      * is selected and we use a different background color when selected,
2772      * must temporarily modify the GC.
2773      */
2774 
2775     if ((butPtr->state == STATE_DISABLED) && (Blt_HasTile(butPtr->disabledTile) == 0)
2776 	&& ((butPtr->disabledFg == NULL) || (curImage != NULL)) &&
2777 	bdImage == NULL && Blt_HasTile(butPtr->tile) == 0) {
2778 	if ((butPtr->flags & SELECTED) && butPtr->indicatorOn == 0
2779 	    && (butPtr->selectBorder != NULL)) {
2780 	    XSetForeground(butPtr->display, butPtr->disabledGC,
2781 		Tk_3DBorderColor(butPtr->selectBorder)->pixel);
2782 	}
2783 	XFillRectangle(butPtr->display, pixmap, butPtr->disabledGC,
2784 	    butPtr->inset, butPtr->inset,
2785 	    (unsigned)(Tk_Width(tkwin) - 2 * butPtr->inset),
2786 	    (unsigned)(Tk_Height(tkwin) - 2 * butPtr->inset));
2787 	if ((butPtr->flags & SELECTED) && butPtr->indicatorOn == 0
2788 	    && (butPtr->selectBorder != NULL)) {
2789 	    XSetForeground(butPtr->display, butPtr->disabledGC,
2790 		Tk_3DBorderColor(butPtr->normalBorder)->pixel);
2791 	}
2792     }
2793     /*
2794      * Draw the border and traversal highlight last.  This way, if the
2795      * button's contents overflow they'll be covered up by the border.
2796      */
2797 
2798     if (relief != TK_RELIEF_FLAT && drawBorder  && butPtr->type > TYPE_LABEL ) {
2799 	int inset = butPtr->highlightWidth;
2800 	if (butPtr->defaultState == STATE_ACTIVE) {
2801 	    inset += 2;
2802 	    Blt_Draw3DRectangle(tkwin, pixmap, border, inset, inset,
2803 		Tk_Width(tkwin) - 2 * inset, Tk_Height(tkwin) - 2 * inset,
2804 		1, TK_RELIEF_SUNKEN);
2805 	    inset += 3;
2806 	}
2807 	Blt_Draw3DRectangle(tkwin, pixmap, border, inset, inset,
2808 	    Tk_Width(tkwin) - 2 * inset, Tk_Height(tkwin) - 2 * inset,
2809 	    butPtr->borderWidth, relief);
2810     }
2811     if (butPtr->highlightWidth > 0 && butPtr->highlightBgColorPtr != NULL  && butPtr->type > TYPE_LABEL ) {
2812 	GC highlightGC;
2813 
2814 	if (butPtr->flags & GOT_FOCUS) {
2815 	    highlightGC = Tk_GCForColor(butPtr->highlightColorPtr, pixmap);
2816 	} else {
2817 	    highlightGC = Tk_GCForColor(butPtr->highlightBgColorPtr, pixmap);
2818 	}
2819 	Tk_DrawFocusHighlight(tkwin, highlightGC, butPtr->highlightWidth, pixmap);
2820     }
2821     if ((butPtr->flags & GOT_FOCUS) && Tk_Width(tkwin) > 11 && Tk_Height(tkwin) > 11 && bdImage  && butPtr->type > TYPE_LABEL ) {
2822         int dx;
2823         dx = 5 + butPtr->highlightWidth;
2824         XDrawRectangle(butPtr->display, pixmap, butPtr->focusGC,
2825             dx, dx, Tk_Width(tkwin) - 2*dx, Tk_Height(tkwin) - 2*dx);
2826     }
2827     /*
2828      * Copy the information from the off-screen pixmap onto the screen,
2829      * then delete the pixmap.
2830      */
2831 
2832     XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
2833 	butPtr->copyGC, 0, 0, (unsigned)Tk_Width(tkwin),
2834 	(unsigned)Tk_Height(tkwin), 0, 0);
2835     Tk_FreePixmap(butPtr->display, pixmap);
2836 }
2837 
2838 /*
2839  *----------------------------------------------------------------------
2840  *
2841  * ComputeButtonGeometry --
2842  *
2843  *	After changes in a button's text or bitmap, this procedure
2844  *	recomputes the button's geometry and passes this information
2845  *	along to the geometry manager for the window.
2846  *
2847  * Results:
2848  *	None.
2849  *
2850  * Side effects:
2851  *	The button's window may change size.
2852  *
2853  *----------------------------------------------------------------------
2854  */
2855 #define STR(s) ((s)?s:"")
2856 static void
ComputeButtonGeometry(butPtr)2857 ComputeButtonGeometry(butPtr)
2858     register Button *butPtr;	/* Button whose geometry may have changed. */
2859 {
2860     int width, height, avgWidth, txtWidth, txtHeight;
2861     int haveImage = 0, haveText = 0, isRadio = 0;
2862     Tk_FontMetrics fm;
2863     Tk_Image curImage;
2864 
2865     curImage = butPtr->image;
2866     butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;
2867     isRadio = (butPtr->type == TYPE_RADIO_BUTTON);
2868     /*
2869     * Leave room for the default ring if needed.
2870     */
2871 
2872     if (butPtr->type == TYPE_CHECK_BUTTON && butPtr->tristateValue &&
2873         butPtr->selVarName) {
2874         char *value;
2875 
2876         value = ButtonGetValue(butPtr);
2877 
2878         if (value == NULL) {
2879             value = "";
2880         }
2881 
2882         if (strcmp(value,butPtr->tristateValue)==0) {
2883             butPtr->flags |= TRISTATED;
2884         } else {
2885             butPtr->flags &= ~TRISTATED;
2886         }
2887     }
2888     if (butPtr->defaultState != STATE_DISABLED) {
2889         butPtr->inset += 5;
2890     }
2891     butPtr->indicatorSpace = 0;
2892 
2893     width = 0;
2894     height = 0;
2895     txtWidth = 0;
2896     txtHeight = 0;
2897     avgWidth = 0;
2898 
2899     if (butPtr->state == STATE_DISABLED && butPtr->disabledImage != NULL) {
2900         curImage = butPtr->disabledImage;
2901     }
2902     if (butPtr->state == STATE_ACTIVE && butPtr->activeImage != NULL) {
2903         curImage = butPtr->activeImage;
2904     }
2905     if (curImage != NULL) {
2906         Tk_SizeOfImage(curImage, &width, &height);
2907         haveImage = 1;
2908     } else if (butPtr->bitmap != None) {
2909         Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
2910         haveImage = 1;
2911     }
2912 
2913     if (butPtr->type != TYPE_FRAME && (haveImage == 0 || butPtr->compound != COMPOUND_NONE)) {
2914 
2915         if (butPtr->rotate > 0.0) {
2916             double rotWidth, rotHeight;
2917             int labelWidth, labelHeight;
2918             TextStyle ts;
2919 
2920             Blt_InitTextStyle(&ts);
2921             ts.font = butPtr->tkfont;
2922             if (butPtr->state == STATE_ACTIVE) {
2923                 ts.shadow.offset = butPtr->shadow.offset;
2924             } else {
2925                 ts.shadow.offset = butPtr->activeShadow.offset;
2926             }
2927             ts.padX.side1 = ts.padX.side2 = 2;
2928 
2929             Blt_GetTextExtents(&ts, STR(butPtr->textPtr), &labelWidth, &labelHeight);
2930             Blt_GetBoundingBox(labelWidth, labelHeight, butPtr->rotate,
2931                 &rotWidth, &rotHeight, (Point2D *)NULL);
2932             butPtr->textWidth = ROUND(rotWidth);
2933             butPtr->textHeight = ROUND(rotHeight);
2934 
2935         } else {
2936             if (butPtr->textLayout) {
2937                 Tk_FreeTextLayout(butPtr->textLayout);
2938             }
2939 
2940             butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont,
2941                 STR(butPtr->textPtr), -1, butPtr->wrapLength,
2942                 butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight);
2943         }
2944 
2945         txtWidth = butPtr->textWidth;
2946         txtHeight = butPtr->textHeight;
2947         avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1);
2948         Tk_GetFontMetrics(butPtr->tkfont, &fm);
2949         haveText = (txtWidth != 0 && txtHeight != 0);
2950     }
2951 
2952     /*
2953     * If the button is compound (i.e., it shows both an image and text), the
2954     * new geometry is a combination of the image and text geometry. We only
2955     * honor the compound bit if the button has both text and an image,
2956     * because otherwise it is not really a compound button.
2957     */
2958 
2959     if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) {
2960         int isVert = 0;
2961         switch (butPtr->compound) {
2962             case COMPOUND_TOP:
2963             case COMPOUND_BOTTOM:
2964             /*
2965             * Image is above or below text.
2966             */
2967             isVert = 1;
2968             height += txtHeight + butPtr->padY;
2969             width = (width > txtWidth ? width : txtWidth);
2970             break;
2971             case COMPOUND_LEFT:
2972             case COMPOUND_RIGHT:
2973             /*
2974             * Image is left or right of text.
2975             */
2976 
2977             width += txtWidth + butPtr->padX;
2978             height = (height > txtHeight ? height : txtHeight);
2979             break;
2980             case COMPOUND_CENTER:
2981             /*
2982             * Image and text are superimposed.
2983             */
2984 
2985             width = (width > txtWidth ? width : txtWidth);
2986             height = (height > txtHeight ? height : txtHeight);
2987             break;
2988             case COMPOUND_NONE:
2989             break;
2990         }
2991         if (butPtr->width < 0) {
2992             width = -butPtr->width;
2993         } else if (butPtr->width > 0) {
2994             width = butPtr->width;
2995         }
2996         if (butPtr->height < 0) {
2997             height = -butPtr->height;
2998         } else if (butPtr->height > 0) {
2999             height = butPtr->height;
3000         }
3001 
3002 
3003         if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
3004             if (butPtr->rotate <= 0 && isVert==0) {
3005                 butPtr->indicatorSpace = height;
3006             } else {
3007                 if (butPtr->indicatorSize>0) {
3008                     butPtr->indicatorSpace = butPtr->indicatorSize+6;
3009                 } else {
3010                     butPtr->indicatorSpace = height;
3011                 }
3012             }
3013             if (butPtr->indicatorSize>0) {
3014                 butPtr->indicatorDiameter = butPtr->indicatorSize + (isRadio*2);
3015             } else if (butPtr->type == TYPE_CHECK_BUTTON) {
3016                 butPtr->indicatorDiameter = (65*height)/100;
3017             } else {
3018                 butPtr->indicatorDiameter = (75*height)/100;
3019             }
3020         }
3021 
3022         width += 2*butPtr->padX;
3023         height += 2*butPtr->padY;
3024     } else {
3025         if (haveImage) {
3026             if (butPtr->width < 0) {
3027                 width = -butPtr->width;
3028             } else if (butPtr->width > 0) {
3029                 width = butPtr->width;
3030             }
3031             if (butPtr->height < 0) {
3032                 height = -butPtr->height;
3033             } else if (butPtr->height > 0) {
3034                 height = butPtr->height;
3035             }
3036 
3037             if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
3038                 butPtr->indicatorSpace = height;
3039                 if (butPtr->indicatorSize>0) {
3040                     butPtr->indicatorDiameter = butPtr->indicatorSize + (isRadio*2);
3041                 } else if (butPtr->type == TYPE_CHECK_BUTTON) {
3042                     butPtr->indicatorDiameter = (65*height)/100;
3043                 } else {
3044                     butPtr->indicatorDiameter = (75*height)/100;
3045                 }
3046             }
3047         } else {
3048             width = txtWidth;
3049             height = txtHeight;
3050 
3051             if (butPtr->width < 0) {
3052                 width = -butPtr->width;
3053             } else if (butPtr->width > 0) {
3054                 if (butPtr->type >= TYPE_FRAME) {
3055                     width = butPtr->width;
3056                 } else {
3057                     width = butPtr->width * avgWidth;
3058                 }
3059             }
3060             if (butPtr->height < 0) {
3061                 height = -butPtr->height;
3062              } else if (butPtr->height > 0) {
3063                  height = butPtr->height;
3064             }
3065             if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
3066                 if (butPtr->indicatorSize>0) {
3067                     butPtr->indicatorDiameter = butPtr->indicatorSize + (isRadio*2);
3068                 } else {
3069                     butPtr->indicatorDiameter = fm.linespace;
3070                     if (butPtr->type == TYPE_CHECK_BUTTON) {
3071                         butPtr->indicatorDiameter =
3072                         (80*butPtr->indicatorDiameter)/100;
3073                     }
3074                 }
3075                 butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth;
3076             }
3077         }
3078     }
3079     butPtr->flags &= ~HAS_ICONS;
3080 
3081     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn && butPtr->icons[0]) {
3082         int h1, h2, h3, w1, w2, w3;
3083         Tk_SizeOfImage(butPtr->icons[0], &w1, &h1);
3084         Tk_SizeOfImage(butPtr->icons[1], &w2, &h2);
3085         if (butPtr->icons[2]) {
3086             Tk_SizeOfImage(butPtr->icons[2], &w3, &h3);
3087             if (w3>w1) { w1 = w3; }
3088             if (h3>h1) { h1 = h3; }
3089         }
3090         if (w1>0 && h1>0 && w2>0 && h2>0) {
3091             butPtr->flags |= HAS_ICONS;
3092             if (w2>w1) { w1 = w2; }
3093             if (h2>h1) { h1 = h2; }
3094             butPtr->indicatorSpace = w1;
3095             if (h1>butPtr->indicatorSpace) { butPtr->indicatorSpace = h1; }
3096             butPtr->indicatorSpace += 4;
3097         }
3098 
3099     }
3100     if (butPtr->type == TYPE_MENU_BUTTON) {
3101         if (butPtr->indicatorOn) {
3102             if (butPtr->indicatorOn) {
3103                 int mm, pixels;
3104                 mm = WidthMMOfScreen(Tk_Screen(butPtr->tkwin));
3105                 pixels = WidthOfScreen(Tk_Screen(butPtr->tkwin));
3106                 butPtr->indicatorHeight = (INDICATOR_HEIGHT * pixels) / (10 * mm);
3107                 butPtr->indicatorWidth = (INDICATOR_WIDTH * pixels) / (10 * mm)
3108                 + 2 * butPtr->indicatorHeight;
3109                 butPtr->indicatorSpace = butPtr->indicatorWidth;
3110             } else {
3111                 butPtr->indicatorHeight = 0;
3112                 butPtr->indicatorWidth = 0;
3113                 butPtr->indicatorSpace = 0;
3114             }
3115         }
3116     }
3117 
3118     if ((curImage == NULL) && (butPtr->bitmap == None)) {
3119         width += 2*butPtr->padX;
3120         height += 2*butPtr->padY;
3121     }
3122     if ((butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin)) {
3123         width += 2;
3124         height += 2;
3125     }
3126     if ((butPtr->type <= TYPE_LABEL)) {
3127         width += 4;
3128     }
3129     if (width%2) width++;
3130     if (height%2) height++;
3131     Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
3132         + 2*butPtr->inset), (int) (height + 2*butPtr->inset));
3133     Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
3134 }
3135 
3136 /*
3137  *--------------------------------------------------------------
3138  *
3139  * ButtonEventProc --
3140  *
3141  *	This procedure is invoked by the Tk dispatcher for various
3142  *	events on buttons.
3143  *
3144  * Results:
3145  *	None.
3146  *
3147  * Side effects:
3148  *	When the window gets deleted, internal structures get
3149  *	cleaned up.  When it gets exposed, it is redisplayed.
3150  *
3151  *--------------------------------------------------------------
3152  */
3153 
3154 static void
ButtonEventProc(clientData,eventPtr)3155 ButtonEventProc(clientData, eventPtr)
3156     ClientData clientData;	/* Information about window. */
3157     XEvent *eventPtr;		/* Information about event. */
3158 {
3159     Button *butPtr = clientData;
3160     if ((butPtr->flags & BUTTON_DELETED)) {
3161         return;
3162     }
3163 
3164     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
3165 	goto redraw;
3166     } else if (eventPtr->type == ConfigureNotify) {
3167 	/*
3168 	 * Must redraw after size changes, since layout could have changed
3169 	 * and borders will need to be redrawn.
3170 	 */
3171 
3172 	goto redraw;
3173     } else if (eventPtr->type == DestroyNotify) {
3174 	if (butPtr->tkwin != NULL) {
3175 	    butPtr->tkwin = NULL;
3176 	    Tcl_DeleteCommandFromToken(butPtr->interp, butPtr->widgetCmd);
3177 	}
3178 	if (butPtr->flags & REDRAW_PENDING) {
3179 	    Tcl_CancelIdleCall(DisplayButton, (ClientData)butPtr);
3180 	}
3181 	/* This is a hack to workaround a bug in 8.3.3. */
3182 	DestroyButton((ClientData)butPtr);
3183 	/* Tcl_EventuallyFree((ClientData)butPtr, (Tcl_FreeProc *)Blt_Free); */
3184     } else if (eventPtr->type == FocusIn) {
3185 	if (eventPtr->xfocus.detail != NotifyInferior) {
3186 	    butPtr->flags |= GOT_FOCUS;
3187 	    if (1 || butPtr->highlightWidth > 0) {
3188 		goto redraw;
3189 	    }
3190 	}
3191     } else if (eventPtr->type == FocusOut) {
3192 	if (eventPtr->xfocus.detail != NotifyInferior) {
3193              butPtr->flags &= ~GOT_FOCUS;
3194 	    if (1 || butPtr->highlightWidth > 0) {
3195 		goto redraw;
3196 	    }
3197 	}
3198     }
3199     return;
3200 
3201   redraw:
3202     if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
3203 	Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
3204 	butPtr->flags |= REDRAW_PENDING;
3205     }
3206 }
3207 
3208 /*
3209  *----------------------------------------------------------------------
3210  *
3211  * ButtonCmdDeletedProc --
3212  *
3213  *	This procedure is invoked when a widget command is deleted.  If
3214  *	the widget isn't already in the process of being destroyed,
3215  *	this command destroys it.
3216  *
3217  * Results:
3218  *	None.
3219  *
3220  * Side effects:
3221  *	The widget is destroyed.
3222  *
3223  *----------------------------------------------------------------------
3224  */
3225 
3226 static void
ButtonCmdDeletedProc(clientData)3227 ButtonCmdDeletedProc(clientData)
3228     ClientData clientData;	/* Pointer to widget record for widget. */
3229 {
3230     Button *butPtr = clientData;
3231     Tk_Window tkwin = butPtr->tkwin;
3232 
3233     /*
3234      * This procedure could be invoked either because the window was
3235      * destroyed and the command was then deleted (in which case tkwin
3236      * is NULL) or because the command was deleted, and then this procedure
3237      * destroys the widget.
3238      */
3239 
3240     if (tkwin != NULL) {
3241 	butPtr->tkwin = NULL;
3242 #ifdef ITCL_NAMESPACES
3243 	Itk_SetWidgetCommand(tkwin, (Tcl_Command) NULL);
3244 #endif
3245 	Tk_DestroyWindow(tkwin);
3246     }
3247 }
3248 
3249 /*
3250  *----------------------------------------------------------------------
3251  *
3252  * InvokeButton --
3253  *
3254  *	This procedure is called to carry out the actions associated
3255  *	with a button, such as invoking a Tcl command or setting a
3256  *	variable.  This procedure is invoked, for example, when the
3257  *	button is invoked via the mouse.
3258  *
3259  * Results:
3260  *	A standard Tcl return value.  Information is also left in
3261  *	interp->result.
3262  *
3263  * Side effects:
3264  *	Depends on the button and its associated command.
3265  *
3266  *----------------------------------------------------------------------
3267  */
3268 
3269 static int
InvokeButton(butPtr)3270 InvokeButton(butPtr)
3271     register Button *butPtr;	/* Information about button. */
3272 {
3273     if ((butPtr->flags & BUTTON_DELETED)) {
3274         return TCL_OK;
3275     }
3276 
3277     if (butPtr->type == TYPE_CHECK_BUTTON) {
3278 	if (butPtr->flags & SELECTED) {
3279 	    if (ButtonSetValue(butPtr, butPtr->offValue, 1) != TCL_OK) {
3280 		return TCL_ERROR;
3281 	    }
3282 	} else {
3283 	    if (ButtonSetValue(butPtr, butPtr->onValue, 1) != TCL_OK) {
3284 		return TCL_ERROR;
3285 	    }
3286 	}
3287     } else if (butPtr->type == TYPE_RADIO_BUTTON) {
3288         if (ButtonSetValue(butPtr, butPtr->onValue, 1) != TCL_OK) {
3289 	    return TCL_ERROR;
3290 	}
3291     }
3292     if ((butPtr->type > TYPE_LABEL) && (butPtr->command != NULL)) {
3293         Tcl_DString buf;
3294         Tcl_DStringInit(&buf);
3295         Tcl_DStringAppend(&buf, butPtr->command, -1);
3296         int code = Tcl_EvalEx(butPtr->interp, Tcl_DStringValue(&buf), Tcl_DStringLength(&buf), TCL_EVAL_GLOBAL);
3297         Tcl_DStringFree(&buf);
3298 	return code;
3299     }
3300     return TCL_OK;
3301 }
3302 
3303 /* Trace tree operation. */
TreeTraceProc(ClientData clientData,Tcl_Interp * interp,Blt_TreeNode node,Blt_TreeKey key,unsigned int flags)3304 static int TreeTraceProc(ClientData clientData,
3305     Tcl_Interp *interp, Blt_TreeNode node, Blt_TreeKey key,
3306     unsigned int flags) {
3307     register Button *butPtr = clientData;
3308     Blt_TreeNode nodePtr;
3309     Tcl_Obj *valuePtr;
3310     CONST char *value;
3311     int doUpd = 0;
3312     nodePtr = Blt_TreeGetNode(butPtr->tree, butPtr->node);
3313     /* printf("TRC(%d)\n", (flags & TREE_TRACE_UNSET));  */
3314     if (nodePtr == NULL) {
3315         nodePtr = Blt_TreeCreateNode(butPtr->tree, Blt_TreeGetNode(butPtr->tree, 0), NULL, -1);
3316     }
3317     if (nodePtr == NULL) {
3318         return TCL_ERROR;
3319     }
3320 
3321     if (flags & TREE_TRACE_UNSET) {
3322         valuePtr = Tcl_NewStringObj("",-1);
3323         Blt_TreeSetValue(NULL, butPtr->tree,
3324             nodePtr, butPtr->selVarName, valuePtr);
3325         Tcl_AppendResult(interp, "can not delete node", 0);
3326         return TCL_ERROR;
3327         /*goto redisplay; */
3328     }
3329 
3330     if (Blt_TreeGetValue(NULL, butPtr->tree,
3331         nodePtr, butPtr->selVarName, &valuePtr) == TCL_OK) {
3332         value = Tcl_GetString(valuePtr);
3333     } else {
3334 	value = "";
3335     }
3336     if (butPtr->type == TYPE_CHECK_BUTTON && butPtr->tristateValue &&
3337         strcmp(value,butPtr->tristateValue)==0) {
3338         if ((butPtr->flags&TRISTATED) == 0) doUpd = 1;
3339         butPtr->flags |= TRISTATED;
3340     } else {
3341         if ((butPtr->flags&TRISTATED) != 0) doUpd = 1;
3342         butPtr->flags &= ~TRISTATED;
3343     }
3344     if (strcmp(value, butPtr->onValue) == 0) {
3345 	if (butPtr->flags & SELECTED) {
3346 	    if (doUpd) goto redisplay;
3347 	    return TCL_OK;
3348 	}
3349 	butPtr->flags |= SELECTED;
3350     } else if (butPtr->flags & SELECTED) {
3351 	butPtr->flags &= ~SELECTED;
3352     } else {
3353         if (doUpd) goto redisplay;
3354         return TCL_OK;
3355     }
3356 
3357  redisplay:
3358     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
3359     && !(butPtr->flags & REDRAW_PENDING)) {
3360         Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
3361         butPtr->flags |= REDRAW_PENDING;
3362     }
3363     return TCL_OK;
3364 }
3365 
3366 
3367 
3368 /*
3369  *--------------------------------------------------------------
3370  *
3371  * ButtonVarProc --
3372  *
3373  *	This procedure is invoked when someone changes the
3374  *	state variable associated with a radio button.  Depending
3375  *	on the new value of the button's variable, the button
3376  *	may be selected or deselected.
3377  *
3378  * Results:
3379  *	NULL is always returned.
3380  *
3381  * Side effects:
3382  *	The button may become selected or deselected.
3383  *
3384  *--------------------------------------------------------------
3385  */
3386 
3387  /* ARGSUSED */
3388 static char *
ButtonVarProc(clientData,interp,name1,name2,flags)3389 ButtonVarProc(clientData, interp, name1, name2, flags)
3390     ClientData clientData;	/* Information about button. */
3391     Tcl_Interp *interp;		/* Interpreter containing variable. */
3392     char *name1;		/* Name of variable. */
3393     char *name2;		/* Second part of variable name. */
3394     int flags;			/* Information about what happened. */
3395 {
3396     register Button *butPtr = clientData;
3397     CONST char *value;
3398     int doUpd = 0;
3399 
3400     if ((butPtr->flags & BUTTON_DELETED)) {
3401         return NULL;
3402     }
3403 
3404     /*
3405      * If the variable is being unset, then just re-establish the
3406      * trace unless the whole interpreter is going away.
3407      */
3408 
3409     if (flags & TCL_TRACE_UNSETS) {
3410 	butPtr->flags &= ~SELECTED;
3411 	if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
3412 	    Tcl_TraceVar(interp, butPtr->selVarName,
3413 		TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
3414 		ButtonVarProc, clientData);
3415 	}
3416 	goto redisplay;
3417     }
3418     /*
3419      * Use the value of the variable to update the selected status of
3420      * the button.
3421      */
3422 
3423     value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
3424     if (value == NULL) {
3425 	value = "";
3426     }
3427     if (butPtr->type == TYPE_CHECK_BUTTON && butPtr->tristateValue &&
3428         strcmp(value,butPtr->tristateValue)==0) {
3429         if ((butPtr->flags&TRISTATED) == 0) doUpd = 1;
3430         butPtr->flags |= TRISTATED;
3431     } else {
3432         if ((butPtr->flags&TRISTATED) != 0) doUpd = 1;
3433         butPtr->flags &= ~TRISTATED;
3434     }
3435     if (strcmp(value, butPtr->onValue) == 0) {
3436 	if (butPtr->flags & SELECTED) {
3437 	    if (doUpd) goto redisplay;
3438 	    return (char *) NULL;
3439 	}
3440 	butPtr->flags |= SELECTED;
3441     } else if (butPtr->flags & SELECTED) {
3442 	butPtr->flags &= ~SELECTED;
3443     } else {
3444         if (doUpd) goto redisplay;
3445         return (char *) NULL;
3446     }
3447 
3448   redisplay:
3449     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
3450 	&& !(butPtr->flags & REDRAW_PENDING)) {
3451 	Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
3452 	butPtr->flags |= REDRAW_PENDING;
3453     }
3454     return (char *) NULL;
3455 }
3456 /* Trace tree operation. */
TreeTextTraceProc(ClientData clientData,Tcl_Interp * interp,Blt_TreeNode node,Blt_TreeKey key,unsigned int flags)3457 static int TreeTextTraceProc(ClientData clientData,
3458     Tcl_Interp *interp, Blt_TreeNode node, Blt_TreeKey key,
3459     unsigned int flags) {
3460     register Button *butPtr = clientData;
3461     Blt_TreeNode nodePtr;
3462     Tcl_Obj *valuePtr;
3463     CONST char *value;
3464 
3465     if ((butPtr->flags & BUTTON_DELETED)) {
3466         return TCL_OK;
3467     }
3468 
3469     /* printf("TRC TXT(%d)\n", (flags & TREE_TRACE_UNSET));  */
3470     nodePtr = Blt_TreeGetNode(butPtr->tree, butPtr->node);
3471     if (nodePtr == NULL) {
3472         nodePtr = Blt_TreeCreateNode(butPtr->tree, Blt_TreeGetNode(butPtr->tree, 0), NULL, -1);
3473     }
3474     if (nodePtr == NULL) {
3475         return TCL_ERROR;
3476     }
3477 
3478     if (flags & TREE_TRACE_UNSET) {
3479         Tcl_AppendResult(interp, "can not delete node", 0);
3480         valuePtr = Tcl_NewStringObj(butPtr->textPtr,-1);
3481         Blt_TreeSetValue(NULL, butPtr->tree,
3482             nodePtr, butPtr->textVarName, valuePtr);
3483         return TCL_ERROR;
3484         /* goto redisplay; */
3485     }
3486 
3487     if (Blt_TreeGetValue(NULL, butPtr->tree,
3488         nodePtr, butPtr->textVarName, &valuePtr) == TCL_OK) {
3489         value = Tcl_GetString(valuePtr);
3490     } else {
3491 	value = "";
3492     }
3493     if (butPtr->textPtr != NULL) {
3494         Blt_Free(butPtr->textPtr);
3495     }
3496     butPtr->textPtr = Blt_Malloc(strlen(value) + 1);
3497     strcpy(butPtr->textPtr, value);
3498     ComputeButtonGeometry(butPtr);
3499 
3500 
3501     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
3502     && !(butPtr->flags & REDRAW_PENDING)) {
3503         Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
3504         butPtr->flags |= REDRAW_PENDING;
3505     }
3506     return TCL_OK;
3507 }
3508 
3509 
3510 
3511 /*
3512  *--------------------------------------------------------------
3513  *
3514  * ButtonTextVarProc --
3515  *
3516  *	This procedure is invoked when someone changes the variable
3517  *	whose contents are to be displayed in a button.
3518  *
3519  * Results:
3520  *	NULL is always returned.
3521  *
3522  * Side effects:
3523  *	The text displayed in the button will change to match the
3524  *	variable.
3525  *
3526  *--------------------------------------------------------------
3527  */
3528 
3529  /* ARGSUSED */
3530 static char *
ButtonTextVarProc(clientData,interp,name1,name2,flags)3531 ButtonTextVarProc(clientData, interp, name1, name2, flags)
3532     ClientData clientData;	/* Information about button. */
3533     Tcl_Interp *interp;		/* Interpreter containing variable. */
3534     char *name1;		/* Not used. */
3535     char *name2;		/* Not used. */
3536     int flags;			/* Information about what happened. */
3537 {
3538     register Button *butPtr = clientData;
3539     CONST char *value;
3540 
3541     if ((butPtr->flags & BUTTON_DELETED)) {
3542         return NULL;
3543     }
3544 
3545     /*
3546      * If the variable is unset, then immediately recreate it unless
3547      * the whole interpreter is going away.
3548      */
3549 
3550     if (flags & TCL_TRACE_UNSETS) {
3551 	if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
3552 	    Tcl_SetVar(interp, butPtr->textVarName, butPtr->textPtr,
3553 		TCL_GLOBAL_ONLY);
3554 	    Tcl_TraceVar(interp, butPtr->textVarName,
3555 		TCL_GLOBAL_ONLY | TCL_TRACE_WRITES | TCL_TRACE_UNSETS,
3556 		ButtonTextVarProc, clientData);
3557 	}
3558 	return (char *) NULL;
3559     }
3560     value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
3561     if (value == NULL) {
3562 	value = "";
3563     }
3564     if (butPtr->textPtr != NULL) {
3565 	Blt_Free(butPtr->textPtr);
3566     }
3567     butPtr->textPtr = Blt_Malloc(strlen(value) + 1);
3568     strcpy(butPtr->textPtr, value);
3569     ComputeButtonGeometry(butPtr);
3570 
3571     if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
3572 	&& !(butPtr->flags & REDRAW_PENDING)) {
3573 	Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
3574 	butPtr->flags |= REDRAW_PENDING;
3575     }
3576     return (char *) NULL;
3577 }
3578 
3579 /*
3580  *----------------------------------------------------------------------
3581  *
3582  * ButtonImageProc --
3583  *
3584  *	This procedure is invoked by the image code whenever the manager
3585  *	for an image does something that affects the size of contents
3586  *	of an image displayed in a button.
3587  *
3588  * Results:
3589  *	None.
3590  *
3591  * Side effects:
3592  *	Arranges for the button to get redisplayed.
3593  *
3594  *----------------------------------------------------------------------
3595  */
3596 
3597 /*ARGSUSED*/
3598 static void
ButtonImageProc(clientData,x,y,width,height,imgWidth,imgHeight)3599 ButtonImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
3600     ClientData clientData;	/* Pointer to widget record. */
3601     int x, y;			/* Upper left pixel (within image)
3602 					 * that must be redisplayed. */
3603     int width, height;		/* Dimensions of area to redisplay
3604 					 * (may be <= 0). */
3605     int imgWidth, imgHeight;	/* New dimensions of image. */
3606 {
3607     register Button *butPtr = clientData;
3608 
3609     if ((butPtr->flags & BUTTON_DELETED)) {
3610         return;
3611     }
3612 
3613     if (butPtr->tkwin != NULL) {
3614 	ComputeButtonGeometry(butPtr);
3615 	if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
3616 	    Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
3617 	    butPtr->flags |= REDRAW_PENDING;
3618 	}
3619     }
3620 }
3621 static void
ButtonImageBdProc(clientData,x,y,width,height,imgWidth,imgHeight)3622 ButtonImageBdProc(clientData, x, y, width, height, imgWidth, imgHeight)
3623     ClientData clientData;	/* Pointer to widget record. */
3624     int x, y;			/* Upper left pixel (within image)
3625 					 * that must be redisplayed. */
3626     int width, height;		/* Dimensions of area to redisplay
3627 					 * (may be <= 0). */
3628     int imgWidth, imgHeight;	/* New dimensions of image. */
3629 {
3630     register Button *butPtr = clientData;
3631 
3632     if ((butPtr->flags & BUTTON_DELETED)) {
3633         return;
3634     }
3635 
3636     if (butPtr->tkwin != NULL) {
3637         butPtr->flags |= BUTTON_DIRTYBD;
3638 	ComputeButtonGeometry(butPtr);
3639 	if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
3640 	    Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
3641 	    butPtr->flags |= REDRAW_PENDING;
3642 	}
3643     }
3644 }
3645 
3646 /*
3647  *----------------------------------------------------------------------
3648  *
3649  * ButtonSelectImageProc --
3650  *
3651  *	This procedure is invoked by the image code whenever the manager
3652  *	for an image does something that affects the size of contents
3653  *	of the image displayed in a button when it is selected.
3654  *
3655  * Results:
3656  *	None.
3657  *
3658  * Side effects:
3659  *	May arrange for the button to get redisplayed.
3660  *
3661  *----------------------------------------------------------------------
3662  */
3663 
3664 /*ARGSUSED*/
3665 static void
ButtonSelectImageProc(clientData,x,y,width,height,imgWidth,imgHeight)3666 ButtonSelectImageProc(clientData, x, y, width, height, imgWidth, imgHeight)
3667     ClientData clientData;	/* Pointer to widget record. */
3668     int x, y;			/* Upper left pixel (within image)
3669 					 * that must be redisplayed. */
3670     int width, height;		/* Dimensions of area to redisplay
3671 					 * (may be <= 0). */
3672     int imgWidth, imgHeight;	/* New dimensions of image. */
3673 {
3674     register Button *butPtr = clientData;
3675 
3676     if ((butPtr->flags & BUTTON_DELETED)) {
3677         return;
3678     }
3679 
3680     /*
3681      * Don't recompute geometry:  it's controlled by the primary image.
3682      */
3683 
3684     if ((butPtr->flags & SELECTED) && (butPtr->tkwin != NULL)
3685 	&& Tk_IsMapped(butPtr->tkwin)
3686 	&& !(butPtr->flags & REDRAW_PENDING)) {
3687 	Tcl_DoWhenIdle(DisplayButton, (ClientData)butPtr);
3688 	butPtr->flags |= REDRAW_PENDING;
3689     }
3690 }
3691 
3692 int
Blt_ButtonInit(interp)3693 Blt_ButtonInit(interp)
3694     Tcl_Interp *interp;
3695 {
3696     static Blt_CmdSpec cmdSpecs[6] =
3697     {
3698 	{"frame", FrameCmd,},
3699 	{"button", ButtonCmd,},
3700 	{"checkbutton", CheckbuttonCmd,},
3701 	{"radiobutton", RadiobuttonCmd,},
3702 	{"label", LabelCmd,},
3703         {"menubutton", MenubuttonCmd,},
3704     };
3705     static Blt_CmdSpec cmdBSpecs[6] =
3706     {
3707 	{"frame", BFrameCmd,},
3708 	{"button", BButtonCmd,},
3709 	{"checkbutton", BCheckbuttonCmd,},
3710 	{"radiobutton", BRadiobuttonCmd,},
3711 	{"label", BLabelCmd,},
3712         {"menubutton", BMenubuttonCmd,},
3713     };
3714     int result;
3715     tkNormalUid = Tk_GetUid("normal");
3716     tkDisabledUid = Tk_GetUid("disabled");
3717     tkActiveUid = Tk_GetUid("active");
3718     result = Blt_InitCmds(interp, "blt::tile", cmdSpecs, 6);
3719     if (result == TCL_OK) {
3720         result = Blt_InitCmds(interp, "blt::widget", cmdBSpecs, 6);
3721     }
3722     return result;
3723 }
3724 
3725 #endif /* NO_TILEBUTTON */
3726