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