]> www.wagner.pp.ru Git - oss/ck.git/blob - ckMenubutton.c
Ck console graphics toolkit
[oss/ck.git] / ckMenubutton.c
1 /* 
2  * ckMenubutton.c --
3  *
4  *      This module implements button-like widgets that are used
5  *      to invoke pull-down menus.
6  *
7  * Copyright (c) 1990-1994 The Regents of the University of California.
8  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
9  * Copyright (c) 1995 Christian Werner
10  *
11  * See the file "license.terms" for information on usage and redistribution
12  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13  */
14
15 #include "ckPort.h"
16 #include "ck.h"
17 #include "default.h"
18
19 /*
20  * A data structure of the following type is kept for each
21  * widget managed by this file:
22  */
23
24 typedef struct {
25     CkWindow *winPtr;           /* Window that embodies the widget.  NULL
26                                  * means that the window has been destroyed
27                                  * but the data structures haven't yet been
28                                  * cleaned up.*/
29     Tcl_Interp *interp;         /* Interpreter associated with menubutton. */
30     Tcl_Command widgetCmd;      /* Token for menubutton's widget command. */
31     char *menuName;             /* Name of menu associated with widget.
32                                  * Malloc-ed. */
33
34     /*
35      * Information about what's displayed in the menu button:
36      */
37
38     char *text;                 /* Text to display in button (malloc'ed)
39                                  * or NULL. */
40     int numChars;               /* # of characters in text. */
41     char *textVarName;          /* Name of variable (malloc'ed) or NULL.
42                                  * If non-NULL, button displays the contents
43                                  * of this variable. */
44
45     /*
46      * Information used when displaying widget:
47      */
48
49     Ck_Uid state;               /* State of button for display purposes:
50                                  * normal, active, or disabled. */
51     int normalFg;               /* Foreground color in normal mode. */
52     int normalBg;               /* Background color in normal mode. */
53     int normalAttr;             /* Attributes in normal mode. */
54     int activeFg;               /* Foreground color in active mode. */
55     int activeBg;               /* Ditto, background color. */
56     int activeAttr;             /* Attributes in active mode. */
57     int disabledBg;             /* Background color when disabled. */
58     int disabledFg;             /* Foreground color when disabled. */
59     int disabledAttr;           /* Attributes when disabled. */
60     int underlineFg;            /* Foreground color for underlined char. */
61     int underlineAttr;          /* Attribute for underlined character. */
62     int indicatorFg;            /* Foreground color for indicator. */
63     int underline;              /* Index of underlined character, < 0 if
64                                  * no underlining. */
65     int width, height;          /* If > 0, these specify dimensions to request
66                                  * for window, in characters for text and in
67                                  * pixels for bitmaps.  In this case the actual
68                                  * size of the text string or bitmap is
69                                  * ignored in computing desired window size. */
70     Ck_Anchor anchor;           /* Where text/bitmap should be displayed
71                                  * inside window region. */
72     int indicatorOn;            /* Non-zero means display indicator;  0 means
73                                  * don't display. */
74
75     /*
76      * Miscellaneous information:
77      */
78
79     char *takeFocus;            /* Value of -takefocus option;  not used in
80                                  * the C code, but used by keyboard traversal
81                                  * scripts.  Malloc'ed, but may be NULL. */
82     int flags;                  /* Various flags;  see below for
83                                  * definitions. */
84 } MenuButton;
85
86 /*
87  * Flag bits for buttons:
88  *
89  * REDRAW_PENDING:              Non-zero means a DoWhenIdle handler
90  *                              has already been queued to redraw
91  *                              this window.
92  * POSTED:                      Non-zero means that the menu associated
93  *                              with this button has been posted (typically
94  *                              because of an active button press).
95  * GOT_FOCUS:                   Non-zero means this button currently
96  *                              has the input focus.
97  */
98
99 #define REDRAW_PENDING          1
100 #define POSTED                  2
101 #define GOT_FOCUS               4
102
103 /*
104  * Information used for parsing configuration specs:
105  */
106
107 static Ck_ConfigSpec configSpecs[] = {
108     {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
109         "ActiveAttributes", DEF_MENUBUTTON_ACTIVE_ATTR_COLOR,
110         Ck_Offset(MenuButton, activeAttr), CK_CONFIG_COLOR_ONLY},
111     {CK_CONFIG_ATTR, "-activeattributes", "activeAttributes",
112         "ActiveAttributes", DEF_MENUBUTTON_ACTIVE_ATTR_MONO,
113         Ck_Offset(MenuButton, activeAttr), CK_CONFIG_MONO_ONLY},
114     {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
115         DEF_MENUBUTTON_ATTR, Ck_Offset(MenuButton, normalAttr), 0},
116     {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
117         DEF_MENUBUTTON_ACTIVE_BG_COLOR, Ck_Offset(MenuButton, activeBg),
118         CK_CONFIG_COLOR_ONLY},
119     {CK_CONFIG_COLOR, "-activebackground", "activeBackground", "Foreground",
120         DEF_MENUBUTTON_ACTIVE_BG_MONO, Ck_Offset(MenuButton, activeBg),
121         CK_CONFIG_MONO_ONLY},
122     {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
123         DEF_MENUBUTTON_ACTIVE_FG_COLOR, Ck_Offset(MenuButton, activeFg),
124         CK_CONFIG_COLOR_ONLY},
125     {CK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
126         DEF_MENUBUTTON_ACTIVE_FG_MONO, Ck_Offset(MenuButton, activeFg),
127         CK_CONFIG_MONO_ONLY},
128     {CK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
129         DEF_MENUBUTTON_ANCHOR, Ck_Offset(MenuButton, anchor), 0},
130     {CK_CONFIG_COLOR, "-background", "background", "Background",
131         DEF_MENUBUTTON_BG_COLOR, Ck_Offset(MenuButton, normalBg),
132         CK_CONFIG_COLOR_ONLY},
133     {CK_CONFIG_COLOR, "-background", "background", "Background",
134         DEF_MENUBUTTON_BG_MONO, Ck_Offset(MenuButton, normalBg),
135         CK_CONFIG_MONO_ONLY},
136     {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
137         (char *) NULL, 0, 0},
138     {CK_CONFIG_ATTR, "-disabledattributes", "disabledAttributes",
139         "DisabledAttributes", DEF_MENUBUTTON_DISABLED_ATTR,
140         Ck_Offset(MenuButton, disabledAttr), 0},
141     {CK_CONFIG_COLOR, "-disabledbackground", "disabledBackground",
142         "DisabledBackground", DEF_MENUBUTTON_DISABLED_FG_COLOR,
143         Ck_Offset(MenuButton, disabledBg),
144         CK_CONFIG_COLOR_ONLY},
145     {CK_CONFIG_COLOR, "-disabledbackground", "disabledBackground",
146         "DisabledBackground", DEF_MENUBUTTON_DISABLED_BG_MONO,
147         Ck_Offset(MenuButton, disabledBg),
148         CK_CONFIG_MONO_ONLY},
149     {CK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
150         "DisabledForeground", DEF_MENUBUTTON_DISABLED_BG_COLOR,
151         Ck_Offset(MenuButton, disabledFg),
152         CK_CONFIG_COLOR_ONLY},
153     {CK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
154         "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_MONO,
155         Ck_Offset(MenuButton, disabledFg),
156         CK_CONFIG_MONO_ONLY},
157     {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
158         (char *) NULL, 0, 0},
159     {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
160         DEF_MENUBUTTON_FG, Ck_Offset(MenuButton, normalFg), 0},
161     {CK_CONFIG_COORD, "-height", "height", "Height",
162         DEF_MENUBUTTON_HEIGHT, Ck_Offset(MenuButton, height), 0},
163     {CK_CONFIG_COLOR, "-indicatorforeground", "indicatorForeground",
164         "Foreground", DEF_MENUBUTTON_INDICATOR_FG_COLOR,
165         Ck_Offset(MenuButton, indicatorFg), CK_CONFIG_COLOR_ONLY},
166     {CK_CONFIG_COLOR, "-indicatorforeground", "indicatorForeground",
167         "Foreground", DEF_MENUBUTTON_INDICATOR_FG_MONO,
168         Ck_Offset(MenuButton, indicatorFg), CK_CONFIG_MONO_ONLY},
169     {CK_CONFIG_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn",
170         DEF_MENUBUTTON_INDICATOR, Ck_Offset(MenuButton, indicatorOn), 0},
171     {CK_CONFIG_STRING, "-menu", "menu", "Menu",
172         DEF_MENUBUTTON_MENU, Ck_Offset(MenuButton, menuName),
173         CK_CONFIG_NULL_OK},
174     {CK_CONFIG_UID, "-state", "state", "State",
175         DEF_MENUBUTTON_STATE, Ck_Offset(MenuButton, state), 0},
176     {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
177         DEF_MENUBUTTON_TAKE_FOCUS, Ck_Offset(MenuButton, takeFocus),
178         CK_CONFIG_NULL_OK},
179     {CK_CONFIG_STRING, "-text", "text", "Text",
180         DEF_MENUBUTTON_TEXT, Ck_Offset(MenuButton, text), 0},
181     {CK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
182         DEF_MENUBUTTON_TEXT_VARIABLE, Ck_Offset(MenuButton, textVarName),
183         CK_CONFIG_NULL_OK},
184     {CK_CONFIG_INT, "-underline", "underline", "Underline",
185         DEF_MENUBUTTON_UNDERLINE, Ck_Offset(MenuButton, underline), 0},
186     {CK_CONFIG_ATTR, "-underlineattributes", "underlineAttributes",
187         "UnderlineAttributes", DEF_MENUBUTTON_UNDERLINE_ATTR,
188         Ck_Offset(MenuButton, underlineAttr), 0},
189     {CK_CONFIG_COLOR, "-underlineforeground", "underlineForeground",
190         "UnderlineForeground", DEF_MENUBUTTON_UNDERLINE_FG_COLOR,
191         Ck_Offset(MenuButton, underlineFg), CK_CONFIG_COLOR_ONLY},
192     {CK_CONFIG_COLOR, "-underlineforeground", "underlineForeground",
193         "UnderlineForeground", DEF_MENUBUTTON_UNDERLINE_FG_MONO,
194         Ck_Offset(MenuButton, underlineFg), CK_CONFIG_MONO_ONLY},
195     {CK_CONFIG_COORD, "-width", "width", "Width",
196         DEF_MENUBUTTON_WIDTH, Ck_Offset(MenuButton, width), 0},
197     {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
198         (char *) NULL, 0, 0}
199 };
200
201 /*
202  * Forward declarations for procedures defined later in this file:
203  */
204
205 static void             ComputeMenuButtonGeometry _ANSI_ARGS_((
206                             MenuButton *mbPtr));
207 static void             MenuButtonCmdDeletedProc _ANSI_ARGS_((
208                             ClientData clientData));
209 static void             MenuButtonEventProc _ANSI_ARGS_((ClientData clientData,
210                             CkEvent *eventPtr));
211 static char *           MenuButtonTextVarProc _ANSI_ARGS_((
212                             ClientData clientData, Tcl_Interp *interp,
213                             char *name1, char *name2, int flags));
214 static int              MenuButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
215                             Tcl_Interp *interp, int argc, char **argv));
216 static int              ConfigureMenuButton _ANSI_ARGS_((Tcl_Interp *interp,
217                             MenuButton *mbPtr, int argc, char **argv,
218                             int flags));
219 static void             DestroyMenuButton _ANSI_ARGS_((ClientData clientData));
220 static void             DisplayMenuButton _ANSI_ARGS_((ClientData clientData));
221 \f
222 /*
223  *--------------------------------------------------------------
224  *
225  * Ck_MenubuttonCmd --
226  *
227  *      This procedure is invoked to process the "menubutton"
228  *      Tcl commands.  See the user documentation for details
229  *      on what it does.
230  *
231  * Results:
232  *      A standard Tcl result.
233  *
234  * Side effects:
235  *      See the user documentation.
236  *
237  *--------------------------------------------------------------
238  */
239
240 int
241 Ck_MenubuttonCmd(clientData, interp, argc, argv)
242     ClientData clientData;      /* Main window associated with
243                                  * interpreter. */
244     Tcl_Interp *interp;         /* Current interpreter. */
245     int argc;                   /* Number of arguments. */
246     char **argv;                /* Argument strings. */
247 {
248     register MenuButton *mbPtr;
249     CkWindow *mainPtr = (CkWindow *) clientData;
250     CkWindow *new;
251
252     if (argc < 2) {
253         Tcl_AppendResult(interp, "wrong # args:  should be \"",
254                 argv[0], " pathName ?options?\"", (char *) NULL);
255         return TCL_ERROR;
256     }
257
258     /*
259      * Create the new window.
260      */
261
262     new = Ck_CreateWindowFromPath(interp, mainPtr, argv[1], 0);
263     if (new == NULL) {
264         return TCL_ERROR;
265     }
266
267     /*
268      * Initialize the data structure for the button.
269      */
270
271     mbPtr = (MenuButton *) ckalloc(sizeof(MenuButton));
272     mbPtr->winPtr = new;
273     mbPtr->interp = interp;
274     mbPtr->widgetCmd = Tcl_CreateCommand(interp, mbPtr->winPtr->pathName,
275             MenuButtonWidgetCmd, (ClientData) mbPtr, MenuButtonCmdDeletedProc);
276     mbPtr->menuName = NULL;
277     mbPtr->text = NULL;
278     mbPtr->numChars = 0;
279
280     mbPtr->textVarName = NULL;
281     mbPtr->state = ckNormalUid;
282     mbPtr->normalBg = 0;
283     mbPtr->normalFg = 0;
284     mbPtr->normalAttr = 0;
285     mbPtr->activeBg = 0;
286     mbPtr->activeFg = 0;
287     mbPtr->activeAttr = 0;
288     mbPtr->disabledBg = 0;
289     mbPtr->disabledFg = 0;
290     mbPtr->disabledAttr = 0;
291     mbPtr->underlineFg = 0;
292     mbPtr->underlineAttr = 0;
293     mbPtr->indicatorFg = 0;
294     mbPtr->underline = -1;
295     mbPtr->width = 0;
296     mbPtr->height = 0;
297     mbPtr->anchor = CK_ANCHOR_CENTER;
298     mbPtr->indicatorOn = 0;
299     mbPtr->takeFocus = NULL;
300     mbPtr->flags = 0;
301
302     Ck_SetClass(mbPtr->winPtr, "Menubutton");
303     Ck_CreateEventHandler(mbPtr->winPtr,
304             CK_EV_EXPOSE | CK_EV_MAP | CK_EV_DESTROY,
305             MenuButtonEventProc, (ClientData) mbPtr);
306     if (ConfigureMenuButton(interp, mbPtr, argc-2, argv+2, 0) != TCL_OK) {
307         Ck_DestroyWindow(mbPtr->winPtr);
308         return TCL_ERROR;
309     }
310
311     interp->result = mbPtr->winPtr->pathName;
312     return TCL_OK;
313 }
314 \f
315 /*
316  *--------------------------------------------------------------
317  *
318  * MenuButtonWidgetCmd --
319  *
320  *      This procedure is invoked to process the Tcl command
321  *      that corresponds to a widget managed by this module.
322  *      See the user documentation for details on what it does.
323  *
324  * Results:
325  *      A standard Tcl result.
326  *
327  * Side effects:
328  *      See the user documentation.
329  *
330  *--------------------------------------------------------------
331  */
332
333 static int
334 MenuButtonWidgetCmd(clientData, interp, argc, argv)
335     ClientData clientData;      /* Information about button widget. */
336     Tcl_Interp *interp;         /* Current interpreter. */
337     int argc;                   /* Number of arguments. */
338     char **argv;                /* Argument strings. */
339 {
340     register MenuButton *mbPtr = (MenuButton *) clientData;
341     int result = TCL_OK;
342     size_t length;
343     int c;
344
345     if (argc < 2) {
346         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
347                 " option ?arg arg ...?\"", (char *) NULL);
348         return TCL_ERROR;
349     }
350     Ck_Preserve((ClientData) mbPtr);
351     c = argv[1][0];
352     length = strlen(argv[1]);
353     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
354             && (length >= 2)) {
355         if (argc != 3) {
356             Tcl_AppendResult(interp, "wrong # args: should be \"",
357                     argv[0], " cget option\"",
358                     (char *) NULL);
359             goto error;
360         }
361         result = Ck_ConfigureValue(interp, mbPtr->winPtr, configSpecs,
362                 (char *) mbPtr, argv[2], 0);
363     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
364             && (length >= 2)) {
365         if (argc == 2) {
366             result = Ck_ConfigureInfo(interp, mbPtr->winPtr, configSpecs,
367                     (char *) mbPtr, (char *) NULL, 0);
368         } else if (argc == 3) {
369             result = Ck_ConfigureInfo(interp, mbPtr->winPtr, configSpecs,
370                     (char *) mbPtr, argv[2], 0);
371         } else {
372             result = ConfigureMenuButton(interp, mbPtr, argc-2, argv+2,
373                     CK_CONFIG_ARGV_ONLY);
374         }
375     } else {
376         Tcl_AppendResult(interp, "bad option \"", argv[1],
377                 "\":  must be cget or configure",
378                 (char *) NULL);
379         goto error;
380     }
381     Ck_Release((ClientData) mbPtr);
382     return result;
383
384     error:
385     Ck_Release((ClientData) mbPtr);
386     return TCL_ERROR;
387 }
388 \f
389 /*
390  *----------------------------------------------------------------------
391  *
392  * DestroyMenuButton --
393  *
394  *      This procedure is invoked to recycle all of the resources
395  *      associated with a button widget.  It is invoked as a
396  *      when-idle handler in order to make sure that there is no
397  *      other use of the button pending at the time of the deletion.
398  *
399  * Results:
400  *      None.
401  *
402  * Side effects:
403  *      Everything associated with the widget is freed up.
404  *
405  *----------------------------------------------------------------------
406  */
407
408 static void
409 DestroyMenuButton(clientData)
410     ClientData clientData;      /* Info about button widget. */
411 {
412     register MenuButton *mbPtr = (MenuButton *) clientData;
413
414     /*
415      * Free up all the stuff that requires special handling, then
416      * let Ck_FreeOptions handle all the standard option-related
417      * stuff.
418      */
419
420     if (mbPtr->textVarName != NULL) {
421         Tcl_UntraceVar(mbPtr->interp, mbPtr->textVarName,
422                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
423                 MenuButtonTextVarProc, (ClientData) mbPtr);
424     }
425     Ck_FreeOptions(configSpecs, (char *) mbPtr, 0);
426     ckfree((char *) mbPtr);
427 }
428 \f
429 /*
430  *----------------------------------------------------------------------
431  *
432  * ConfigureMenuButton --
433  *
434  *      This procedure is called to process an argv/argc list, plus
435  *      the Tk option database, in order to configure (or
436  *      reconfigure) a menubutton widget.
437  *
438  * Results:
439  *      The return value is a standard Tcl result.  If TCL_ERROR is
440  *      returned, then interp->result contains an error message.
441  *
442  * Side effects:
443  *      Configuration information, such as text string, colors, font,
444  *      etc. get set for mbPtr;  old resources get freed, if there
445  *      were any.  The menubutton is redisplayed.
446  *
447  *----------------------------------------------------------------------
448  */
449
450 static int
451 ConfigureMenuButton(interp, mbPtr, argc, argv, flags)
452     Tcl_Interp *interp;         /* Used for error reporting. */
453     register MenuButton *mbPtr; /* Information about widget;  may or may
454                                  * not already have values for some fields. */
455     int argc;                   /* Number of valid entries in argv. */
456     char **argv;                /* Arguments. */
457     int flags;                  /* Flags to pass to Tk_ConfigureWidget. */
458 {
459     int result;
460
461     /*
462      * Eliminate any existing trace on variables monitored by the menubutton.
463      */
464
465     if (mbPtr->textVarName != NULL) {
466         Tcl_UntraceVar(interp, mbPtr->textVarName,
467                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
468                 MenuButtonTextVarProc, (ClientData) mbPtr);
469     }
470
471     result = Ck_ConfigureWidget(interp, mbPtr->winPtr, configSpecs,
472             argc, argv, (char *) mbPtr, flags);
473     if (result != TCL_OK) {
474         return TCL_ERROR;
475     }
476
477     /*
478      * A few options need special processing, such as setting the
479      * background from a 3-D border, or filling in complicated
480      * defaults that couldn't be specified to Tk_ConfigureWidget.
481      */
482
483     if ((mbPtr->state != ckNormalUid) && (mbPtr->state != ckActiveUid)
484             && (mbPtr->state != ckDisabledUid)) {
485         Tcl_AppendResult(interp, "bad state value \"", mbPtr->state,
486             "\":  must be normal, active, or disabled", (char *) NULL);
487         mbPtr->state = ckNormalUid;
488         return TCL_ERROR;
489     }
490
491     if (mbPtr->textVarName != NULL) {
492         /*
493          * The menubutton displays a variable.  Set up a trace to watch
494          * for any changes in it.
495          */
496
497         char *value;
498
499         value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
500         if (value == NULL) {
501             Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
502                     TCL_GLOBAL_ONLY);
503         } else {
504             if (mbPtr->text != NULL) {
505                 ckfree(mbPtr->text);
506             }
507             mbPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
508             strcpy(mbPtr->text, value);
509         }
510         Tcl_TraceVar(interp, mbPtr->textVarName,
511                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
512                 MenuButtonTextVarProc, (ClientData) mbPtr);
513     }
514
515     ComputeMenuButtonGeometry(mbPtr);
516
517     /*
518      * Lastly, arrange for the button to be redisplayed.
519      */
520
521     if ((mbPtr->winPtr->flags & CK_MAPPED)
522         && !(mbPtr->flags & REDRAW_PENDING)) {
523         Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
524         mbPtr->flags |= REDRAW_PENDING;
525     }
526
527     return TCL_OK;
528 }
529 \f
530 /*
531  *----------------------------------------------------------------------
532  *
533  * DisplayMenuButton --
534  *
535  *      This procedure is invoked to display a menubutton widget.
536  *
537  * Results:
538  *      None.
539  *
540  * Side effects:
541  *      Commands are output to X to display the menubutton in its
542  *      current mode.
543  *
544  *----------------------------------------------------------------------
545  */
546
547 static void
548 DisplayMenuButton(clientData)
549     ClientData clientData;      /* Information about widget. */
550 {
551     MenuButton *mbPtr = (MenuButton *) clientData;
552     int x, y, fg, bg, attr, textWidth, charWidth;
553     CkWindow *winPtr = mbPtr->winPtr;
554
555     mbPtr->flags &= ~REDRAW_PENDING;
556     if ((mbPtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED)) {
557         return;
558     }
559
560     if (mbPtr->state == ckDisabledUid) {
561         fg = mbPtr->disabledFg;
562         bg = mbPtr->disabledBg;
563         attr = mbPtr->disabledAttr;
564     } else if (mbPtr->state == ckActiveUid) {
565         fg = mbPtr->activeFg;
566         bg = mbPtr->activeBg;
567         attr = mbPtr->activeAttr;
568     } else {
569         fg = mbPtr->normalFg;
570         bg = mbPtr->normalBg;
571         attr = mbPtr->normalAttr;
572     }
573
574     /*
575      * Display text for button.
576      */
577
578     if (mbPtr->text != NULL)
579         CkMeasureChars(winPtr->mainPtr, mbPtr->text, mbPtr->numChars, 0,
580             winPtr->width, 0, CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS,
581             &textWidth, &charWidth);
582     else
583         textWidth = 0;
584
585     switch (mbPtr->anchor) {
586         case CK_ANCHOR_NW: case CK_ANCHOR_W: case CK_ANCHOR_SW:
587             x = 0;
588             break;
589         case CK_ANCHOR_N: case CK_ANCHOR_CENTER: case CK_ANCHOR_S:
590             x = (winPtr->width - textWidth) / 2;
591             if (mbPtr->indicatorOn)
592                 x--;
593             break;
594         default:
595             x = winPtr->width - textWidth;
596             if (mbPtr->indicatorOn)
597                 x -= 2;
598             break;
599     }
600     if (x + textWidth > winPtr->width)
601         textWidth = winPtr->width - x;
602
603     switch (mbPtr->anchor) {
604         case CK_ANCHOR_NW: case CK_ANCHOR_N: case CK_ANCHOR_NE:
605             y = 0;
606             break;
607         case CK_ANCHOR_W: case CK_ANCHOR_CENTER: case CK_ANCHOR_E:
608             y = (winPtr->height - 1) / 2;
609             break;
610         default:
611             y = winPtr->height - 1;
612             if (y < 0)
613                 y = 0;
614             break;
615     }
616
617     Ck_SetWindowAttr(winPtr, fg, bg, attr);
618     Ck_ClearToBot(winPtr, 0, 0);
619     if (mbPtr->text != NULL) {
620         CkDisplayChars(winPtr->mainPtr, winPtr->window, mbPtr->text,
621             charWidth, x, y,
622             0, CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS);
623         if (mbPtr->underline >= 0 && mbPtr->state == ckNormalUid) {
624             Ck_SetWindowAttr(winPtr, mbPtr->underlineFg, bg,
625                 mbPtr->underlineAttr);
626             CkUnderlineChars(winPtr->mainPtr, winPtr->window,
627                 mbPtr->text, charWidth, x, y,
628                 0, CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS,
629                 mbPtr->underline, mbPtr->underline);
630             Ck_SetWindowAttr(winPtr, fg, bg, attr);
631         }
632     }
633     if (mbPtr->indicatorOn) {
634         int gchar;
635
636         x = textWidth + 2;
637         if (x >= winPtr->width)
638             x = winPtr->width - 1;
639         Ck_GetGChar(mbPtr->interp, "diamond", &gchar);
640         Ck_SetWindowAttr(winPtr, mbPtr->indicatorFg, bg, attr);
641         mvwaddch(winPtr->window, y, x, gchar);
642     }
643     Ck_SetWindowAttr(winPtr, fg, bg, attr);
644     wmove(winPtr->window, y, x);
645     Ck_EventuallyRefresh(winPtr);
646 }
647 \f
648 /*
649  *--------------------------------------------------------------
650  *
651  * MenuButtonEventProc --
652  *
653  *      This procedure is invoked by the Tk dispatcher for various
654  *      events on buttons.
655  *
656  * Results:
657  *      None.
658  *
659  * Side effects:
660  *      When the window gets deleted, internal structures get
661  *      cleaned up.  When it gets exposed, it is redisplayed.
662  *
663  *--------------------------------------------------------------
664  */
665
666 static void
667 MenuButtonEventProc(clientData, eventPtr)
668     ClientData clientData;      /* Information about window. */
669     CkEvent *eventPtr;          /* Information about event. */
670 {
671     MenuButton *mbPtr = (MenuButton *) clientData;
672
673     if (eventPtr->type == CK_EV_EXPOSE) {
674         if (mbPtr->winPtr != NULL && !(mbPtr->flags & REDRAW_PENDING)) {
675             Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
676             mbPtr->flags |= REDRAW_PENDING;
677         }
678     } else if (eventPtr->type == CK_EV_DESTROY) {
679         if (mbPtr->winPtr != NULL) {
680             mbPtr->winPtr = NULL;
681             Tcl_DeleteCommand(mbPtr->interp,
682                     Tcl_GetCommandName(mbPtr->interp, mbPtr->widgetCmd));
683         }
684         if (mbPtr->flags & REDRAW_PENDING) {
685             Tk_CancelIdleCall(DisplayMenuButton, (ClientData) mbPtr);
686         }
687         Ck_EventuallyFree((ClientData) mbPtr,
688             (Ck_FreeProc *) DestroyMenuButton);
689     }
690 }
691 \f
692 /*
693  *----------------------------------------------------------------------
694  *
695  * MenuButtonCmdDeletedProc --
696  *
697  *      This procedure is invoked when a widget command is deleted.  If
698  *      the widget isn't already in the process of being destroyed,
699  *      this command destroys it.
700  *
701  * Results:
702  *      None.
703  *
704  * Side effects:
705  *      The widget is destroyed.
706  *
707  *----------------------------------------------------------------------
708  */
709
710 static void
711 MenuButtonCmdDeletedProc(clientData)
712     ClientData clientData;      /* Pointer to widget record for widget. */
713 {
714     MenuButton *mbPtr = (MenuButton *) clientData;
715     CkWindow *winPtr = mbPtr->winPtr;
716
717     /*
718      * This procedure could be invoked either because the window was
719      * destroyed and the command was then deleted (in which case winPtr
720      * is NULL) or because the command was deleted, and then this procedure
721      * destroys the widget.
722      */
723
724     if (winPtr != NULL) {
725         mbPtr->winPtr = NULL;
726         Ck_DestroyWindow(winPtr);
727     }
728 }
729 \f
730 /*
731  *----------------------------------------------------------------------
732  *
733  * ComputeMenuButtonGeometry --
734  *
735  *      After changes in a menu button's text or bitmap, this procedure
736  *      recomputes the menu button's geometry and passes this information
737  *      along to the geometry manager for the window.
738  *
739  * Results:
740  *      None.
741  *
742  * Side effects:
743  *      The menu button's window may change size.
744  *
745  *----------------------------------------------------------------------
746  */
747
748 static void
749 ComputeMenuButtonGeometry(mbPtr)
750     register MenuButton *mbPtr;         /* Widget record for menu button. */
751 {
752     int width, height, dummy;
753     CkWindow *winPtr = mbPtr->winPtr;
754
755     mbPtr->numChars = mbPtr->text == NULL ? 0 : strlen(mbPtr->text);
756     if (mbPtr->height > 0)
757         height = mbPtr->height;
758     else
759         height = 1;
760     if (mbPtr->width > 0)
761         width = mbPtr->width;
762     else
763         CkMeasureChars(winPtr->mainPtr, mbPtr->text == NULL ? "" : mbPtr->text,
764             mbPtr->numChars, 0, 100000, 0,
765             CK_NEWLINES_NOT_SPECIAL | CK_IGNORE_TABS,
766             &width, &dummy);
767
768     /*
769      * When issuing the geometry request, add extra space for the indicator
770      * if any.
771      */
772
773     if (mbPtr->indicatorOn)
774         width += 2;
775
776     Ck_GeometryRequest(mbPtr->winPtr, width, height);
777 }
778 \f
779 /*
780  *--------------------------------------------------------------
781  *
782  * MenuButtonTextVarProc --
783  *
784  *      This procedure is invoked when someone changes the variable
785  *      whose contents are to be displayed in a menu button.
786  *
787  * Results:
788  *      NULL is always returned.
789  *
790  * Side effects:
791  *      The text displayed in the menu button will change to match the
792  *      variable.
793  *
794  *--------------------------------------------------------------
795  */
796
797 static char *
798 MenuButtonTextVarProc(clientData, interp, name1, name2, flags)
799     ClientData clientData;      /* Information about button. */
800     Tcl_Interp *interp;         /* Interpreter containing variable. */
801     char *name1;                /* Name of variable. */
802     char *name2;                /* Second part of variable name. */
803     int flags;                  /* Information about what happened. */
804 {
805     register MenuButton *mbPtr = (MenuButton *) clientData;
806     char *value;
807
808     /*
809      * If the variable is unset, then immediately recreate it unless
810      * the whole interpreter is going away.
811      */
812
813     if (flags & TCL_TRACE_UNSETS) {
814         if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
815             Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
816                     TCL_GLOBAL_ONLY);
817             Tcl_TraceVar(interp, mbPtr->textVarName,
818                     TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
819                     MenuButtonTextVarProc, clientData);
820         }
821         return (char *) NULL;
822     }
823
824     value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
825     if (value == NULL) {
826         value = "";
827     }
828     if (mbPtr->text != NULL) {
829         ckfree(mbPtr->text);
830     }
831     mbPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1));
832     strcpy(mbPtr->text, value);
833     ComputeMenuButtonGeometry(mbPtr);
834
835     if ((mbPtr->winPtr != NULL) && (mbPtr->winPtr->flags & CK_MAPPED)
836             && !(mbPtr->flags & REDRAW_PENDING)) {
837         Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
838         mbPtr->flags |= REDRAW_PENDING;
839     }
840     return (char *) NULL;
841 }