|
|
1.1 root 1: /*
2: * tkButton.c --
3: *
4: * This module implements a collection of button-like
5: * widgets for the Tk toolkit. The widgets implemented
6: * include labels, buttons, check buttons, and radio
7: * buttons.
8: *
9: * Copyright 1990-1992 Regents of the University of California.
10: * Permission to use, copy, modify, and distribute this
11: * software and its documentation for any purpose and without
12: * fee is hereby granted, provided that the above copyright
13: * notice appear in all copies. The University of California
14: * makes no representations about the suitability of this
15: * software for any purpose. It is provided "as is" without
16: * express or implied warranty.
17: */
18:
19: #ifndef lint
20: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkButton.c,v 1.69 92/08/21 11:42:47 ouster Exp $ SPRITE (Berkeley)";
21: #endif
22:
23: #include "default.h"
24: #include "tkconfig.h"
25: #include "tkint.h"
26:
27: #include <assert.h>
28:
29: /*
30: * A data structure of the following type is kept for each
31: * widget managed by this file:
32: */
33:
34: typedef struct {
35: Tk_Window tkwin; /* Window that embodies the button. NULL
36: * means that the window has been destroyed. */
37: Tcl_Interp *interp; /* Interpreter associated with button. */
38: int type; /* Type of widget: restricts operations
39: * that may be performed on widget. See
40: * below for possible values. */
41:
42: /*
43: * Information about what's in the button.
44: */
45:
46: char *text; /* Text to display in button (malloc'ed)
47: * or NULL. */
48: int textLength; /* # of characters in text. */
49: char *textVarName; /* Name of variable (malloc'ed) or NULL.
50: * If non-NULL, button displays the contents
51: * of this variable. */
52: Pixmap bitmap; /* Bitmap to display or None. If not None
53: * then text and textVar are ignored. */
54:
55: /*
56: * Information used when displaying widget:
57: */
58:
59: Tk_Uid state; /* State of button for display purposes:
60: * normal, active, or disabled. */
61: Tk_3DBorder normalBorder; /* Structure used to draw 3-D
62: * border and background when window
63: * isn't active. NULL means no such
64: * border exists. */
65: Tk_3DBorder activeBorder; /* Structure used to draw 3-D
66: * border and background when window
67: * is active. NULL means no such
68: * border exists. */
69: int borderWidth; /* Width of border. */
70: int relief; /* 3-d effect: TK_RELIEF_RAISED, etc. */
71: XFontStruct *fontPtr; /* Information about text font, or NULL. */
72: XColor *normalFg; /* Foreground color in normal mode. */
73: XColor *activeFg; /* Foreground color in active mode. NULL
74: * means use normalFg instead. */
75: XColor *disabledFg; /* Foreground color when disabled. NULL
76: * means use normalFg with a 50% stipple
77: * instead. */
78: GC normalTextGC; /* GC for drawing text in normal mode. Also
79: * used to copy from off-screen pixmap onto
80: * screen. */
81: GC activeTextGC; /* GC for drawing text in active mode (NULL
82: * means use normalTextGC). */
83: Pixmap gray; /* Pixmap for displaying disabled text if
84: * disabledFg is NULL. */
85: GC disabledGC; /* Used to produce disabled effect. If
86: * disabledFg isn't NULL, this GC is used to
87: * draw button text or icon. Otherwise
88: * text or icon is drawn with normalGC and
89: * this GC is used to stipple background
90: * across it. */
91: int leftBearing; /* Amount text sticks left from its origin,
92: * in pixels. */
93: int rightBearing; /* Amount text sticks right from its origin. */
94: int width, height; /* If > 0, these specify dimensions to request
95: * for window, in characters for text and in
96: * pixels for bitmaps. In this case the actual
97: * size of the text string or bitmap is
98: * ignored in computing desired window size. */
99: int padX, padY; /* Extra space around text or bitmap (pixels
100: * on each side). */
101: Tk_Anchor anchor; /* Where text/bitmap should be displayed
102: * inside button region. */
103: XColor *selectorFg; /* Color for selector. */
104: GC selectorGC; /* For drawing highlight when this button
105: * is in selected state. */
106: int selectorSpace; /* Horizontal space (in pixels) allocated for
107: * display of selector. */
108: int selectorDiameter; /* Diameter of selector, in pixels. */
109:
110: /*
111: * For check and radio buttons, the fields below are used
112: * to manage the variable indicating the button's state.
113: */
114:
115: char *selVarName; /* Name of variable used to control selected
116: * state of button. Malloc'ed (if
117: * not NULL). */
118: char *onValue; /* Value to store in variable when
119: * this button is selected. Malloc'ed (if
120: * not NULL). */
121: char *offValue; /* Value to store in variable when this
122: * button isn't selected. Malloc'ed
123: * (if not NULL). Valid only for check
124: * buttons. */
125:
126: /*
127: * Miscellaneous information:
128: */
129:
130: Cursor cursor; /* Current cursor for window, or None. */
131: char *command; /* Command to execute when button is
132: * invoked; valid for buttons only.
133: * If not NULL, it's malloc-ed. */
134: int flags; /* Various flags; see below for
135: * definitions. */
136: Tk_TimerToken updateTimerToken; /* Added by Don to optimize rapid
137: * updates. */
138: } Button;
139:
140: /*
141: * Possible "type" values for buttons. These are the kinds of
142: * widgets supported by this file. The ordering of the type
143: * numbers is significant: greater means more features and is
144: * used in the code.
145: */
146:
147: #define TYPE_LABEL 0
148: #define TYPE_BUTTON 1
149: #define TYPE_CHECK_BUTTON 2
150: #define TYPE_RADIO_BUTTON 3
151:
152: /*
153: * Class names for buttons, indexed by one of the type values above.
154: */
155:
156: static char *classNames[] = {"Label", "Button", "CheckButton", "RadioButton"};
157:
158: /*
159: * Flag bits for buttons:
160: *
161: * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
162: * has already been queued to redraw
163: * this window.
164: * SELECTED: Non-zero means this button is selected,
165: * so special highlight should be drawn.
166: */
167:
168: #define REDRAW_PENDING 1
169: #define SELECTED 2
170:
171: /*
172: * Mask values used to selectively enable entries in the
173: * configuration specs:
174: */
175:
176: #define LABEL_MASK TK_CONFIG_USER_BIT
177: #define BUTTON_MASK TK_CONFIG_USER_BIT << 1
178: #define CHECK_BUTTON_MASK TK_CONFIG_USER_BIT << 2
179: #define RADIO_BUTTON_MASK TK_CONFIG_USER_BIT << 3
180: #define ALL_MASK (LABEL_MASK | BUTTON_MASK \
181: | CHECK_BUTTON_MASK | RADIO_BUTTON_MASK)
182:
183: static int configFlags[] = {LABEL_MASK, BUTTON_MASK,
184: CHECK_BUTTON_MASK, RADIO_BUTTON_MASK};
185: /*
186: * Information used for parsing configuration specs:
187: */
188:
189: static Tk_ConfigSpec configSpecs[] = {
190: {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
191: DEF_BUTTON_ACTIVE_BG_COLOR, Tk_Offset(Button, activeBorder),
192: BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
193: |TK_CONFIG_COLOR_ONLY},
194: {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
195: DEF_BUTTON_ACTIVE_BG_MONO, Tk_Offset(Button, activeBorder),
196: BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
197: |TK_CONFIG_MONO_ONLY},
198: {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
199: DEF_BUTTON_ACTIVE_FG_COLOR, Tk_Offset(Button, activeFg),
200: BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
201: |TK_CONFIG_COLOR_ONLY},
202: {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
203: DEF_BUTTON_ACTIVE_FG_MONO, Tk_Offset(Button, activeFg),
204: BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK
205: |TK_CONFIG_MONO_ONLY},
206: {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
207: DEF_BUTTON_ANCHOR, Tk_Offset(Button, anchor), ALL_MASK},
208: {TK_CONFIG_BORDER, "-background", "background", "Background",
209: DEF_BUTTON_BG_COLOR, Tk_Offset(Button, normalBorder),
210: ALL_MASK | TK_CONFIG_COLOR_ONLY},
211: {TK_CONFIG_BORDER, "-background", "background", "Background",
212: DEF_BUTTON_BG_MONO, Tk_Offset(Button, normalBorder),
213: ALL_MASK | TK_CONFIG_MONO_ONLY},
214: {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
215: (char *) NULL, 0, ALL_MASK},
216: {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
217: (char *) NULL, 0, ALL_MASK},
218: #if defined(USE_XPM3)
219: {TK_CONFIG_PIXMAP, "-bitmap", "bitmap", "Bitmap",
220: DEF_BUTTON_BITMAP, Tk_Offset(Button, bitmap),
221: ALL_MASK|TK_CONFIG_NULL_OK},
222: #else
223: {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
224: DEF_BUTTON_BITMAP, Tk_Offset(Button, bitmap),
225: ALL_MASK|TK_CONFIG_NULL_OK},
226: #endif
227: {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
228: DEF_BUTTON_BORDER_WIDTH, Tk_Offset(Button, borderWidth), ALL_MASK},
229: {TK_CONFIG_STRING, "-command", "command", "Command",
230: DEF_BUTTON_COMMAND, Tk_Offset(Button, command),
231: BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
232: {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
233: DEF_BUTTON_CURSOR, Tk_Offset(Button, cursor),
234: ALL_MASK|TK_CONFIG_NULL_OK},
235: {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
236: "DisabledForeground", DEF_BUTTON_DISABLED_FG_COLOR,
237: Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
238: |RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
239: {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
240: "DisabledForeground", DEF_BUTTON_DISABLED_FG_MONO,
241: Tk_Offset(Button, disabledFg), BUTTON_MASK|CHECK_BUTTON_MASK
242: |RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
243: {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
244: (char *) NULL, 0, ALL_MASK},
245: {TK_CONFIG_FONT, "-font", "font", "Font",
246: DEF_BUTTON_FONT, Tk_Offset(Button, fontPtr),
247: ALL_MASK},
248: {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
249: DEF_BUTTON_FG, Tk_Offset(Button, normalFg), ALL_MASK},
250: {TK_CONFIG_INT, "-height", "height", "Height",
251: DEF_BUTTON_HEIGHT, Tk_Offset(Button, height), ALL_MASK},
252: {TK_CONFIG_STRING, "-offvalue", "offValue", "Value",
253: DEF_BUTTON_OFF_VALUE, Tk_Offset(Button, offValue),
254: CHECK_BUTTON_MASK},
255: {TK_CONFIG_STRING, "-onvalue", "onValue", "Value",
256: DEF_BUTTON_ON_VALUE, Tk_Offset(Button, onValue),
257: CHECK_BUTTON_MASK},
258: {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
259: DEF_BUTTON_PADX, Tk_Offset(Button, padX), ALL_MASK},
260: {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
261: DEF_BUTTON_PADY, Tk_Offset(Button, padY), ALL_MASK},
262: {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
263: DEF_BUTTON_RELIEF, Tk_Offset(Button, relief),
264: BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
265: {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
266: DEF_LABEL_RELIEF, Tk_Offset(Button, relief), LABEL_MASK},
267: {TK_CONFIG_COLOR, "-selector", "selector", "Foreground",
268: DEF_BUTTON_SELECTOR_COLOR, Tk_Offset(Button, selectorFg),
269: CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_COLOR_ONLY
270: |TK_CONFIG_NULL_OK},
271: {TK_CONFIG_COLOR, "-selector", "selector", "Foreground",
272: DEF_BUTTON_SELECTOR_MONO, Tk_Offset(Button, selectorFg),
273: CHECK_BUTTON_MASK|RADIO_BUTTON_MASK|TK_CONFIG_MONO_ONLY
274: |TK_CONFIG_NULL_OK},
275: {TK_CONFIG_UID, "-state", "state", "State",
276: DEF_BUTTON_STATE, Tk_Offset(Button, state),
277: BUTTON_MASK|CHECK_BUTTON_MASK|RADIO_BUTTON_MASK},
278: {TK_CONFIG_STRING, "-text", "text", "Text",
279: DEF_BUTTON_TEXT, Tk_Offset(Button, text), ALL_MASK},
280: {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
281: DEF_BUTTON_TEXT_VARIABLE, Tk_Offset(Button, textVarName),
282: ALL_MASK|TK_CONFIG_NULL_OK},
283: {TK_CONFIG_STRING, "-value", "value", "Value",
284: DEF_BUTTON_VALUE, Tk_Offset(Button, onValue),
285: RADIO_BUTTON_MASK},
286: {TK_CONFIG_STRING, "-variable", "variable", "Variable",
287: DEF_RADIOBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
288: RADIO_BUTTON_MASK},
289: {TK_CONFIG_STRING, "-variable", "variable", "Variable",
290: DEF_CHECKBUTTON_VARIABLE, Tk_Offset(Button, selVarName),
291: CHECK_BUTTON_MASK},
292: {TK_CONFIG_INT, "-width", "width", "Width",
293: DEF_BUTTON_WIDTH, Tk_Offset(Button, width), ALL_MASK},
294: {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
295: (char *) NULL, 0, 0}
296: };
297:
298: /*
299: * String to print out in error messages, identifying options for
300: * widget commands for different types of labels or buttons:
301: */
302:
303: static char *optionStrings[] = {
304: "configure",
305: "activate, configure, deactivate, flash, or invoke",
306: "activate, configure, deactivate, deselect, flash, invoke, select, or toggle",
307: "activate, configure, deactivate, deselect, flash, invoke, or select"
308: };
309:
310: static int ButtonUpdateTime = 200; // Added by Don.
311:
312: /*
313: * Forward declarations for procedures defined later in this file:
314: */
315:
316: static void ButtonEventProc _ANSI_ARGS_((ClientData clientData,
317: XEvent *eventPtr));
318: static char * ButtonTextVarProc _ANSI_ARGS_((ClientData clientData,
319: Tcl_Interp *interp, char *name1, char *name2,
320: int flags));
321: static char * ButtonVarProc _ANSI_ARGS_((ClientData clientData,
322: Tcl_Interp *interp, char *name1, char *name2,
323: int flags));
324: static int ButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
325: Tcl_Interp *interp, int argc, char **argv));
326: static void ComputeButtonGeometry _ANSI_ARGS_((Button *butPtr));
327: static int ConfigureButton _ANSI_ARGS_((Tcl_Interp *interp,
328: Button *butPtr, int argc, char **argv,
329: int flags));
330: static void DestroyButton _ANSI_ARGS_((ClientData clientData));
331: static void DisplayButton _ANSI_ARGS_((ClientData clientData));
332: static int InvokeButton _ANSI_ARGS_((Button *butPtr));
333:
334: /*
335: *--------------------------------------------------------------
336: *
337: * Tk_ButtonCmd --
338: *
339: * This procedure is invoked to process the "button", "label",
340: * "radiobutton", and "checkbutton" Tcl commands. See the
341: * user documentation for details on what it does.
342: *
343: * Results:
344: * A standard Tcl result.
345: *
346: * Side effects:
347: * See the user documentation.
348: *
349: *--------------------------------------------------------------
350: */
351:
352: int
353: Tk_ButtonCmd(clientData, interp, argc, argv)
354: ClientData clientData; /* Main window associated with
355: * interpreter. */
356: Tcl_Interp *interp; /* Current interpreter. */
357: int argc; /* Number of arguments. */
358: char **argv; /* Argument strings. */
359: {
360: register Button *butPtr;
361: int type;
362: Tk_Window tkwin = (Tk_Window) clientData;
363: Tk_Window new;
364:
365: if (argc < 2) {
366: Tcl_AppendResult(interp, "wrong # args: should be \"",
367: argv[0], " pathName ?options?\"", (char *) NULL);
368: return TCL_ERROR;
369: }
370:
371: switch (argv[0][0]) {
372: case 'l':
373: type = TYPE_LABEL;
374: break;
375: case 'b':
376: type = TYPE_BUTTON;
377: break;
378: case 'c':
379: type = TYPE_CHECK_BUTTON;
380: break;
381: case 'r':
382: type = TYPE_RADIO_BUTTON;
383: break;
384: default:
385: sprintf(interp->result,
386: "unknown button-creation command \"%.50s\"");
387: return TCL_ERROR;
388: }
389:
390: /*
391: * Create the new window.
392: */
393:
394: new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
395: if (new == NULL) {
396: return TCL_ERROR;
397: }
398:
399: /*
400: * Initialize the data structure for the button.
401: */
402:
403: butPtr = (Button *) ckalloc(sizeof(Button));
404: butPtr->tkwin = new;
405: butPtr->interp = interp;
406: butPtr->type = type;
407: butPtr->text = NULL;
408: butPtr->textVarName = NULL;
409: butPtr->bitmap = None;
410: butPtr->state = tkNormalUid;
411: butPtr->normalBorder = NULL;
412: butPtr->activeBorder = NULL;
413: butPtr->borderWidth = 0;
414: butPtr->relief = TK_RELIEF_FLAT;
415: butPtr->fontPtr = NULL;
416: butPtr->normalFg = NULL;
417: butPtr->activeFg = NULL;
418: butPtr->disabledFg = NULL;
419: butPtr->normalTextGC = None;
420: butPtr->activeTextGC = None;
421: butPtr->gray = None;
422: butPtr->disabledGC = None;
423: butPtr->selectorFg = NULL;
424: butPtr->selectorGC = None;
425: butPtr->selVarName = NULL;
426: butPtr->onValue = NULL;
427: butPtr->offValue = NULL;
428: butPtr->cursor = None;
429: butPtr->command = NULL;
430: butPtr->flags = 0;
431: butPtr->updateTimerToken = 0;
432:
433: Tk_SetClass(new, classNames[type]);
434: //fprintf(stderr, "ButtonWidgetCmd Made %s %s\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin));
435: Tk_CreateEventHandler(butPtr->tkwin, ExposureMask|StructureNotifyMask,
436: ButtonEventProc, (ClientData) butPtr);
437: Tcl_CreateCommand(interp, Tk_PathName(butPtr->tkwin), ButtonWidgetCmd,
438: (ClientData) butPtr, (void (*)()) NULL);
439: if (ConfigureButton(interp, butPtr, argc-2, argv+2,
440: configFlags[type]) != TCL_OK) {
441: Tk_DestroyWindow(butPtr->tkwin);
442: return TCL_ERROR;
443: }
444:
445: interp->result = Tk_PathName(butPtr->tkwin);
446: return TCL_OK;
447: }
448:
449: /*
450: *--------------------------------------------------------------
451: *
452: * ButtonWidgetCmd --
453: *
454: * This procedure is invoked to process the Tcl command
455: * that corresponds to a widget managed by this module.
456: * See the user documentation for details on what it does.
457: *
458: * Results:
459: * A standard Tcl result.
460: *
461: * Side effects:
462: * See the user documentation.
463: *
464: *--------------------------------------------------------------
465: */
466:
467: static int
468: ButtonWidgetCmd(clientData, interp, argc, argv)
469: ClientData clientData; /* Information about button widget. */
470: Tcl_Interp *interp; /* Current interpreter. */
471: int argc; /* Number of arguments. */
472: char **argv; /* Argument strings. */
473: {
474: register Button *butPtr = (Button *) clientData;
475: int result = TCL_OK;
476: int length;
477: char c;
478:
479: if (argc < 2) {
480: sprintf(interp->result,
481: "wrong # args: should be \"%.50s option [arg arg ...]\"",
482: argv[0]);
483: return TCL_ERROR;
484: }
485: Tk_Preserve((ClientData) butPtr);
486: c = argv[1][0];
487: length = strlen(argv[1]);
488: if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)
489: && (butPtr->type != TYPE_LABEL)) {
490: if (argc > 2) {
491: sprintf(interp->result,
492: "wrong # args: should be \"%.50s activate\"",
493: argv[0]);
494: goto error;
495: }
496: if (butPtr->state != tkDisabledUid) {
497: butPtr->state = tkActiveUid;
498: Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
499: goto redisplay;
500: }
501: } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
502: if (argc == 2) {
503: result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
504: (char *) butPtr, (char *) NULL, configFlags[butPtr->type]);
505: } else if (argc == 3) {
506: result = Tk_ConfigureInfo(interp, butPtr->tkwin, configSpecs,
507: (char *) butPtr, argv[2],
508: configFlags[butPtr->type]);
509: } else {
510: result = ConfigureButton(interp, butPtr, argc-2, argv+2,
511: configFlags[butPtr->type] | TK_CONFIG_ARGV_ONLY);
512: }
513: } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)
514: && (length > 2) && (butPtr->type != TYPE_LABEL)) {
515: if (argc > 2) {
516: sprintf(interp->result,
517: "wrong # args: should be \"%.50s deactivate\"",
518: argv[0]);
519: goto error;
520: }
521: if (butPtr->state != tkDisabledUid) {
522: butPtr->state = tkNormalUid;
523: Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
524: goto redisplay;
525: }
526: } else if ((c == 'd') && (strncmp(argv[1], "deselect", length) == 0)
527: && (length > 2) && (butPtr->type >= TYPE_CHECK_BUTTON)) {
528: if (argc > 2) {
529: sprintf(interp->result,
530: "wrong # args: should be \"%.50s deselect\"",
531: argv[0]);
532: goto error;
533: }
534: if (butPtr->type == TYPE_CHECK_BUTTON) {
535: Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue,
536: TCL_GLOBAL_ONLY);
537: } else if (butPtr->flags & SELECTED) {
538: Tcl_SetVar(interp, butPtr->selVarName, "", TCL_GLOBAL_ONLY);
539: }
540: } else if ((c == 'f') && (strncmp(argv[1], "flash", length) == 0)
541: && (butPtr->type != TYPE_LABEL)) {
542: int i;
543:
544: if (argc > 2) {
545: sprintf(interp->result,
546: "wrong # args: should be \"%.50s flash\"",
547: argv[0]);
548: goto error;
549: }
550: if (butPtr->state != tkDisabledUid) {
551: for (i = 0; i < 4; i++) {
552: butPtr->state = (butPtr->state == tkNormalUid)
553: ? tkActiveUid : tkNormalUid;
554: Tk_SetBackgroundFromBorder(butPtr->tkwin,
555: (butPtr->state == tkActiveUid) ? butPtr->activeBorder
556: : butPtr->normalBorder);
557: DisplayButton((ClientData) butPtr);
558: XFlush(Tk_Display(butPtr->tkwin));
559: Tk_Sleep(50);
560: }
561: }
562: } else if ((c == 'i') && (strncmp(argv[1], "invoke", length) == 0)
563: && (butPtr->type > TYPE_LABEL)) {
564: if (argc > 2) {
565: sprintf(interp->result,
566: "wrong # args: should be \"%.50s invoke\"",
567: argv[0]);
568: goto error;
569: }
570: if (butPtr->state != tkDisabledUid) {
571: result = InvokeButton(butPtr);
572: }
573: } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
574: && (butPtr->type >= TYPE_CHECK_BUTTON)) {
575: if (argc > 2) {
576: sprintf(interp->result,
577: "wrong # args: should be \"%.50s select\"",
578: argv[0]);
579: goto error;
580: }
581: Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY);
582: } else if ((c == 't') && (strncmp(argv[1], "toggle", length) == 0)
583: && (length >= 2) && (butPtr->type == TYPE_CHECK_BUTTON)) {
584: if (argc > 2) {
585: sprintf(interp->result,
586: "wrong # args: should be \"%.50s select\"",
587: argv[0]);
588: goto error;
589: }
590: if (butPtr->flags & SELECTED) {
591: Tcl_SetVar(interp, butPtr->selVarName, butPtr->offValue, TCL_GLOBAL_ONLY);
592: } else {
593: Tcl_SetVar(interp, butPtr->selVarName, butPtr->onValue, TCL_GLOBAL_ONLY);
594: }
595: } else {
596: sprintf(interp->result,
597: "bad option \"%.50s\": must be %s", argv[1],
598: optionStrings[butPtr->type]);
599: goto error;
600: }
601: Tk_Release((ClientData) butPtr);
602: return result;
603:
604: redisplay:
605: if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
606: //Tk_TimerToken last = butPtr->updateTimerToken;
607: butPtr->flags |= REDRAW_PENDING;
608: // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
609: assert(butPtr->updateTimerToken == 0);
610: if (butPtr->updateTimerToken == 0) {
611: butPtr->updateTimerToken =
612: Tk_CreateTimerHandler(
613: ButtonUpdateTime,
614: DisplayButton,
615: (ClientData) butPtr);
616: }
617: //fprintf(stderr, "ButtonWidgetCmd Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
618: }
619: Tk_Release((ClientData) butPtr);
620: return TCL_OK;
621:
622: error:
623: Tk_Release((ClientData) butPtr);
624: return TCL_ERROR;
625: }
626:
627: /*
628: *----------------------------------------------------------------------
629: *
630: * DestroyButton --
631: *
632: * This procedure is invoked by Tk_EventuallyFree or Tk_Release
633: * to clean up the internal structure of a button at a safe time
634: * (when no-one is using it anymore).
635: *
636: * Results:
637: * None.
638: *
639: * Side effects:
640: * Everything associated with the widget is freed up.
641: *
642: *----------------------------------------------------------------------
643: */
644:
645: static void
646: DestroyButton(clientData)
647: ClientData clientData; /* Info about entry widget. */
648: {
649: register Button *butPtr = (Button *) clientData;
650:
651: if (butPtr->text != NULL) {
652: ckfree(butPtr->text);
653: }
654: if (butPtr->textVarName != NULL) {
655: Tcl_UntraceVar(butPtr->interp, butPtr->textVarName,
656: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
657: ButtonTextVarProc, (ClientData) butPtr);
658: ckfree(butPtr->textVarName);
659: }
660: if (butPtr->bitmap != None) {
661: #if defined(USE_XPM3)
662: Tk_FreePixmap(butPtr->bitmap);
663: #else
664: Tk_FreeBitmap(butPtr->bitmap);
665: #endif
666: }
667: if (butPtr->normalBorder != NULL) {
668: Tk_Free3DBorder(butPtr->normalBorder);
669: }
670: if (butPtr->activeBorder != NULL) {
671: Tk_Free3DBorder(butPtr->activeBorder);
672: }
673: if (butPtr->fontPtr != NULL) {
674: Tk_FreeFontStruct(butPtr->fontPtr);
675: }
676: if (butPtr->normalFg != NULL) {
677: Tk_FreeColor(butPtr->normalFg);
678: }
679: if (butPtr->disabledFg != NULL) {
680: Tk_FreeColor(butPtr->disabledFg);
681: }
682: if (butPtr->activeFg != NULL) {
683: Tk_FreeColor(butPtr->activeFg);
684: }
685: if (butPtr->normalTextGC != None) {
686: Tk_FreeGC(butPtr->normalTextGC);
687: }
688: if (butPtr->activeTextGC != None) {
689: Tk_FreeGC(butPtr->activeTextGC);
690: }
691: if (butPtr->gray != None) {
692: Tk_FreeBitmap(butPtr->gray);
693: }
694: if (butPtr->disabledGC != None) {
695: Tk_FreeGC(butPtr->disabledGC);
696: }
697: if (butPtr->selectorFg != NULL) {
698: Tk_FreeColor(butPtr->selectorFg);
699: }
700: if (butPtr->selectorGC != None) {
701: Tk_FreeGC(butPtr->selectorGC);
702: }
703: if (butPtr->selVarName != NULL) {
704: Tcl_UntraceVar(butPtr->interp, butPtr->selVarName,
705: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
706: ButtonVarProc, (ClientData) butPtr);
707: ckfree(butPtr->selVarName);
708: }
709: if (butPtr->onValue != NULL) {
710: ckfree(butPtr->onValue);
711: }
712: if (butPtr->offValue != NULL) {
713: ckfree(butPtr->offValue);
714: }
715: if (butPtr->cursor != None) {
716: Tk_FreeCursor(butPtr->cursor);
717: }
718: if (butPtr->command != NULL) {
719: ckfree(butPtr->command);
720: }
721: if (butPtr->updateTimerToken != NULL) {
722: Tk_DeleteTimerHandler(butPtr->updateTimerToken);
723: //fprintf(stderr, "DestroyButton Delete Timer was %d now 0\n", butPtr->updateTimerToken);
724: butPtr->updateTimerToken = 0;
725:
726: }
727: ckfree((char *) butPtr);
728: }
729:
730: /*
731: *----------------------------------------------------------------------
732: *
733: * ConfigureButton --
734: *
735: * This procedure is called to process an argv/argc list, plus
736: * the Tk option database, in order to configure (or
737: * reconfigure) a button widget.
738: *
739: * Results:
740: * The return value is a standard Tcl result. If TCL_ERROR is
741: * returned, then interp->result contains an error message.
742: *
743: * Side effects:
744: * Configuration information, such as text string, colors, font,
745: * etc. get set for butPtr; old resources get freed, if there
746: * were any. The button is redisplayed.
747: *
748: *----------------------------------------------------------------------
749: */
750:
751: static int
752: ConfigureButton(interp, butPtr, argc, argv, flags)
753: Tcl_Interp *interp; /* Used for error reporting. */
754: register Button *butPtr; /* Information about widget; may or may
755: * not already have values for some fields. */
756: int argc; /* Number of valid entries in argv. */
757: char **argv; /* Arguments. */
758: int flags; /* Flags to pass to Tk_ConfigureWidget. */
759: {
760: XGCValues gcValues;
761: GC newGC;
762: unsigned long mask;
763:
764: /*
765: * Eliminate any existing trace on variables monitored by the button.
766: */
767:
768: if (butPtr->textVarName != NULL) {
769: Tcl_UntraceVar(interp, butPtr->textVarName,
770: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
771: ButtonTextVarProc, (ClientData) butPtr);
772: }
773: if (butPtr->selVarName != NULL) {
774: Tcl_UntraceVar(interp, butPtr->selVarName,
775: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
776: ButtonVarProc, (ClientData) butPtr);
777: }
778:
779: if (Tk_ConfigureWidget(interp, butPtr->tkwin, configSpecs,
780: argc, argv, (char *) butPtr, flags) != TCL_OK) {
781: return TCL_ERROR;
782: }
783:
784: /*
785: * A few options need special processing, such as setting the
786: * background from a 3-D border, or filling in complicated
787: * defaults that couldn't be specified to Tk_ConfigureWidget.
788: */
789:
790: if (butPtr->state == tkActiveUid) {
791: Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->activeBorder);
792: } else {
793: Tk_SetBackgroundFromBorder(butPtr->tkwin, butPtr->normalBorder);
794: if ((butPtr->state != tkNormalUid)
795: && (butPtr->state != tkDisabledUid)) {
796: Tcl_AppendResult(interp, "bad state value \"", butPtr->state,
797: "\": must be normal, active, or disabled", (char *) NULL);
798: butPtr->state = tkNormalUid;
799: return TCL_ERROR;
800: }
801: }
802:
803: gcValues.font = butPtr->fontPtr->fid;
804: gcValues.foreground = butPtr->normalFg->pixel;
805: gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
806:
807: /*
808: * Note: GraphicsExpose events are disabled in normalTextGC because it's
809: * used to copy stuff from an off-screen pixmap onto the screen (we know
810: * that there's no problem with obscured areas).
811: */
812:
813: gcValues.graphics_exposures = False;
814: newGC = Tk_GetGC(butPtr->tkwin,
815: GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcValues);
816: if (butPtr->normalTextGC != None) {
817: Tk_FreeGC(butPtr->normalTextGC);
818: }
819: butPtr->normalTextGC = newGC;
820:
821: if (butPtr->activeFg != NULL) {
822: gcValues.font = butPtr->fontPtr->fid;
823: gcValues.foreground = butPtr->activeFg->pixel;
824: gcValues.background = Tk_3DBorderColor(butPtr->activeBorder)->pixel;
825: newGC = Tk_GetGC(butPtr->tkwin, GCForeground|GCBackground|GCFont,
826: &gcValues);
827: if (butPtr->activeTextGC != None) {
828: Tk_FreeGC(butPtr->activeTextGC);
829: }
830: butPtr->activeTextGC = newGC;
831: }
832:
833: gcValues.font = butPtr->fontPtr->fid;
834: gcValues.background = Tk_3DBorderColor(butPtr->normalBorder)->pixel;
835: if (butPtr->disabledFg != NULL) {
836: gcValues.foreground = butPtr->disabledFg->pixel;
837: mask = GCForeground|GCBackground|GCFont;
838: } else {
839: gcValues.foreground = gcValues.background;
840: if (butPtr->gray == None) {
841: butPtr->gray = Tk_GetBitmap(interp, butPtr->tkwin,
842: Tk_GetUid("gray50"));
843: if (butPtr->gray == None) {
844: return TCL_ERROR;
845: }
846: }
847: gcValues.fill_style = FillStippled;
848: gcValues.stipple = butPtr->gray;
849: mask = GCForeground|GCFillStyle|GCStipple;
850: }
851: newGC = Tk_GetGC(butPtr->tkwin, mask, &gcValues);
852: if (butPtr->disabledGC != None) {
853: Tk_FreeGC(butPtr->disabledGC);
854: }
855: butPtr->disabledGC = newGC;
856:
857: if (butPtr->padX < 0) {
858: butPtr->padX = 0;
859: }
860: if (butPtr->padY < 0) {
861: butPtr->padY = 0;
862: }
863:
864: if (butPtr->type >= TYPE_CHECK_BUTTON) {
865: char *value;
866:
867: if (butPtr->selectorFg != NULL) {
868: gcValues.foreground = butPtr->selectorFg->pixel;
869: newGC = Tk_GetGC(butPtr->tkwin, GCForeground, &gcValues);
870: } else {
871: newGC = None;
872: }
873: if (butPtr->selectorGC != None) {
874: Tk_FreeGC(butPtr->selectorGC);
875: }
876: butPtr->selectorGC = newGC;
877:
878: if (butPtr->selVarName == NULL) {
879: butPtr->selVarName = (char *) ckalloc((unsigned)
880: (strlen(Tk_Name(butPtr->tkwin)) + 1));
881: strcpy(butPtr->selVarName, Tk_Name(butPtr->tkwin));
882: }
883: if (butPtr->onValue == NULL) {
884: butPtr->onValue = (char *) ckalloc((unsigned)
885: (strlen(Tk_Name(butPtr->tkwin)) + 1));
886: strcpy(butPtr->onValue, Tk_Name(butPtr->tkwin));
887: }
888:
889: /*
890: * Select the button if the associated variable has the
891: * appropriate value, initialize the variable if it doesn't
892: * exist, then set a trace on the variable to monitor future
893: * changes to its value.
894: */
895:
896: value = Tcl_GetVar(interp, butPtr->selVarName, TCL_GLOBAL_ONLY);
897: butPtr->flags &= ~SELECTED;
898: if (value != NULL) {
899: if (strcmp(value, butPtr->onValue) == 0) {
900: butPtr->flags |= SELECTED;
901: }
902: } else {
903: Tcl_SetVar(interp, butPtr->selVarName,
904: (butPtr->type == TYPE_CHECK_BUTTON) ? butPtr->offValue : "",
905: TCL_GLOBAL_ONLY);
906: }
907: Tcl_TraceVar(interp, butPtr->selVarName,
908: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
909: ButtonVarProc, (ClientData) butPtr);
910: }
911:
912: /*
913: * If the button is to display the value of a variable, then set up
914: * a trace on the variable's value, create the variable if it doesn't
915: * exist, and fetch its current value.
916: */
917:
918: if ((butPtr->bitmap == None) && (butPtr->textVarName != NULL)) {
919: char *value;
920:
921: value = Tcl_GetVar(interp, butPtr->textVarName, TCL_GLOBAL_ONLY);
922: if (value == NULL) {
923: Tcl_SetVar(interp, butPtr->textVarName, butPtr->text,
924: TCL_GLOBAL_ONLY);
925: } else {
926: if (butPtr->text != NULL) {
927: ckfree(butPtr->text);
928: }
929: butPtr->text = ckalloc((unsigned) (strlen(value) + 1));
930: strcpy(butPtr->text, value);
931: }
932: Tcl_TraceVar(interp, butPtr->textVarName,
933: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
934: ButtonTextVarProc, (ClientData) butPtr);
935: }
936:
937: ComputeButtonGeometry(butPtr);
938:
939: /*
940: * Lastly, arrange for the button to be redisplayed.
941: */
942:
943: if (Tk_IsMapped(butPtr->tkwin) && !(butPtr->flags & REDRAW_PENDING)) {
944: //Tk_TimerToken last = butPtr->updateTimerToken;
945: butPtr->flags |= REDRAW_PENDING;
946: // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
947: assert(butPtr->updateTimerToken == 0);
948: if (butPtr->updateTimerToken == 0) {
949: butPtr->updateTimerToken =
950: Tk_CreateTimerHandler(
951: ButtonUpdateTime,
952: DisplayButton,
953: (ClientData) butPtr);
954: }
955: //fprintf(stderr, "ConfigureButton Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
956: }
957:
958: return TCL_OK;
959: }
960:
961: /*
962: *----------------------------------------------------------------------
963: *
964: * DisplayButton --
965: *
966: * This procedure is invoked to display a button widget.
967: *
968: * Results:
969: * None.
970: *
971: * Side effects:
972: * Commands are output to X to display the button in its
973: * current mode.
974: *
975: *----------------------------------------------------------------------
976: */
977:
978: static void
979: DisplayButton(clientData)
980: ClientData clientData; /* Information about widget. */
981: {
982: register Button *butPtr = (Button *) clientData;
983: GC gc;
984: Tk_3DBorder border;
985: Pixmap pixmap;
986: int x = 0; /* Initialization only needed to stop
987: * compiler warning. */
988: int y;
989: register Tk_Window tkwin = butPtr->tkwin;
990:
991: //fprintf(stderr, "DisplayButton Handled Timer %s %s was %d now 0\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), butPtr->updateTimerToken);
992:
993: assert(butPtr->updateTimerToken != 0);
994: butPtr->updateTimerToken = 0;
995:
996: butPtr->flags &= ~REDRAW_PENDING;
997: if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
998: return;
999: }
1000:
1001: if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) {
1002: gc = butPtr->disabledGC;
1003: border = butPtr->normalBorder;
1004: } else if (butPtr->state == tkActiveUid) {
1005: gc = butPtr->activeTextGC;
1006: border = butPtr->activeBorder;
1007: } else {
1008: gc = butPtr->normalTextGC;
1009: border = butPtr->normalBorder;
1010: }
1011:
1012: /*
1013: * In order to avoid screen flashes, this procedure redraws
1014: * the button in a pixmap, then copies the pixmap to the
1015: * screen in a single operation. This means that there's no
1016: * point in time where the on-sreen image has been cleared.
1017: */
1018:
1019: pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
1020: Tk_Width(tkwin), Tk_Height(tkwin),
1021: Tk_DefaultDepth(Tk_Screen(tkwin)));
1022: Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border,
1023: 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
1024:
1025: /*
1026: * Display bitmap or text for button.
1027: */
1028:
1029: if (butPtr->bitmap != None) {
1030: unsigned int width, height;
1031:
1032: #if defined(USE_XPM3)
1033: Tk_SizeOfPixmap(butPtr->bitmap, &width, &height);
1034: #else
1035: Tk_SizeOfBitmap(butPtr->bitmap, &width, &height);
1036: #endif
1037: switch (butPtr->anchor) {
1038: case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
1039: x = butPtr->borderWidth + butPtr->selectorSpace
1040: + butPtr->padX + 1;
1041: break;
1042: case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
1043: x = (Tk_Width(tkwin) + butPtr->selectorSpace - width)/2;
1044: break;
1045: default:
1046: x = Tk_Width(tkwin) - butPtr->borderWidth - butPtr->padX
1047: - width - 1;
1048: break;
1049: }
1050: switch (butPtr->anchor) {
1051: case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
1052: y = butPtr->borderWidth + butPtr->padY + 1;
1053: break;
1054: case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
1055: y = (Tk_Height(tkwin) - height)/2;
1056: break;
1057: default:
1058: y = Tk_Height(tkwin) - butPtr->borderWidth - butPtr->padY
1059: - height - 1;
1060: break;
1061: }
1062: if (butPtr->relief == TK_RELIEF_RAISED) {
1063: x -= 1;
1064: y -= 1;
1065: } else if (butPtr->relief == TK_RELIEF_SUNKEN) {
1066: x += 1;
1067: y += 1;
1068: }
1069: #if defined(USE_XPM3)
1070: XCopyArea(Tk_Display(tkwin), butPtr->bitmap, pixmap,
1071: gc, 0, 0, width, height, x, y);
1072: #else
1073: XCopyPlane(Tk_Display(tkwin), butPtr->bitmap, pixmap,
1074: gc, 0, 0, width, height, x, y, 1);
1075: #endif
1076: y += height/2;
1077: } else {
1078: switch (butPtr->anchor) {
1079: case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
1080: x = butPtr->borderWidth + butPtr->padX + butPtr->selectorSpace
1081: + butPtr->leftBearing + 1;
1082: break;
1083: case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
1084: x = (Tk_Width(tkwin) + butPtr->selectorSpace
1085: + butPtr->leftBearing - butPtr->rightBearing)/2;
1086: break;
1087: default:
1088: x = Tk_Width(tkwin) - butPtr->borderWidth - butPtr->padX
1089: - butPtr->rightBearing - 1;
1090: break;
1091: }
1092: switch (butPtr->anchor) {
1093: case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
1094: y = butPtr->borderWidth + butPtr->fontPtr->ascent
1095: + butPtr->padY + 1;
1096: break;
1097: case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
1098: y = (Tk_Height(tkwin) + butPtr->fontPtr->ascent
1099: - butPtr->fontPtr->descent)/2;
1100: break;
1101: default:
1102: y = Tk_Height(tkwin) - butPtr->borderWidth - butPtr->padY
1103: - butPtr->fontPtr->descent - 1;
1104: break;
1105: }
1106: if (butPtr->relief == TK_RELIEF_RAISED) {
1107: x -= 1;
1108: y -= 1;
1109: } else if (butPtr->relief == TK_RELIEF_SUNKEN) {
1110: x += 1;
1111: y += 1;
1112: }
1113: XDrawString(Tk_Display(tkwin), pixmap, gc, x, y,
1114: butPtr->text, butPtr->textLength);
1115: y -= (butPtr->fontPtr->ascent - butPtr->fontPtr->descent)/2;
1116: x -= butPtr->leftBearing;
1117: }
1118:
1119: /*
1120: * Draw the selector for check buttons and radio buttons. At this
1121: * point x and y refer to the top-left corner of the text or bitmap.
1122: */
1123:
1124: if ((butPtr->type == TYPE_CHECK_BUTTON) && (butPtr->selectorGC != None)) {
1125: int dim;
1126:
1127: dim = butPtr->selectorDiameter;
1128: x -= (butPtr->selectorSpace + butPtr->padX + dim)/2;
1129: y -= dim/2;
1130: Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap, border, x, y,
1131: dim, dim, butPtr->borderWidth, TK_RELIEF_SUNKEN);
1132: x += butPtr->borderWidth;
1133: y += butPtr->borderWidth;
1134: dim -= 2*butPtr->borderWidth;
1135: if (dim > 0) {
1136: if (butPtr->flags & SELECTED) {
1137: XFillRectangle(Tk_Display(tkwin), pixmap, butPtr->selectorGC,
1138: x, y, (unsigned int) dim, (unsigned int) dim);
1139: } else {
1140: Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap,
1141: butPtr->normalBorder, x, y, dim, dim,
1142: butPtr->borderWidth, TK_RELIEF_FLAT);
1143: }
1144: }
1145: } else if ((butPtr->type == TYPE_RADIO_BUTTON)
1146: && (butPtr->selectorGC != None)) {
1147: XPoint points[4];
1148: int radius;
1149:
1150: radius = butPtr->selectorDiameter/2;
1151: points[0].x = x - (butPtr->selectorSpace + butPtr->padX
1152: + butPtr->selectorDiameter)/2;
1153: points[0].y = y;
1154: points[1].x = points[0].x + radius;
1155: points[1].y = points[0].y + radius;
1156: points[2].x = points[1].x + radius;
1157: points[2].y = points[0].y;
1158: points[3].x = points[1].x;
1159: points[3].y = points[0].y - radius;
1160: if (butPtr->flags & SELECTED) {
1161: XFillPolygon(Tk_Display(tkwin), pixmap, butPtr->selectorGC,
1162: points, 4, Convex, CoordModeOrigin);
1163: } else {
1164: Tk_Fill3DPolygon(Tk_Display(tkwin), pixmap, butPtr->normalBorder,
1165: points, 4, butPtr->borderWidth, TK_RELIEF_FLAT);
1166: }
1167: Tk_Draw3DPolygon(Tk_Display(tkwin), pixmap, border,
1168: points, 4, butPtr->borderWidth, TK_RELIEF_RAISED);
1169: }
1170:
1171: /*
1172: * If the button is disabled with a stipple rather than a special
1173: * foreground color, generate the stippled effect.
1174: */
1175:
1176: if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg == NULL)) {
1177: XFillRectangle(Tk_Display(tkwin), pixmap, butPtr->disabledGC,
1178: butPtr->borderWidth, butPtr->borderWidth,
1179: (unsigned) (Tk_Width(tkwin) - 2*butPtr->borderWidth),
1180: (unsigned) (Tk_Height(tkwin) - 2*butPtr->borderWidth));
1181: }
1182:
1183: /*
1184: * Draw the border last. This way, if the button's contents
1185: * overflow onto the border they'll be covered up by the border.
1186: */
1187:
1188: if (butPtr->relief != TK_RELIEF_FLAT) {
1189: Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap, border,0, 0,
1190: Tk_Width(tkwin), Tk_Height(tkwin), butPtr->borderWidth,
1191: butPtr->relief);
1192: }
1193:
1194: /*
1195: * Copy the information from the off-screen pixmap onto the screen,
1196: * then delete the pixmap.
1197: */
1198:
1199: XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
1200: butPtr->normalTextGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
1201: XFreePixmap(Tk_Display(tkwin), pixmap);
1202: }
1203:
1204: /*
1205: *--------------------------------------------------------------
1206: *
1207: * ButtonEventProc --
1208: *
1209: * This procedure is invoked by the Tk dispatcher for various
1210: * events on buttons.
1211: *
1212: * Results:
1213: * None.
1214: *
1215: * Side effects:
1216: * When the window gets deleted, internal structures get
1217: * cleaned up. When it gets exposed, it is redisplayed.
1218: *
1219: *--------------------------------------------------------------
1220: */
1221:
1222: static void
1223: ButtonEventProc(clientData, eventPtr)
1224: ClientData clientData; /* Information about window. */
1225: XEvent *eventPtr; /* Information about event. */
1226: {
1227: Button *butPtr = (Button *) clientData;
1228: if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
1229: if ((butPtr->tkwin != NULL) && !(butPtr->flags & REDRAW_PENDING)) {
1230: //Tk_TimerToken last = butPtr->updateTimerToken;
1231: butPtr->flags |= REDRAW_PENDING;
1232: // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
1233: assert(butPtr->updateTimerToken == NULL);
1234: if (butPtr->updateTimerToken == NULL) {
1235: butPtr->updateTimerToken =
1236: Tk_CreateTimerHandler(
1237: ButtonUpdateTime,
1238: DisplayButton,
1239: (ClientData) butPtr);
1240: } // if
1241: //fprintf(stderr, "ButtonEventProc Expose Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
1242: }
1243: } else if (eventPtr->type == DestroyNotify) {
1244: Tcl_DeleteCommand(butPtr->interp, Tk_PathName(butPtr->tkwin));
1245: butPtr->tkwin = NULL;
1246: if (butPtr->flags & REDRAW_PENDING) {
1247: //fprintf(stderr, "ButtonEventProc Destroy Timer was %d now 0\n", butPtr->updateTimerToken);
1248: // Tk_CancelIdleCall(DisplayButton, (ClientData) butPtr);
1249: butPtr->flags &= ~REDRAW_PENDING;
1250: assert(butPtr->updateTimerToken != NULL);
1251: if (butPtr->updateTimerToken != NULL) {
1252: Tk_DeleteTimerHandler(butPtr->updateTimerToken);
1253: butPtr->updateTimerToken = 0;
1254: }
1255: }
1256: Tk_EventuallyFree((ClientData) butPtr, DestroyButton);
1257: }
1258: }
1259:
1260: /*
1261: *----------------------------------------------------------------------
1262: *
1263: * ComputeButtonGeometry --
1264: *
1265: * After changes in a button's text or bitmap, this procedure
1266: * recomputes the button's geometry and passes this information
1267: * along to the geometry manager for the window.
1268: *
1269: * Results:
1270: * None.
1271: *
1272: * Side effects:
1273: * The button's window may change size.
1274: *
1275: *----------------------------------------------------------------------
1276: */
1277:
1278: static void
1279: ComputeButtonGeometry(butPtr)
1280: register Button *butPtr; /* Button whose geometry may have changed. */
1281: {
1282: XCharStruct bbox;
1283: int dummy;
1284: unsigned int width, height;
1285:
1286: butPtr->selectorSpace = 0;
1287: if (butPtr->bitmap != None) {
1288: #if defined(USE_XPM3)
1289: Tk_SizeOfPixmap(butPtr->bitmap, &width, &height);
1290: #else
1291: Tk_SizeOfBitmap(butPtr->bitmap, &width, &height);
1292: #endif
1293: if (butPtr->width > 0) {
1294: width = butPtr->width;
1295: }
1296: if (butPtr->height > 0) {
1297: height = butPtr->height;
1298: }
1299: if ((butPtr->type >= TYPE_CHECK_BUTTON)
1300: && (butPtr->selectorGC != None)) {
1301: butPtr->selectorSpace = (14*height)/10;
1302: if (butPtr->type == TYPE_CHECK_BUTTON) {
1303: butPtr->selectorDiameter = (65*height)/100;
1304: } else {
1305: butPtr->selectorDiameter = (75*height)/100;
1306: }
1307: }
1308: } else {
1309: butPtr->textLength = strlen(butPtr->text);
1310: XTextExtents(butPtr->fontPtr, butPtr->text, butPtr->textLength,
1311: &dummy, &dummy, &dummy, &bbox);
1312: butPtr->leftBearing = bbox.lbearing;
1313: butPtr->rightBearing = bbox.rbearing;
1314: width = bbox.lbearing + bbox.rbearing;
1315: height = butPtr->fontPtr->ascent + butPtr->fontPtr->descent;
1316: if (butPtr->width > 0) {
1317: width = butPtr->width * XTextWidth(butPtr->fontPtr, "0", 1);
1318: }
1319: if (butPtr->height > 0) {
1320: height *= butPtr->height;
1321: }
1322: if ((butPtr->type >= TYPE_CHECK_BUTTON)
1323: && (butPtr->selectorGC != None)) {
1324: butPtr->selectorDiameter = butPtr->fontPtr->ascent
1325: + butPtr->fontPtr->descent;
1326: if (butPtr->type == TYPE_CHECK_BUTTON) {
1327: butPtr->selectorDiameter = (80*butPtr->selectorDiameter)/100;
1328: }
1329: butPtr->selectorSpace = butPtr->selectorDiameter + butPtr->padX;
1330: }
1331: }
1332:
1333: /*
1334: * When issuing the geometry request, add extra space for the selector,
1335: * if any, and for the border and padding, plus two extra pixels so the
1336: * display can be offset by 1 pixel in either direction for the raised
1337: * or lowered effect.
1338: */
1339:
1340: width += 2*butPtr->padX;
1341: height += 2*butPtr->padY;
1342: Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->selectorSpace
1343: + 2*butPtr->borderWidth + 2),
1344: (int) (height + 2*butPtr->borderWidth + 2));
1345: Tk_SetInternalBorder(butPtr->tkwin, butPtr->borderWidth);
1346: }
1347:
1348: /*
1349: *----------------------------------------------------------------------
1350: *
1351: * InvokeButton --
1352: *
1353: * This procedure is called to carry out the actions associated
1354: * with a button, such as invoking a Tcl command or setting a
1355: * variable. This procedure is invoked, for example, when the
1356: * button is invoked via the mouse.
1357: *
1358: * Results:
1359: * A standard Tcl return value. Information is also left in
1360: * interp->result.
1361: *
1362: * Side effects:
1363: * Depends on the button and its associated command.
1364: *
1365: *----------------------------------------------------------------------
1366: */
1367:
1368: static int
1369: InvokeButton(butPtr)
1370: register Button *butPtr; /* Information about button. */
1371: {
1372: if (butPtr->type == TYPE_CHECK_BUTTON) {
1373: if (butPtr->flags & SELECTED) {
1374: Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->offValue,
1375: TCL_GLOBAL_ONLY);
1376: } else {
1377: Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
1378: TCL_GLOBAL_ONLY);
1379: }
1380: } else if (butPtr->type == TYPE_RADIO_BUTTON) {
1381: Tcl_SetVar(butPtr->interp, butPtr->selVarName, butPtr->onValue,
1382: TCL_GLOBAL_ONLY);
1383: }
1384: if ((butPtr->type != TYPE_LABEL) && (butPtr->command != NULL)) {
1385: return Tcl_GlobalEval(butPtr->interp, butPtr->command);
1386: }
1387: return TCL_OK;
1388: }
1389:
1390: /*
1391: *--------------------------------------------------------------
1392: *
1393: * ButtonVarProc --
1394: *
1395: * This procedure is invoked when someone changes the
1396: * state variable associated with a radio button. Depending
1397: * on the new value of the button's variable, the button
1398: * may be selected or deselected.
1399: *
1400: * Results:
1401: * NULL is always returned.
1402: *
1403: * Side effects:
1404: * The button may become selected or deselected.
1405: *
1406: *--------------------------------------------------------------
1407: */
1408:
1409: /* ARGSUSED */
1410: static char *
1411: ButtonVarProc(clientData, interp, name1, name2, flags)
1412: ClientData clientData; /* Information about button. */
1413: Tcl_Interp *interp; /* Interpreter containing variable. */
1414: char *name1; /* Name of variable. */
1415: char *name2; /* Second part of variable name. */
1416: int flags; /* Information about what happened. */
1417: {
1418: register Button *butPtr = (Button *) clientData;
1419: char *value;
1420:
1421: /*
1422: * If the variable is being unset, then just re-establish the
1423: * trace unless the whole interpreter is going away.
1424: */
1425:
1426: if (flags & TCL_TRACE_UNSETS) {
1427: butPtr->flags &= ~SELECTED;
1428: if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1429: Tcl_TraceVar2(interp, name1, name2,
1430: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1431: ButtonVarProc, clientData);
1432: }
1433: goto redisplay;
1434: }
1435:
1436: /*
1437: * Use the value of the variable to update the selected status of
1438: * the button.
1439: */
1440:
1441: value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
1442: if (strcmp(value, butPtr->onValue) == 0) {
1443: if (butPtr->flags & SELECTED) {
1444: return (char *) NULL;
1445: }
1446: butPtr->flags |= SELECTED;
1447: } else if (butPtr->flags & SELECTED) {
1448: butPtr->flags &= ~SELECTED;
1449: } else {
1450: return (char *) NULL;
1451: }
1452:
1453: redisplay:
1454: if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
1455: && !(butPtr->flags & REDRAW_PENDING)) {
1456: //Tk_TimerToken last = butPtr->updateTimerToken;
1457: butPtr->flags |= REDRAW_PENDING;
1458: // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
1459: assert(butPtr->updateTimerToken == NULL);
1460: if (butPtr->updateTimerToken == NULL) {
1461: butPtr->updateTimerToken =
1462: Tk_CreateTimerHandler(
1463: ButtonUpdateTime,
1464: DisplayButton,
1465: (ClientData) butPtr);
1466: }
1467: //fprintf(stderr, "ButtonVarProc Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
1468: }
1469: return (char *) NULL;
1470: }
1471:
1472: /*
1473: *--------------------------------------------------------------
1474: *
1475: * ButtonTextVarProc --
1476: *
1477: * This procedure is invoked when someone changes the variable
1478: * whose contents are to be displayed in a button.
1479: *
1480: * Results:
1481: * NULL is always returned.
1482: *
1483: * Side effects:
1484: * The text displayed in the button will change to match the
1485: * variable.
1486: *
1487: *--------------------------------------------------------------
1488: */
1489:
1490: /* ARGSUSED */
1491: static char *
1492: ButtonTextVarProc(clientData, interp, name1, name2, flags)
1493: ClientData clientData; /* Information about button. */
1494: Tcl_Interp *interp; /* Interpreter containing variable. */
1495: char *name1; /* Name of variable. */
1496: char *name2; /* Second part of variable name. */
1497: int flags; /* Information about what happened. */
1498: {
1499: register Button *butPtr = (Button *) clientData;
1500: char *value;
1501:
1502: /*
1503: * If the variable is unset, then immediately recreate it unless
1504: * the whole interpreter is going away.
1505: */
1506:
1507: if (flags & TCL_TRACE_UNSETS) {
1508: if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1509: Tcl_SetVar2(interp, name1, name2, butPtr->text,
1510: flags & TCL_GLOBAL_ONLY);
1511: Tcl_TraceVar2(interp, name1, name2,
1512: TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1513: ButtonTextVarProc, clientData);
1514: }
1515: return (char *) NULL;
1516: }
1517:
1518: value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
1519: if (value == NULL) {
1520: value = "";
1521: }
1522: if (butPtr->text != NULL) {
1523: ckfree(butPtr->text);
1524: }
1525: butPtr->text = ckalloc((unsigned) (strlen(value) + 1));
1526: strcpy(butPtr->text, value);
1527: ComputeButtonGeometry(butPtr);
1528:
1529: if ((butPtr->tkwin != NULL) && Tk_IsMapped(butPtr->tkwin)
1530: && !(butPtr->flags & REDRAW_PENDING)) {
1531: //Tk_TimerToken last = butPtr->updateTimerToken;
1532: butPtr->flags |= REDRAW_PENDING;
1533: // Tk_DoWhenIdle(DisplayButton, (ClientData) butPtr);
1534: assert(butPtr->updateTimerToken == NULL);
1535: if (butPtr->updateTimerToken == NULL) {
1536: butPtr->updateTimerToken =
1537: Tk_CreateTimerHandler(
1538: ButtonUpdateTime,
1539: DisplayButton,
1540: (ClientData) butPtr);
1541: }
1542: //fprintf(stderr, "ButtonTextVarProc Set Timer %s %s was %d now %d\n", Tk_Class(butPtr->tkwin), Tk_PathName(butPtr->tkwin), last, butPtr->updateTimerToken);
1543: }
1544: return (char *) NULL;
1545: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.